@falkenslab/agent-kit 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/chat/chatController.d.ts +10 -0
- package/dist/chat/chatController.js +39 -1
- package/dist/chat/extensions.d.ts +9 -0
- package/dist/chat/extensions.js +13 -1
- package/dist/chat/runs.d.ts +3 -1
- package/dist/chat/runs.js +1 -1
- package/dist/core/extensionCatalog.d.ts +6 -1
- package/dist/core/extensionCatalog.js +10 -8
- package/dist/core/extensionOptions.d.ts +5 -0
- package/dist/core/extensionOptions.js +10 -0
- package/dist/core/extensions.d.ts +6 -0
- package/dist/core/externalExtensions.d.ts +24 -1
- package/dist/core/externalExtensions.js +78 -12
- package/dist/core/liveExtensions.d.ts +38 -0
- package/dist/core/liveExtensions.js +69 -0
- package/dist/core/marketplaces.d.ts +1 -0
- package/dist/core/messages/de.js +10 -1
- package/dist/core/messages/en.d.ts +14 -0
- package/dist/core/messages/en.js +10 -1
- package/dist/core/messages/es.js +10 -1
- package/dist/core/messages/fr.js +10 -1
- package/dist/core/npxPackages.d.ts +2 -2
- package/dist/core/runner.js +17 -0
- package/dist/core/session.d.ts +6 -2
- package/dist/core/session.js +5 -2
- package/dist/core/sessionFacts.d.ts +14 -0
- package/dist/extensions/awareness/catalog.d.ts +8 -0
- package/dist/extensions/awareness/catalog.js +92 -0
- package/dist/extensions/awareness/index.d.ts +3 -1
- package/dist/extensions/awareness/index.js +15 -6
- package/dist/extensions/awareness/labels.js +8 -5
- package/dist/extensions/awareness/tools.d.ts +2 -2
- package/dist/extensions/awareness/tools.js +4 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/tui/extensionCommand.js +11 -6
- package/extensions/awareness/skills/extensions/SKILL.md +56 -0
- package/package.json +1 -1
- package/schemas/marketplace-catalog.schema.json +6 -0
|
@@ -97,6 +97,10 @@ export function createExtensionRestart(extensions, targets) {
|
|
|
97
97
|
const reconnect = targets.controls().reconnectMcpServer;
|
|
98
98
|
if (!extension || !reconnect || !extension.servers.length)
|
|
99
99
|
return false;
|
|
100
|
+
// A remote server's URL and headers are fixed when the session opens: restarting it would keep
|
|
101
|
+
// the old values (#61), so its options take opening the session again.
|
|
102
|
+
if (extension.remoteServers.length)
|
|
103
|
+
return false;
|
|
100
104
|
try {
|
|
101
105
|
for (const server of extension.servers)
|
|
102
106
|
await reconnect(server);
|
|
@@ -108,6 +112,71 @@ export function createExtensionRestart(extensions, targets) {
|
|
|
108
112
|
}
|
|
109
113
|
};
|
|
110
114
|
}
|
|
115
|
+
/** Signing in and out for a session's installed extensions. */
|
|
116
|
+
export function createExtensionAuth(extensions, targets) {
|
|
117
|
+
const remote = (name) => extensions.find((candidate) => candidate.name === name)?.remoteServers ?? [];
|
|
118
|
+
const statuses = async () => {
|
|
119
|
+
const status = targets.controls().mcpServerStatus;
|
|
120
|
+
return new Map(status ? (await status().catch(() => [])).map((server) => [server.name, server.status]) : []);
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
async needed() {
|
|
124
|
+
const now = await statuses();
|
|
125
|
+
return extensions.filter((extension) => extension.remoteServers.some((server) => now.get(server) === "needs-auth")).map((extension) => extension.name);
|
|
126
|
+
},
|
|
127
|
+
async signIn(name, callbackUrl) {
|
|
128
|
+
const extension = extensions.find((candidate) => candidate.name === name);
|
|
129
|
+
if (!extension)
|
|
130
|
+
return { status: "unavailable", reason: "not-running" };
|
|
131
|
+
if (!extension.remoteServers.length)
|
|
132
|
+
return { status: "unavailable", reason: "not-remote" };
|
|
133
|
+
const controls = targets.controls();
|
|
134
|
+
const now = await statuses();
|
|
135
|
+
const pending = extension.remoteServers.filter((server) => now.get(server) === "needs-auth");
|
|
136
|
+
if (!pending.length)
|
|
137
|
+
return { status: "signed-in" };
|
|
138
|
+
if (callbackUrl) {
|
|
139
|
+
if (!controls.mcpSubmitOAuthCallbackUrl || !controls.reconnectMcpServer)
|
|
140
|
+
return { status: "unavailable", reason: "unsupported" };
|
|
141
|
+
// A pasted callback stores the token, but the session stays needs-auth until the server
|
|
142
|
+
// reconnects (confirmed empirically, CLI 2.1.300).
|
|
143
|
+
for (const server of pending) {
|
|
144
|
+
await controls.mcpSubmitOAuthCallbackUrl(server, callbackUrl);
|
|
145
|
+
await controls.reconnectMcpServer(server);
|
|
146
|
+
}
|
|
147
|
+
return (await this.connected(name, 5000)) ? { status: "signed-in" } : { status: "open", links: [] };
|
|
148
|
+
}
|
|
149
|
+
if (!controls.mcpAuthenticate)
|
|
150
|
+
return { status: "unavailable", reason: "unsupported" };
|
|
151
|
+
const links = [];
|
|
152
|
+
for (const server of pending) {
|
|
153
|
+
const started = await controls.mcpAuthenticate(server);
|
|
154
|
+
if (started.authUrl)
|
|
155
|
+
links.push({ server, url: started.authUrl });
|
|
156
|
+
}
|
|
157
|
+
return { status: "open", links };
|
|
158
|
+
},
|
|
159
|
+
async connected(name, timeoutMs = 0) {
|
|
160
|
+
const servers = remote(name);
|
|
161
|
+
for (const start = Date.now();; await new Promise((resolve) => setTimeout(resolve, 500))) {
|
|
162
|
+
const now = await statuses();
|
|
163
|
+
if (servers.length && servers.every((server) => now.get(server) === "connected"))
|
|
164
|
+
return true;
|
|
165
|
+
if (Date.now() - start >= timeoutMs)
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
async signOut(name) {
|
|
170
|
+
const servers = remote(name);
|
|
171
|
+
const clear = targets.controls().mcpClearAuth;
|
|
172
|
+
if (!servers.length || !clear)
|
|
173
|
+
return false;
|
|
174
|
+
for (const server of servers)
|
|
175
|
+
await clear(server);
|
|
176
|
+
return true;
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
111
180
|
/** The kit's lists, as the extension is now. */
|
|
112
181
|
function follow(extension, enabled, targets) {
|
|
113
182
|
const without = (list, items) => {
|
package/dist/core/messages/de.js
CHANGED
|
@@ -35,7 +35,7 @@ export const de = {
|
|
|
35
35
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}, in derselben Sitzung)`,
|
|
36
36
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}: die Sitzung wird mit derselben Unterhaltung neu geöffnet)`,
|
|
37
37
|
extensionNotInstalled: (name) => `(${name} ist nicht installiert: /extensions listet sie auf)`,
|
|
38
|
-
extensionsUsage: "(Verwendung: /extensions, enable|disable <Name>, config <Name> [Schlüssel=Wert…], search [Wörter], install <Name>, update [Name], remove <Name> [--delete-data], reload, trust)",
|
|
38
|
+
extensionsUsage: "(Verwendung: /extensions, enable|disable <Name>, config <Name> [Schlüssel=Wert…], search [Wörter], install <Name>, update [Name], remove <Name> [--delete-data], reload, trust, sign-in <Name> [Rückruf-URL], sign-out <Name>)",
|
|
39
39
|
extensionsCantReopen: "(dieser Chat kann seine Sitzung nicht neu öffnen: im Terminal aktivieren oder deaktivieren und neu starten)",
|
|
40
40
|
extensionOptions: (name) => `Optionen von ${name}:`,
|
|
41
41
|
extensionNoOptions: (name) => `(${name} hat keine Optionen)`,
|
|
@@ -51,6 +51,15 @@ export const de = {
|
|
|
51
51
|
extensionIsInstalled: "(installiert)",
|
|
52
52
|
extensionUpdateAvailable: (installed) => `(installiert: ${installed}, Update verfügbar)`,
|
|
53
53
|
extensionLacks: (name, what) => `(${name}: ${what})`,
|
|
54
|
+
extensionTalksTo: (name, hosts) => `(${name} spricht mit ${hosts}: was dein Agent dorthin sendet, verlässt diesen Computer)`,
|
|
55
|
+
extensionNeedsSignIn: (name) => `${name} braucht deine Anmeldung: /extensions sign-in ${name}`,
|
|
56
|
+
extensionSignInOpen: (name, url) => `(zum Anmelden bei ${name} öffne ${url} : sobald du dort fertig bist, ist sie hier verbunden)`,
|
|
57
|
+
extensionSignInPaste: (name) => `(auf einem anderen Computer? Füge die Adresse ein, bei der der Browser landet: /extensions sign-in ${name} <diese Adresse>)`,
|
|
58
|
+
extensionSignedIn: (name) => `(bei ${name} angemeldet: ihre Werkzeuge sind bereit)`,
|
|
59
|
+
extensionSignInWaiting: (name) => `(warte noch auf die Anmeldung bei ${name}; /extensions sign-in ${name} gibt den Link erneut)`,
|
|
60
|
+
extensionAlreadySignedIn: (name) => `(${name} ist bereits angemeldet)`,
|
|
61
|
+
extensionSignInUnavailable: (name) => `(bei ${name} kann man sich hier nicht anmelden: sie läuft nicht in dieser Sitzung, hat keinen entfernten Server, oder das SDK dieses Agenten kann es nicht)`,
|
|
62
|
+
extensionSignedOut: (name) => `(bei ${name} abgemeldet: sie bittet dich, dich erneut anzumelden)`,
|
|
54
63
|
extensionInstalled: (name) => `(${name} installiert: in einem neuen Gespräch da, oder sofort mit /extensions reload, ohne dieses zu verlieren)`,
|
|
55
64
|
extensionUpdated: (name, from, to) => `(${name} aktualisiert, ${from} → ${to}: in einem neuen Gespräch da, oder sofort mit /extensions reload)`,
|
|
56
65
|
extensionsUpToDate: "(alle Erweiterungen aus einem Marketplace sind aktuell)",
|
|
@@ -68,6 +68,20 @@ export interface Messages {
|
|
|
68
68
|
extensionInstalled(name: string): string;
|
|
69
69
|
/** After installing: a program it needs that this computer lacks (#63); `what` is the reason, in English. */
|
|
70
70
|
extensionLacks(name: string, what: string): string;
|
|
71
|
+
/** After installing one with remote servers (#61): the hosts it talks to. */
|
|
72
|
+
extensionTalksTo(name: string, hosts: string): string;
|
|
73
|
+
/** In /extensions: a running extension whose remote server needs the person to sign in. */
|
|
74
|
+
extensionNeedsSignIn(name: string): string;
|
|
75
|
+
/** The link to sign in to an extension's remote server. */
|
|
76
|
+
extensionSignInOpen(name: string, url: string): string;
|
|
77
|
+
/** How to finish signing in from another computer. */
|
|
78
|
+
extensionSignInPaste(name: string): string;
|
|
79
|
+
extensionSignedIn(name: string): string;
|
|
80
|
+
/** Signing in hasn't finished after a while. */
|
|
81
|
+
extensionSignInWaiting(name: string): string;
|
|
82
|
+
extensionAlreadySignedIn(name: string): string;
|
|
83
|
+
extensionSignInUnavailable(name: string): string;
|
|
84
|
+
extensionSignedOut(name: string): string;
|
|
71
85
|
extensionUpdated(name: string, from: string, to: string): string;
|
|
72
86
|
extensionsUpToDate: string;
|
|
73
87
|
extensionRemoved(name: string, withData: boolean): string;
|
package/dist/core/messages/en.js
CHANGED
|
@@ -34,7 +34,7 @@ export const en = {
|
|
|
34
34
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}, in this same session)`,
|
|
35
35
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}: the session reopens, with the same conversation)`,
|
|
36
36
|
extensionNotInstalled: (name) => `(${name} isn't installed: /extensions lists them)`,
|
|
37
|
-
extensionsUsage: "(usage: /extensions, enable|disable <name>, config <name> [key=value…], search [words], install <name>, update [name], remove <name> [--delete-data], reload, trust)",
|
|
37
|
+
extensionsUsage: "(usage: /extensions, enable|disable <name>, config <name> [key=value…], search [words], install <name>, update [name], remove <name> [--delete-data], reload, trust, sign-in <name> [callback URL], sign-out <name>)",
|
|
38
38
|
extensionsCantReopen: "(this chat can't reopen its session: enable or disable it from the terminal, and start again)",
|
|
39
39
|
extensionOptions: (name) => `${name}'s options:`,
|
|
40
40
|
extensionNoOptions: (name) => `(${name} has no options)`,
|
|
@@ -51,6 +51,15 @@ export const en = {
|
|
|
51
51
|
extensionUpdateAvailable: (installed) => `(installed: ${installed}, update available)`,
|
|
52
52
|
extensionInstalled: (name) => `(${name} installed: it's there in a new conversation, or now with /extensions reload, keeping this one)`,
|
|
53
53
|
extensionLacks: (name, what) => `(${name} ${what})`,
|
|
54
|
+
extensionTalksTo: (name, hosts) => `(${name} talks to ${hosts}: what your agent sends there leaves this computer)`,
|
|
55
|
+
extensionNeedsSignIn: (name) => `${name} needs you to sign in: /extensions sign-in ${name}`,
|
|
56
|
+
extensionSignInOpen: (name, url) => `(to sign in to ${name}, open ${url} : when you're done there, it's connected here)`,
|
|
57
|
+
extensionSignInPaste: (name) => `(on another computer? Paste the address the browser ends on: /extensions sign-in ${name} <that address>)`,
|
|
58
|
+
extensionSignedIn: (name) => `(signed in to ${name}: its tools are ready)`,
|
|
59
|
+
extensionSignInWaiting: (name) => `(still waiting for the sign-in to ${name}; /extensions sign-in ${name} gives the link again)`,
|
|
60
|
+
extensionAlreadySignedIn: (name) => `(${name} is already signed in)`,
|
|
61
|
+
extensionSignInUnavailable: (name) => `(${name} can't be signed in to here: it isn't running in this session, has no remote server, or this agent's SDK can't sign in)`,
|
|
62
|
+
extensionSignedOut: (name) => `(signed out of ${name}: it will ask you to sign in again)`,
|
|
54
63
|
extensionUpdated: (name, from, to) => `(${name} updated, ${from} → ${to}: it's there in a new conversation, or now with /extensions reload)`,
|
|
55
64
|
extensionsUpToDate: "(every extension from a marketplace is up to date)",
|
|
56
65
|
extensionRemoved: (name, withData) => `(${name} removed${withData ? ", with its data" : "; its data is kept for a reinstall"})`,
|
package/dist/core/messages/es.js
CHANGED
|
@@ -35,7 +35,7 @@ export const es = {
|
|
|
35
35
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "activada" : "desactivada"}, en esta misma sesión)`,
|
|
36
36
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "activada" : "desactivada"}: la sesión se reabre, con la misma conversación)`,
|
|
37
37
|
extensionNotInstalled: (name) => `(${name} no está instalada: /extensions las lista)`,
|
|
38
|
-
extensionsUsage: "(uso: /extensions, enable|disable <nombre>, config <nombre> [clave=valor…], search [palabras], install <nombre>, update [nombre], remove <nombre> [--delete-data], reload, trust)",
|
|
38
|
+
extensionsUsage: "(uso: /extensions, enable|disable <nombre>, config <nombre> [clave=valor…], search [palabras], install <nombre>, update [nombre], remove <nombre> [--delete-data], reload, trust, sign-in <nombre> [URL de vuelta], sign-out <nombre>)",
|
|
39
39
|
extensionsCantReopen: "(este chat no puede reabrir su sesión: actívala o desactívala desde la terminal y vuelve a empezar)",
|
|
40
40
|
extensionOptions: (name) => `Opciones de ${name}:`,
|
|
41
41
|
extensionNoOptions: (name) => `(${name} no tiene opciones)`,
|
|
@@ -51,6 +51,15 @@ export const es = {
|
|
|
51
51
|
extensionIsInstalled: "(instalada)",
|
|
52
52
|
extensionUpdateAvailable: (installed) => `(instalada: ${installed}, hay actualización)`,
|
|
53
53
|
extensionLacks: (name, what) => `(${name}: ${what})`,
|
|
54
|
+
extensionTalksTo: (name, hosts) => `(${name} habla con ${hosts}: lo que tu agente le envíe sale de este ordenador)`,
|
|
55
|
+
extensionNeedsSignIn: (name) => `${name} necesita que inicies sesión: /extensions sign-in ${name}`,
|
|
56
|
+
extensionSignInOpen: (name, url) => `(para iniciar sesión en ${name}, abre ${url} : cuando termines allí, queda conectada aquí)`,
|
|
57
|
+
extensionSignInPaste: (name) => `(¿en otro ordenador? Pega la dirección en la que acaba el navegador: /extensions sign-in ${name} <esa dirección>)`,
|
|
58
|
+
extensionSignedIn: (name) => `(sesión iniciada en ${name}: sus herramientas están listas)`,
|
|
59
|
+
extensionSignInWaiting: (name) => `(sigo esperando el inicio de sesión en ${name}; /extensions sign-in ${name} vuelve a dar el enlace)`,
|
|
60
|
+
extensionAlreadySignedIn: (name) => `(${name} ya tiene la sesión iniciada)`,
|
|
61
|
+
extensionSignInUnavailable: (name) => `(no se puede iniciar sesión en ${name} aquí: no se ejecuta en esta sesión, no tiene servidor remoto, o el SDK de este agente no sabe hacerlo)`,
|
|
62
|
+
extensionSignedOut: (name) => `(sesión cerrada en ${name}: te pedirá que vuelvas a iniciarla)`,
|
|
54
63
|
extensionInstalled: (name) => `(${name} instalada: está en una conversación nueva, o ya con /extensions reload, sin perder esta)`,
|
|
55
64
|
extensionUpdated: (name, from, to) => `(${name} actualizada, ${from} → ${to}: está en una conversación nueva, o ya con /extensions reload)`,
|
|
56
65
|
extensionsUpToDate: "(todas las extensiones de un marketplace están al día)",
|
package/dist/core/messages/fr.js
CHANGED
|
@@ -35,7 +35,7 @@ export const fr = {
|
|
|
35
35
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "activée" : "désactivée"}, dans cette même session)`,
|
|
36
36
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "activée" : "désactivée"} : la session se rouvre, avec la même conversation)`,
|
|
37
37
|
extensionNotInstalled: (name) => `(${name} n'est pas installée : /extensions les liste)`,
|
|
38
|
-
extensionsUsage: "(usage : /extensions, enable|disable <nom>, config <nom> [clé=valeur…], search [mots], install <nom>, update [nom], remove <nom> [--delete-data], reload, trust)",
|
|
38
|
+
extensionsUsage: "(usage : /extensions, enable|disable <nom>, config <nom> [clé=valeur…], search [mots], install <nom>, update [nom], remove <nom> [--delete-data], reload, trust, sign-in <nom> [URL de retour], sign-out <nom>)",
|
|
39
39
|
extensionsCantReopen: "(ce chat ne peut pas rouvrir sa session : activez-la ou désactivez-la depuis le terminal, puis recommencez)",
|
|
40
40
|
extensionOptions: (name) => `Options de ${name} :`,
|
|
41
41
|
extensionNoOptions: (name) => `(${name} n'a pas d'options)`,
|
|
@@ -51,6 +51,15 @@ export const fr = {
|
|
|
51
51
|
extensionIsInstalled: "(installée)",
|
|
52
52
|
extensionUpdateAvailable: (installed) => `(installée : ${installed}, mise à jour disponible)`,
|
|
53
53
|
extensionLacks: (name, what) => `(${name} : ${what})`,
|
|
54
|
+
extensionTalksTo: (name, hosts) => `(${name} échange avec ${hosts} : ce que votre agent y envoie quitte cet ordinateur)`,
|
|
55
|
+
extensionNeedsSignIn: (name) => `${name} a besoin que vous vous connectiez : /extensions sign-in ${name}`,
|
|
56
|
+
extensionSignInOpen: (name, url) => `(pour vous connecter à ${name}, ouvrez ${url} : une fois terminé là-bas, elle est connectée ici)`,
|
|
57
|
+
extensionSignInPaste: (name) => `(sur un autre ordinateur ? Collez l'adresse où le navigateur arrive : /extensions sign-in ${name} <cette adresse>)`,
|
|
58
|
+
extensionSignedIn: (name) => `(connecté à ${name} : ses outils sont prêts)`,
|
|
59
|
+
extensionSignInWaiting: (name) => `(toujours en attente de la connexion à ${name} ; /extensions sign-in ${name} redonne le lien)`,
|
|
60
|
+
extensionAlreadySignedIn: (name) => `(${name} est déjà connectée)`,
|
|
61
|
+
extensionSignInUnavailable: (name) => `(impossible de se connecter à ${name} ici : elle ne tourne pas dans cette session, n'a pas de serveur distant, ou le SDK de cet agent ne sait pas le faire)`,
|
|
62
|
+
extensionSignedOut: (name) => `(déconnecté de ${name} : elle vous demandera de vous reconnecter)`,
|
|
54
63
|
extensionInstalled: (name) => `(${name} installée : elle est là dans une nouvelle conversation, ou dès maintenant avec /extensions reload, en gardant celle-ci)`,
|
|
55
64
|
extensionUpdated: (name, from, to) => `(${name} mise à jour, ${from} → ${to} : elle est là dans une nouvelle conversation, ou dès maintenant avec /extensions reload)`,
|
|
56
65
|
extensionsUpToDate: "(toutes les extensions d'un marketplace sont à jour)",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionDirs,
|
|
1
|
+
import type { ExtensionDirs, LocalServer } from "./externalExtensions.js";
|
|
2
2
|
/**
|
|
3
3
|
* Installed extensions' servers run with `npx` (#54): a `.mcp.json` that says
|
|
4
4
|
* `npx -y <package>@<x.y.z>` runs as it is in Claude Code, and in the kit too, which doesn't run
|
|
@@ -32,7 +32,7 @@ export declare const packageSpec: (server: NpxServer) => string;
|
|
|
32
32
|
* does but not as the kit can, or its package. Its command is `npx` (`npx.cmd`), its options
|
|
33
33
|
* only `-y`/`--yes`, then `<package>@<x.y.z>` and the server's own arguments.
|
|
34
34
|
*/
|
|
35
|
-
export declare function npxServer(server:
|
|
35
|
+
export declare function npxServer(server: LocalServer): NpxServer | string | undefined;
|
|
36
36
|
/** Where an agent keeps the packages its extensions' npx servers run: in its scope (the project's when it has none), shared by both. */
|
|
37
37
|
export declare function packageCacheDir(dirs: ExtensionDirs): string | undefined;
|
|
38
38
|
/** A package's folder in the cache. */
|
package/dist/core/runner.js
CHANGED
|
@@ -19,6 +19,22 @@ export function toolResultText(content) {
|
|
|
19
19
|
.map((part) => part.text)
|
|
20
20
|
.join("\n");
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* The SDK's sign-in methods for a remote MCP server behind OAuth, when its `Query` has them: they
|
|
24
|
+
* aren't in its typings (confirmed empirically, SDK 0.3.300; ADR-028), so each is checked first.
|
|
25
|
+
*/
|
|
26
|
+
function signInControls(result) {
|
|
27
|
+
const methods = result;
|
|
28
|
+
const call = (name) => (typeof methods[name] === "function" ? (...args) => methods[name].apply(result, args) : undefined);
|
|
29
|
+
const authenticate = call("mcpAuthenticate");
|
|
30
|
+
const submit = call("mcpSubmitOAuthCallbackUrl");
|
|
31
|
+
const clear = call("mcpClearAuth");
|
|
32
|
+
return {
|
|
33
|
+
...(authenticate ? { mcpAuthenticate: (server) => authenticate(server) } : {}),
|
|
34
|
+
...(submit ? { mcpSubmitOAuthCallbackUrl: (server, callbackUrl) => submit(server, callbackUrl) } : {}),
|
|
35
|
+
...(clear ? { mcpClearAuth: (server) => clear(server) } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
22
38
|
/**
|
|
23
39
|
* Thin wrapper around the SDK's own `query()`: same two-argument shape (a plain string
|
|
24
40
|
* for a one-shot run, or an `AsyncIterable<SDKUserMessage>` — see `createInputQueue()` —
|
|
@@ -38,6 +54,7 @@ export function runQuery(prompt, options) {
|
|
|
38
54
|
reloadPlugins: () => result.reloadPlugins(),
|
|
39
55
|
reconnectMcpServer: (server) => result.reconnectMcpServer(server),
|
|
40
56
|
mcpServerStatus: () => result.mcpServerStatus(),
|
|
57
|
+
...signInControls(result),
|
|
41
58
|
});
|
|
42
59
|
// The main agent's tool calls by id, to name the results that come back for them.
|
|
43
60
|
const toolNames = new Map();
|
package/dist/core/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Options, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
1
|
+
import type { McpServerStatus, Options, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { type TranscriptLogger } from "./hooks/transcriptLogger.js";
|
|
3
3
|
import { type ModeControl } from "./modeControl.js";
|
|
4
4
|
import { type AboutExtension } from "./extensions.js";
|
|
@@ -7,7 +7,7 @@ import type { ToolLabels } from "./toolLabels.js";
|
|
|
7
7
|
import type { AgentSpec, BaseSessionConfig } from "./agentSpec.js";
|
|
8
8
|
import { type Language } from "./language.js";
|
|
9
9
|
import { type RunFolder } from "./runs.js";
|
|
10
|
-
import { type ExtensionRestart, type ExtensionSwitch } from "./liveExtensions.js";
|
|
10
|
+
import { type ExtensionAuth, type ExtensionRestart, type ExtensionSwitch } from "./liveExtensions.js";
|
|
11
11
|
/**
|
|
12
12
|
* Builds the `options` object passed to the Agent SDK's `query()` — everything about
|
|
13
13
|
* *how* the agent runs a session (system prompt, tools, MCP servers, hooks), independent
|
|
@@ -47,6 +47,8 @@ export declare function buildSessionOptions<TConfig extends BaseSessionConfig>(c
|
|
|
47
47
|
switchExtension: ExtensionSwitch;
|
|
48
48
|
/** Restarts an installed extension's servers in the running session, with its options as they are now (#55). */
|
|
49
49
|
restartExtension: ExtensionRestart;
|
|
50
|
+
/** Signing in to installed extensions' remote servers, and out (#61). */
|
|
51
|
+
extensionAuth: ExtensionAuth;
|
|
50
52
|
}>;
|
|
51
53
|
export { createModeControl, togglePlanMode, type ModeControl } from "./modeControl.js";
|
|
52
54
|
/** The extensions of a session: where they're installed, what's installed, what runs and what can't (with why). */
|
|
@@ -73,6 +75,8 @@ export interface ExtensionsStatus {
|
|
|
73
75
|
}[];
|
|
74
76
|
/** The running session's tools, as the model names them (empty until it has started): what a capability's tools are checked against. */
|
|
75
77
|
sessionTools?: () => readonly string[];
|
|
78
|
+
/** Its MCP servers as they are now (empty until it has started): which remote ones need signing in (#61). */
|
|
79
|
+
serverStatus?: () => Promise<McpServerStatus[]>;
|
|
76
80
|
}
|
|
77
81
|
/**
|
|
78
82
|
* The CLI's folder for a project, where it keeps each session's transcript and large tool
|
package/dist/core/session.js
CHANGED
|
@@ -22,7 +22,7 @@ import { replyLanguageInstruction } from "./language.js";
|
|
|
22
22
|
import { chooseLanguage } from "./messages/index.js";
|
|
23
23
|
import { createRunStore } from "./runs.js";
|
|
24
24
|
import { createSessionView, registerSessionView } from "./sessionFacts.js";
|
|
25
|
-
import { createExtensionRestart, createExtensionSwitch, mountPlugin } from "./liveExtensions.js";
|
|
25
|
+
import { createExtensionAuth, createExtensionRestart, createExtensionSwitch, mountPlugin } from "./liveExtensions.js";
|
|
26
26
|
import { secretsDir } from "./extensionOptions.js";
|
|
27
27
|
import { createElicitationHandler } from "./elicitation.js";
|
|
28
28
|
import { createReadOnlyHints } from "./readOnlyHints.js";
|
|
@@ -358,6 +358,7 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
358
358
|
active: resolved.active.map(({ extension }) => extension.name),
|
|
359
359
|
about: Object.fromEntries(resolved.active.map(({ extension, manifest, unavailable }, index) => [extension.name, aboutExtension(manifest, Object.keys(added[index]?.mcpServers ?? {}), language, unavailable)])),
|
|
360
360
|
sessionTools: () => late.view?.controls().tools ?? [],
|
|
361
|
+
serverStatus: async () => (await late.view?.controls().mcpServerStatus?.().catch(() => [])) ?? [],
|
|
361
362
|
inactive: resolved.inactive,
|
|
362
363
|
};
|
|
363
364
|
// What the session is, for the extensions that ask (sessionFacts.ts, #43).
|
|
@@ -405,6 +406,7 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
405
406
|
provides: manifest.provides,
|
|
406
407
|
capabilities: manifest.capabilities,
|
|
407
408
|
unavailable,
|
|
409
|
+
remoteServers: contribution?.remoteServers ?? [],
|
|
408
410
|
help: contribution?.helpLines ?? [],
|
|
409
411
|
},
|
|
410
412
|
];
|
|
@@ -412,8 +414,9 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
412
414
|
const liveTargets = { readOnlyTools, allowedSubagentTypes, toolLabels, status: extensions, facts: viewBase, controls: () => late.view?.controls() ?? {} };
|
|
413
415
|
const switchExtension = createExtensionSwitch(liveExtensions, liveTargets);
|
|
414
416
|
const restartExtension = createExtensionRestart(liveExtensions, liveTargets);
|
|
417
|
+
const extensionAuth = createExtensionAuth(liveExtensions, liveTargets);
|
|
415
418
|
const apis = Object.fromEntries(resolved.active.flatMap(({ extension }, index) => (added[index]?.api ? [[extension.name, added[index].api]] : [])));
|
|
416
|
-
return { options: sdkOptions, transcriptLogger, transcriptPath, modeControl, language, toolLabels, extensions, apis, switchExtension, restartExtension };
|
|
419
|
+
return { options: sdkOptions, transcriptLogger, transcriptPath, modeControl, language, toolLabels, extensions, apis, switchExtension, restartExtension, extensionAuth };
|
|
417
420
|
}
|
|
418
421
|
export { createModeControl, togglePlanMode } from "./modeControl.js";
|
|
419
422
|
/** The Claude Code configuration folder the SDK's CLI uses: `CLAUDE_CONFIG_DIR`, or `~/.claude`. */
|
|
@@ -71,6 +71,20 @@ export interface SessionLive {
|
|
|
71
71
|
reconnectMcpServer?: (server: string) => Promise<void>;
|
|
72
72
|
/** Its MCP servers as they are now: their tools and what each says of itself (plan mode's read-only hints). */
|
|
73
73
|
mcpServerStatus?: () => Promise<McpServerStatus[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Signing in to a remote server behind OAuth (#61): the SDK's `Query` has these, undocumented
|
|
76
|
+
* (in `sdk.mjs`, not in `sdk.d.ts`; confirmed empirically, SDK 0.3.300). Attached only when they
|
|
77
|
+
* exist, so a session whose SDK lacks them just can't sign in.
|
|
78
|
+
*/
|
|
79
|
+
mcpAuthenticate?: (server: string) => Promise<McpAuthentication>;
|
|
80
|
+
mcpSubmitOAuthCallbackUrl?: (server: string, callbackUrl: string) => Promise<unknown>;
|
|
81
|
+
mcpClearAuth?: (server: string) => Promise<unknown>;
|
|
82
|
+
}
|
|
83
|
+
/** What starting a remote server's sign-in gives (the SDK's `mcpAuthenticate()`): the link for the person, and whether the CLI waits for its localhost callback. */
|
|
84
|
+
export interface McpAuthentication {
|
|
85
|
+
authUrl?: string;
|
|
86
|
+
requiresUserAction?: boolean;
|
|
87
|
+
callbackExpected?: boolean;
|
|
74
88
|
}
|
|
75
89
|
/** The facts known when the options are built: everything but the mode and what only the running session knows. */
|
|
76
90
|
export type StaticSessionFacts = Omit<SessionFacts, "mode" | "switchableModes" | "tools" | "commands" | "context" | "extensions"> & {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { BaseSessionConfig } from "../../core/agentSpec.js";
|
|
3
|
+
import type { SessionFacts } from "../../core/sessionFacts.js";
|
|
4
|
+
/** The awareness's catalog tool, over the agent's extension folders and its own marketplace and topics. */
|
|
5
|
+
export declare function createCatalogTool(config: BaseSessionConfig, session: () => Promise<SessionFacts>): import("@anthropic-ai/claude-agent-sdk").SdkMcpToolDefinition<{
|
|
6
|
+
words: z.ZodOptional<z.ZodString>;
|
|
7
|
+
refresh: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { tool } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { describeCapability } from "../../core/extensions.js";
|
|
4
|
+
import { extensionCatalog, knownMarketplaces, marketplacesDir } from "../../core/extensionCatalog.js";
|
|
5
|
+
import { extensionOptionStates, listInstalled } from "../../core/externalExtensions.js";
|
|
6
|
+
import { updateMarketplace } from "../../core/marketplaces.js";
|
|
7
|
+
/**
|
|
8
|
+
* The agent as an advisor on extensions (#64): `extensions_catalog` tells it what's installed and
|
|
9
|
+
* how it stands, what its catalogs offer (from their copies on this computer, instant and offline),
|
|
10
|
+
* and, with `refresh`, what's new in them, so it can suggest an extension the person lacks. It
|
|
11
|
+
* never installs, enables or configures anything: refreshing renews the catalogs' copies only, so
|
|
12
|
+
* the tool stays read-only for plan mode.
|
|
13
|
+
*/
|
|
14
|
+
const ok = (text) => ({ content: [{ type: "text", text }] });
|
|
15
|
+
/** How long ago an ISO date was, as the model reads it. */
|
|
16
|
+
function age(iso, now) {
|
|
17
|
+
const days = Math.floor((now - Date.parse(iso)) / 86_400_000);
|
|
18
|
+
return Number.isNaN(days) ? "at an unknown time" : days <= 0 ? "today" : days === 1 ? "yesterday" : `${days} days ago`;
|
|
19
|
+
}
|
|
20
|
+
/** A catalog entry in one line: what installs it, what it is and does, what it needs, where it talks to, how it stands here. */
|
|
21
|
+
function describeEntry(entry) {
|
|
22
|
+
const parts = [`- **${entry.spec}**${entry.version ? ` ${entry.version}` : ""}: ${entry.description || "(no description)"}`];
|
|
23
|
+
if (entry.capabilities.length)
|
|
24
|
+
parts.push(`Can: ${entry.capabilities.map(({ id, label }) => `${label} (${id})`).join(", ")}.`);
|
|
25
|
+
if (entry.requirements.length)
|
|
26
|
+
parts.push(`Needs on the computer: ${entry.requirements.map(({ name, for: needed }) => (needed.length ? `${name} (for ${needed.join(", ")})` : name)).join(", ")}.`);
|
|
27
|
+
if (entry.remote.length)
|
|
28
|
+
parts.push(`Talks to ${entry.remote.join(", ")}.`);
|
|
29
|
+
if (entry.topics.length)
|
|
30
|
+
parts.push(`Topics: ${entry.topics.join(", ")}.`);
|
|
31
|
+
parts.push(entry.installed ? (entry.update ? `Installed, with an update here: \`/extensions update ${entry.plugin.name}\`.` : "Installed.") : `Not installed: \`/extensions install ${entry.spec}\`.`);
|
|
32
|
+
return parts.join(" ");
|
|
33
|
+
}
|
|
34
|
+
/** The awareness's catalog tool, over the agent's extension folders and its own marketplace and topics. */
|
|
35
|
+
export function createCatalogTool(config, session) {
|
|
36
|
+
const dirs = config.extensionDirs;
|
|
37
|
+
const official = config.officialMarketplace;
|
|
38
|
+
const topics = config.catalogTopics;
|
|
39
|
+
// At most one fetch per marketplace and session: a second "what's new" answers from that copy.
|
|
40
|
+
const refreshed = new Set();
|
|
41
|
+
return tool("extensions_catalog", "Your extensions and your catalogs: what's installed here and how it stands (running, or off and why, options missing), what the catalogs you know offer (from their copies on this computer, with each copy's age) and, with `refresh: true`, what's new in them online. With `words`, only what matches (name, description, capabilities, topics). Read-only: it never installs, enables or configures anything; installing is the person's, with `/extensions install <name>@<marketplace>`.", {
|
|
42
|
+
words: z.string().optional().describe("A few words of the task or extension to look for, e.g. `grading rubrics` or `weather`. Without them, everything."),
|
|
43
|
+
refresh: z.boolean().optional().describe("Fetch the catalogs again from their source first, and say what's new: only when the person asks what's new, or the copies are over a week old."),
|
|
44
|
+
}, async ({ words, refresh }) => {
|
|
45
|
+
if (!dirs || !marketplacesDir(dirs))
|
|
46
|
+
return ok("This agent has no folder for extensions: it can't install any. Say so if the person asks for one.");
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const sections = [];
|
|
49
|
+
// What's installed, and how it stands in this session.
|
|
50
|
+
const facts = await session().catch(() => undefined);
|
|
51
|
+
const running = new Set(facts?.extensions.active.map(({ name }) => name) ?? []);
|
|
52
|
+
const off = new Map(facts?.extensions.inactive.map(({ name, reason }) => [name, reason]) ?? []);
|
|
53
|
+
const installed = (await listInstalled(dirs)).filter((extension) => !extension.shadowed);
|
|
54
|
+
const installedLines = await Promise.all(installed.map(async (extension) => {
|
|
55
|
+
const state = !extension.lock.enabled ? "disabled (`/extensions enable " + extension.name + "`)" : running.has(extension.name) ? "running" : off.has(extension.name) ? `off: it ${off.get(extension.name)}` : "enabled, from the next session";
|
|
56
|
+
const missing = (await extensionOptionStates(extension).catch(() => [])).filter(({ option, set }) => option.required && !set).map(({ key }) => key);
|
|
57
|
+
const can = (extension.manifest?.capabilities ?? []).map((capability) => describeCapability(capability, "en").label);
|
|
58
|
+
return `- **${extension.name}**${extension.manifest?.version ? ` ${extension.manifest.version}` : ""} (${extension.scope} scope): ${state}.${can.length ? ` Can: ${can.join(", ")}.` : ""}${missing.length ? ` Options to set: ${missing.join(", ")} (\`/extensions config ${extension.name}\`; a secret through the agent's settings or \`extension config\`, never in the chat).` : ""}`;
|
|
59
|
+
}));
|
|
60
|
+
sections.push(`## Installed here\n\n${installedLines.join("\n") || "None."}`);
|
|
61
|
+
// Fetching the catalogs again, when asked: what's new, compared with the copies before.
|
|
62
|
+
let before = [];
|
|
63
|
+
const failed = [];
|
|
64
|
+
if (refresh) {
|
|
65
|
+
before = await extensionCatalog(dirs, { ...(official ? { official } : {}), ...(topics?.length ? { topics } : {}), language: "en" }).catch(() => []);
|
|
66
|
+
for (const marketplace of await knownMarketplaces(dirs, official).catch(() => [])) {
|
|
67
|
+
if (refreshed.has(marketplace.name))
|
|
68
|
+
continue;
|
|
69
|
+
refreshed.add(marketplace.name);
|
|
70
|
+
await updateMarketplace(marketplacesDir(dirs), marketplace.name).catch(() => failed.push(marketplace.name));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const known = await knownMarketplaces(dirs, official).catch(() => []);
|
|
74
|
+
const entries = await extensionCatalog(dirs, {
|
|
75
|
+
...(official ? { official } : {}),
|
|
76
|
+
...(topics?.length ? { topics } : {}),
|
|
77
|
+
...(words?.trim() ? { words: words.trim().split(/\s+/) } : {}),
|
|
78
|
+
language: "en",
|
|
79
|
+
}).catch(() => []);
|
|
80
|
+
const copies = known.map((marketplace) => `${marketplace.name}${marketplace.official ? " (yours)" : ""}, copied ${age(marketplace.updated, now)}`).join("; ");
|
|
81
|
+
sections.push(`## Offered by your catalogs\n\n${known.length ? `From their copies on this computer: ${copies}.` : "No catalog is known: the person adds one with `/extensions` (a marketplace)."}${words?.trim() ? ` Matching "${words.trim()}".` : ""}\n\n${entries.map(describeEntry).join("\n") || "Nothing matches."}`);
|
|
82
|
+
if (refresh) {
|
|
83
|
+
const was = new Map(before.map((entry) => [entry.spec, entry.version]));
|
|
84
|
+
const added = entries.filter((entry) => !was.has(entry.spec));
|
|
85
|
+
const updates = entries.filter((entry) => entry.update);
|
|
86
|
+
const lines = [...added.map((entry) => `- New: **${entry.spec}**${entry.version ? ` ${entry.version}` : ""}: ${entry.description}`), ...updates.map((entry) => `- A newer version of **${entry.plugin.name}**: ${entry.version}.`)];
|
|
87
|
+
const unreachable = failed.length ? `\n\nCouldn't fetch ${failed.join(", ")} (no network, or it's unavailable): what's above is its copy on this computer.` : "";
|
|
88
|
+
sections.push(`## What's new online\n\n${lines.join("\n") || "Nothing new since the last copies."}${unreachable}`);
|
|
89
|
+
}
|
|
90
|
+
return ok(sections.join("\n\n"));
|
|
91
|
+
}, { annotations: { readOnlyHint: true } });
|
|
92
|
+
}
|
|
@@ -3,7 +3,9 @@ import type { Extension } from "../../core/extensions.js";
|
|
|
3
3
|
/** Absolute path of the awareness extension's plugin, next to `dist/` (or `src/` under tsx). */
|
|
4
4
|
export declare function awarenessPluginRoot(): string;
|
|
5
5
|
/** The "Who you are" section of the system prompt: name, version, what it is, the kit's version, and where to look. */
|
|
6
|
-
export declare function identityPromptSection(identity: AgentIdentity
|
|
6
|
+
export declare function identityPromptSection(identity: AgentIdentity, options?: {
|
|
7
|
+
catalog?: boolean;
|
|
8
|
+
}): string;
|
|
7
9
|
/** The awareness extension's options. */
|
|
8
10
|
export interface AwarenessOptions {
|
|
9
11
|
/**
|
|
@@ -3,12 +3,13 @@ import { fileURLToPath } from "node:url";
|
|
|
3
3
|
import { agentKitVersion } from "../../core/version.js";
|
|
4
4
|
import { awarenessToolLabels } from "./labels.js";
|
|
5
5
|
import { createAwarenessServer } from "./tools.js";
|
|
6
|
+
import { createCatalogTool } from "./catalog.js";
|
|
6
7
|
/** Absolute path of the awareness extension's plugin, next to `dist/` (or `src/` under tsx). */
|
|
7
8
|
export function awarenessPluginRoot() {
|
|
8
9
|
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "extensions", "awareness");
|
|
9
10
|
}
|
|
10
11
|
/** The "Who you are" section of the system prompt: name, version, what it is, the kit's version, and where to look. */
|
|
11
|
-
export function identityPromptSection(identity) {
|
|
12
|
+
export function identityPromptSection(identity, options = {}) {
|
|
12
13
|
const who = `${identity.name}${identity.version ? ` ${identity.version}` : ""}${identity.description ? `: ${identity.description}` : ""}`;
|
|
13
14
|
return `## Who you are
|
|
14
15
|
You are ${who}. You are built on agent-kit ${agentKitVersion()}, which provides your chat (its modes, keys and slash commands) and your built-in tools.
|
|
@@ -16,8 +17,14 @@ You are ${who}. You are built on agent-kit ${agentKitVersion()}, which provides
|
|
|
16
17
|
- Your mode is the kit's (guided, interactive, autonomous or plan), not a permission mode you may see elsewhere: you don't know which one is on until \`about_me\` says.
|
|
17
18
|
- When they ask about your domain (your own commands, configuration, folders), \`about_me\` with \`part: "guide"\`.
|
|
18
19
|
- When they ask how to use you (the chat's modes, keys and commands, approvals, resuming, the command line), load the \`help\` skill.
|
|
19
|
-
- Don't invent what none of them says
|
|
20
|
+
- Don't invent what none of them says.${options.catalog ? `\n${CATALOG_RULES}` : ""}`;
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* When the agent can install extensions (#64): look at its catalogs itself, suggest what fits in
|
|
24
|
+
* one sentence, and leave installing to the person. The tool is read-only; these are the rules.
|
|
25
|
+
*/
|
|
26
|
+
const CATALOG_RULES = `- When the person asks for something none of your tools or skills does well (or that an extension of yours that's off would do), call \`extensions_catalog\` with a few words of the task. If an extension there fits, suggest it in one sentence (what it adds, what it needs, and \`/extensions install <name>@<marketplace>\`) and go on with what you can do without it. Suggest only what it returns; never install, enable or configure an extension yourself; once the person declines one, don't suggest it again in this conversation.
|
|
27
|
+
- When they ask what extensions they have, what one does, how to set it up or why it's off, call \`extensions_catalog\` and load the \`extensions\` skill; when they ask what's new, or its copies are over a week old, call it with \`refresh: true\`.`;
|
|
21
28
|
/**
|
|
22
29
|
* The awareness (#43): the agent knows who it is and what it is at each moment of a session,
|
|
23
30
|
* and the person can ask it. A "Who you are" section, `about_me` (the session's facts, read
|
|
@@ -30,11 +37,13 @@ export function awareness(options = {}) {
|
|
|
30
37
|
name: "awareness",
|
|
31
38
|
plugin: awarenessPluginRoot(),
|
|
32
39
|
missing: ({ spec }) => (spec.identity ? undefined : "needs `identity` in the spec"),
|
|
33
|
-
async contribute({ spec, session }) {
|
|
40
|
+
async contribute({ spec, session, config }) {
|
|
41
|
+
// The catalog only where the agent has a folder for extensions: elsewhere it can't install any (#64).
|
|
42
|
+
const catalog = Boolean(config.extensionDirs);
|
|
34
43
|
return {
|
|
35
|
-
mcpServers: { awareness: createAwarenessServer(session, options.guide) },
|
|
36
|
-
promptSection: identityPromptSection(spec.identity),
|
|
37
|
-
readOnlyTools: ["mcp__awareness__about_me"],
|
|
44
|
+
mcpServers: { awareness: createAwarenessServer(session, options.guide, catalog ? [createCatalogTool(config, session)] : []) },
|
|
45
|
+
promptSection: identityPromptSection(spec.identity, { catalog }),
|
|
46
|
+
readOnlyTools: ["mcp__awareness__about_me", ...(catalog ? ["mcp__awareness__extensions_catalog"] : [])],
|
|
38
47
|
toolLabels: awarenessToolLabels(),
|
|
39
48
|
};
|
|
40
49
|
},
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { getLanguage } from "../../core/messages/index.js";
|
|
2
2
|
const TEXTS = {
|
|
3
|
-
en: { now: "Looking at itself", guide: "Reading its own guide", phrase: ["looked at itself", "looked at itself {n} times"] },
|
|
4
|
-
es: { now: "Mirándose a sí mismo", guide: "Leyendo su propia guía", phrase: ["se miró a sí mismo", "se miró a sí mismo {n} veces"] },
|
|
5
|
-
fr: { now: "Se regarde lui-même", guide: "Lit son propre guide", phrase: ["s'est regardé lui-même", "s'est regardé lui-même {n} fois"] },
|
|
6
|
-
de: { now: "Betrachtet sich selbst", guide: "Liest seine eigene Anleitung", phrase: ["sich selbst betrachtet", "sich selbst {n}-mal betrachtet"] },
|
|
3
|
+
en: { now: "Looking at itself", guide: "Reading its own guide", phrase: ["looked at itself", "looked at itself {n} times"], catalog: "Looking at its extensions", catalogNew: "Looking for new extensions", catalogPhrase: ["looked at its extensions", "looked at its extensions {n} times"] },
|
|
4
|
+
es: { now: "Mirándose a sí mismo", guide: "Leyendo su propia guía", phrase: ["se miró a sí mismo", "se miró a sí mismo {n} veces"], catalog: "Mirando sus extensiones", catalogNew: "Buscando extensiones nuevas", catalogPhrase: ["miró sus extensiones", "miró sus extensiones {n} veces"] },
|
|
5
|
+
fr: { now: "Se regarde lui-même", guide: "Lit son propre guide", phrase: ["s'est regardé lui-même", "s'est regardé lui-même {n} fois"], catalog: "Regarde ses extensions", catalogNew: "Cherche de nouvelles extensions", catalogPhrase: ["a regardé ses extensions", "a regardé ses extensions {n} fois"] },
|
|
6
|
+
de: { now: "Betrachtet sich selbst", guide: "Liest seine eigene Anleitung", phrase: ["sich selbst betrachtet", "sich selbst {n}-mal betrachtet"], catalog: "Sieht seine Erweiterungen an", catalogNew: "Sucht neue Erweiterungen", catalogPhrase: ["seine Erweiterungen angesehen", "seine Erweiterungen {n}-mal angesehen"] },
|
|
7
7
|
};
|
|
8
8
|
/** How the chat shows the awareness's tool (server `awareness`), in the kit's language. */
|
|
9
9
|
export function awarenessToolLabels() {
|
|
10
10
|
const texts = TEXTS[getLanguage()];
|
|
11
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
mcp__awareness__about_me: { label: (input) => (input.part === "guide" ? texts.guide : texts.now), phrase: texts.phrase },
|
|
13
|
+
mcp__awareness__extensions_catalog: { label: (input) => (input.refresh ? texts.catalogNew : texts.catalog), phrase: texts.catalogPhrase },
|
|
14
|
+
};
|
|
12
15
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionFacts } from "../../core/sessionFacts.js";
|
|
2
2
|
/** The session's facts as the model reads them: markdown, in English (ADR-019). */
|
|
3
3
|
export declare function describeSession(facts: SessionFacts): string;
|
|
4
|
-
/** The awareness's MCP server: `about_me`, from the session's facts and the agent's guide. */
|
|
5
|
-
export declare function createAwarenessServer(session: () => Promise<SessionFacts>, guide?: string): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
|
|
4
|
+
/** The awareness's MCP server: `about_me`, from the session's facts and the agent's guide, and any more tools it's given (the catalog, #64). */
|
|
5
|
+
export declare function createAwarenessServer(session: () => Promise<SessionFacts>, guide?: string, more?: readonly unknown[]): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
|
|
@@ -44,8 +44,8 @@ export function describeSession(facts) {
|
|
|
44
44
|
}
|
|
45
45
|
return sections.join("\n\n");
|
|
46
46
|
}
|
|
47
|
-
/** The awareness's MCP server: `about_me`, from the session's facts and the agent's guide. */
|
|
48
|
-
export function createAwarenessServer(session, guide) {
|
|
47
|
+
/** The awareness's MCP server: `about_me`, from the session's facts and the agent's guide, and any more tools it's given (the catalog, #64). */
|
|
48
|
+
export function createAwarenessServer(session, guide, more = []) {
|
|
49
49
|
const aboutMe = tool("about_me", "What you are right now: your mode and the ones the person can switch to, your extensions with their tools and the ones off (with why), your subagents, the commands the person can type, how full your context is. With `part: \"guide\"`, your own guide to your domain (your commands, configuration, folders). It changes during a session: call it whenever the person asks about you, rather than answering from memory.", { part: z.enum(["now", "guide"]).optional().describe("`now` (the default): what you are right now. `guide`: your own guide to your domain.") }, async ({ part }) => {
|
|
50
50
|
if (part === "guide") {
|
|
51
51
|
if (!guide)
|
|
@@ -54,5 +54,6 @@ export function createAwarenessServer(session, guide) {
|
|
|
54
54
|
}
|
|
55
55
|
return ok(describeSession(await session()));
|
|
56
56
|
}, { annotations: { readOnlyHint: true } });
|
|
57
|
-
|
|
57
|
+
// Tools with different input schemas: as the sources' server does.
|
|
58
|
+
return createSdkMcpServer({ name: "awareness", version: "1.0.0", tools: [aboutMe, ...more] });
|
|
58
59
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { createFriendlyToolLabel, truncate, truncatePath, withToolLabels, withTo
|
|
|
9
9
|
export type { Mode, BaseSessionConfig, AgentSpec, AgentIdentity, PlanModeSpec } from "./core/agentSpec.js";
|
|
10
10
|
export { buildSessionOptions, createInputQueue, createDeferred, createModeControl, togglePlanMode, type ModeControl, type ExtensionsStatus } from "./core/session.js";
|
|
11
11
|
export { runQuery, type AgentEvent, type AgentRun, type SessionUsage, type ContextUsage } from "./core/runner.js";
|
|
12
|
-
export type { SessionFacts } from "./core/sessionFacts.js";
|
|
12
|
+
export type { McpAuthentication, SessionFacts } from "./core/sessionFacts.js";
|
|
13
13
|
export { knowledgePluginRoot, knowledgePromptSection } from "./extensions/knowledge/prompt.js";
|
|
14
14
|
export { awareness, type AwarenessOptions } from "./extensions/awareness/index.js";
|
|
15
15
|
export { sources, type SourcesOptions } from "./extensions/sources/index.js";
|
|
@@ -17,9 +17,9 @@ export { knowledge, type KnowledgeOptions } from "./extensions/knowledge/index.j
|
|
|
17
17
|
export { memory, type MemoryOptions } from "./extensions/memory/index.js";
|
|
18
18
|
export { describeCapability, readExtensionManifest, type AboutExtension, type CapabilityText, type DescribedCapability, type Extension, type ExtensionCapability, type ExtensionContext, type ExtensionContribution, type ExtensionManifest, type FolderOption, type UnavailableCapability, } from "./core/extensions.js";
|
|
19
19
|
export { checkRequirements, type ExtensionRequirement, type RequirementCheck } from "./core/extensionRequirements.js";
|
|
20
|
-
export { addExtension, codeStoreDir, installTarget, scopePrivateDir, extensionDataDir, extensionOptionStates, setExtensionOptions, removeExtension, setExtensionEnabled, listInstalled, readExternalManifest, type ExtensionAuthor, type ExtensionDirs, type ExtensionScope, type ExternalManifest, type ExternalToolLabel, type PluginServer, type InstalledExtension, type LockEntry, type ExtensionOptionState, } from "./core/externalExtensions.js";
|
|
20
|
+
export { addExtension, codeStoreDir, installTarget, scopePrivateDir, extensionDataDir, extensionOptionStates, setExtensionOptions, removeExtension, setExtensionEnabled, listInstalled, readExternalManifest, type ExtensionAuthor, type ExtensionDirs, type ExtensionScope, type ExternalManifest, type ExternalToolLabel, type PluginServer, type LocalServer, type RemoteServer, isRemoteServer, remoteHosts, remoteServerName, type InstalledExtension, type LockEntry, type ExtensionOptionState, } from "./core/externalExtensions.js";
|
|
21
21
|
export type { ExtensionOption, OptionValue } from "./core/extensionOptions.js";
|
|
22
|
-
export type { ExtensionRestart, ExtensionSwitch } from "./core/liveExtensions.js";
|
|
22
|
+
export type { ExtensionAuth, ExtensionRestart, ExtensionSwitch, SignIn } from "./core/liveExtensions.js";
|
|
23
23
|
export { packageCacheDir, type CachedPackage } from "./core/npxPackages.js";
|
|
24
24
|
export { addMarketplace, findPlugin, inspectMarketplace, installFromMarketplace, listMarketplaces, readMarketplaceManifest, removeMarketplace, resolvePluginSource, updateMarketplace, type KnownMarketplace, type MarketplaceManifest, type MarketplacePlugin, type PluginSource, } from "./core/marketplaces.js";
|
|
25
25
|
export { extensionCatalog, installExtension, restoreExtensions, updateExtension, type CatalogEntry } from "./core/extensionCatalog.js";
|