@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
61 lines • 2.57 kB
JavaScript
import { AgentBackendUnsupportedError } from '../backend.js';
/**
* `GET /agent/skills` as an autocomplete source — the concrete one behind `/`, and an example of
* the shape any other takes. `position: 'start'` because a skill is a command: `/deploy` is one and
* `src/foo` is a path.
*/
export function createSkillsSource(options) {
// One read per thread. The endpoint answers with the whole scope-resolved list rather than a
// search, so asking again per keystroke would fetch identical rows only to filter them locally.
// A failed read is forgotten, so the next keystroke retries instead of caching the outage.
//
// The order it arrives in IS the precedence — most specific scope first, then alphabetical — and
// is passed through untouched. The type-ahead filter narrows this list by what the user typed; it
// never re-derives which skills apply, which is the server's answer and the model's too.
const byThread = new Map();
return {
id: 'skills',
label: options.label ?? 'Skills',
trigger: options.trigger ?? '/',
position: 'start',
getItems: () => {
const threadId = options.getThreadId?.();
const key = threadId ?? '';
const cached = byThread.get(key);
if (cached) {
return cached;
}
const listSkills = options.backend.listSkills;
if (listSkills === undefined) {
return Promise.reject(new AgentBackendUnsupportedError('listSkills'));
}
const pending = listSkills
.call(options.backend, threadId)
.then((entries) => entries.map(toItem))
.catch((error) => {
byThread.delete(key);
throw error;
});
byThread.set(key, pending);
return pending;
},
};
}
function toItem(entry) {
const data = {
scope: entry.scope,
...(entry.shadows !== undefined ? { shadows: entry.shadows } : {}),
};
return {
id: entry.name,
label: entry.name,
description: entry.description,
// `shadows` is present only on a conflict, so saying "overrides" is never noise: without it a
// user cannot tell "there is no org default" from "there is one and mine wins".
hint: entry.shadows === undefined
? entry.scope
: `${entry.scope} · overrides ${entry.shadows.join(', ')}`,
data,
};
}
//# sourceMappingURL=skills-source.js.map