@falkenslab/agent-kit 0.22.0 → 0.23.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 +1 -4
- package/dist/chat/chatController.d.ts +1 -0
- package/dist/chat/chatController.js +4 -1
- package/dist/chat/extensions.d.ts +6 -0
- package/dist/chat/extensions.js +25 -0
- package/dist/core/extensionCatalog.d.ts +9 -0
- package/dist/core/extensionCatalog.js +10 -3
- package/dist/core/extensionRequirements.d.ts +66 -0
- package/dist/core/extensionRequirements.js +112 -0
- package/dist/core/extensions.d.ts +68 -1
- package/dist/core/extensions.js +77 -6
- package/dist/core/externalExtensions.d.ts +18 -3
- package/dist/core/externalExtensions.js +34 -25
- package/dist/core/liveExtensions.d.ts +4 -0
- package/dist/core/liveExtensions.js +11 -2
- package/dist/core/marketplaces.d.ts +1 -0
- package/dist/core/messages/de.js +4 -0
- package/dist/core/messages/en.d.ts +8 -0
- package/dist/core/messages/en.js +4 -0
- package/dist/core/messages/es.js +4 -0
- package/dist/core/messages/fr.js +4 -0
- package/dist/core/session.d.ts +4 -4
- package/dist/core/session.js +14 -5
- package/dist/core/sessionFacts.d.ts +6 -0
- package/dist/extensions/awareness/tools.js +6 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/tui/extensionCommand.js +17 -1
- package/package.json +1 -1
- package/schemas/agent-kit-metadata.schema.json +60 -2
- package/schemas/marketplace-catalog.schema.json +10 -0
|
@@ -5,12 +5,14 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
|
+
import { capabilitiesProblem, parseCapabilities, providedBy } from "./extensions.js";
|
|
8
9
|
import { getLanguage } from "./messages/index.js";
|
|
9
10
|
import { truncate } from "./toolLabels.js";
|
|
10
11
|
import { agentKitVersion } from "./version.js";
|
|
11
12
|
import { outsideArchive } from "./packaged.js";
|
|
12
13
|
import { extensionSecretsPath, missingOptions, optionsNamed, parseOptionValue, parseUserConfig, readOptionValues, removeSecrets, storeOptionValues } from "./extensionOptions.js";
|
|
13
14
|
import { isTrusted } from "./workspaceTrust.js";
|
|
15
|
+
import { checkRequirements, describeMissing, findOnPath, parseRequirements, requirementsProblem } from "./extensionRequirements.js";
|
|
14
16
|
import { cachedPackageDir, cachedPackageProblem, cachePackage, npxServer, packageCacheDir, packageSpec } from "./npxPackages.js";
|
|
15
17
|
export const LOCK_FILE = "extensions.lock.json";
|
|
16
18
|
/**
|
|
@@ -91,6 +93,7 @@ export async function readExternalManifest(dir) {
|
|
|
91
93
|
const author = raw.author;
|
|
92
94
|
// `repository` may be a URL or `{ url }`, as in package.json.
|
|
93
95
|
const repository = typeof raw.repository === "string" ? raw.repository : raw.repository?.url;
|
|
96
|
+
const capabilities = parseCapabilities(kit.capabilities);
|
|
94
97
|
return {
|
|
95
98
|
name,
|
|
96
99
|
description: typeof raw.description === "string" ? raw.description : "",
|
|
@@ -102,10 +105,11 @@ export async function readExternalManifest(dir) {
|
|
|
102
105
|
...(typeof repository === "string" ? { repository } : {}),
|
|
103
106
|
...text("license"),
|
|
104
107
|
...(Array.isArray(raw.keywords) ? { keywords: raw.keywords.filter((keyword) => typeof keyword === "string") } : {}),
|
|
105
|
-
provides: kit.provides
|
|
108
|
+
provides: providedBy(kit.provides, capabilities),
|
|
106
109
|
requires: kit.requires ?? [],
|
|
110
|
+
capabilities,
|
|
107
111
|
...(kit.kit ? { kit: kit.kit } : {}),
|
|
108
|
-
|
|
112
|
+
requirements: parseRequirements(kit.requirements),
|
|
109
113
|
servers: await pluginServers(dir, raw),
|
|
110
114
|
...(Array.isArray(kit.readOnlyTools) ? { readOnlyTools: kit.readOnlyTools.filter((tool) => typeof tool === "string") } : {}),
|
|
111
115
|
labels: kit.labels ?? {},
|
|
@@ -320,12 +324,13 @@ function manifestToolLabels(manifest) {
|
|
|
320
324
|
}));
|
|
321
325
|
}
|
|
322
326
|
/** The extension an installed one makes: its servers started through the launcher, its rules from its manifest. */
|
|
323
|
-
function externalExtension(installed, manifest, cache) {
|
|
327
|
+
function externalExtension(installed, manifest, cache, unavailable = []) {
|
|
324
328
|
return {
|
|
325
329
|
name: manifest.name,
|
|
326
330
|
plugin: installed.dir,
|
|
327
331
|
external: true,
|
|
328
332
|
installedIn: installed.scopeDir,
|
|
333
|
+
...(unavailable.length ? { unavailable } : {}),
|
|
329
334
|
async contribute() {
|
|
330
335
|
const servers = {};
|
|
331
336
|
if (Object.keys(manifest.servers).length)
|
|
@@ -395,23 +400,19 @@ export async function loadExternalExtensions(dirs) {
|
|
|
395
400
|
if (typeof reason === "string")
|
|
396
401
|
off.push({ name: installed.name, reason });
|
|
397
402
|
else
|
|
398
|
-
extensions.push(externalExtension(installed, reason, cache));
|
|
403
|
+
extensions.push(externalExtension(installed, reason.manifest, cache, reason.unavailable));
|
|
399
404
|
}
|
|
400
405
|
return { extensions, off, untrusted };
|
|
401
406
|
}
|
|
402
407
|
/** Why an installed extension can't load, or its manifest when it can. */
|
|
403
408
|
/** Whether a program is on the `PATH`, as a shell would find it (with `PATHEXT`'s extensions on Windows). */
|
|
404
409
|
export async function onPath(program, env = process.env) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return true;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return false;
|
|
410
|
+
return (await findOnPath(program, env)) !== undefined;
|
|
411
|
+
}
|
|
412
|
+
/** A requirement this computer lacks, as the person reads it: what it is, what for, and how to install it. */
|
|
413
|
+
export function missingRequirement(check) {
|
|
414
|
+
const needed = check.requirement.for.length ? ` for ${check.requirement.for.join(", ")}` : "";
|
|
415
|
+
return `needs ${describeMissing(check)}${needed}`;
|
|
415
416
|
}
|
|
416
417
|
async function problem(installed, cache) {
|
|
417
418
|
let manifest;
|
|
@@ -429,16 +430,22 @@ async function problem(installed, cache) {
|
|
|
429
430
|
return `is installed as "${installed.name}" but its manifest says "${manifest.name}"`;
|
|
430
431
|
if (manifest.kit && !inKitRange(agentKitVersion(), manifest.kit))
|
|
431
432
|
return `works with agent-kit ${manifest.kit}, and this agent runs ${agentKitVersion()}`;
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
433
|
+
const requirements = requirementsProblem(manifest.requirements, manifest.provides);
|
|
434
|
+
if (requirements)
|
|
435
|
+
return requirements;
|
|
436
|
+
// A program the whole extension needs keeps it off; one only some capabilities need, those (#63).
|
|
437
|
+
const checks = (await checkRequirements(manifest.requirements)).filter((check) => !check.ok);
|
|
438
|
+
const whole = checks.filter((check) => !check.requirement.for.length);
|
|
439
|
+
if (whole.length)
|
|
440
|
+
return whole.map((check) => missingRequirement(check)).join("; ");
|
|
441
|
+
const unavailable = checks.map((check) => ({ capabilities: check.requirement.for, reason: `needs ${describeMissing(check)}` }));
|
|
442
|
+
const capabilities = await capabilitiesProblem(installed.dir, manifest.capabilities);
|
|
443
|
+
if (capabilities)
|
|
444
|
+
return capabilities;
|
|
438
445
|
const options = optionsProblem(manifest, await readOptionValues(installed.privateDir, installed.name, manifest.userConfig, installed.lock.options));
|
|
439
446
|
if (options)
|
|
440
447
|
return options;
|
|
441
|
-
return (await serversProblem(manifest, installed.dir, { cache, locked: installed.lock.packages })) ?? manifest;
|
|
448
|
+
return (await serversProblem(manifest, installed.dir, { cache, locked: installed.lock.packages })) ?? { manifest, unavailable };
|
|
442
449
|
}
|
|
443
450
|
const exec = promisify(execFile);
|
|
444
451
|
/** Whether `source` names a git repository rather than a folder. */
|
|
@@ -477,9 +484,11 @@ export async function addExtension(source, scopeDir, options = {}) {
|
|
|
477
484
|
if (options.subdir)
|
|
478
485
|
from = path.join(from, options.subdir);
|
|
479
486
|
const manifest = await readExternalManifest(from);
|
|
480
|
-
const servers = await serversProblem(manifest, from);
|
|
487
|
+
const servers = (await serversProblem(manifest, from)) ?? (await capabilitiesProblem(from, manifest.capabilities)) ?? requirementsProblem(manifest.requirements, manifest.provides);
|
|
481
488
|
if (servers)
|
|
482
489
|
throw new Error(`${manifest.name} can't be installed: it ${servers}.`);
|
|
490
|
+
// What it needs and this computer lacks: said, never installed, and it installs anyway (#63).
|
|
491
|
+
const missing = (await checkRequirements(manifest.requirements)).filter((check) => !check.ok).map((check) => missingRequirement(check));
|
|
483
492
|
const target = path.join(scopeDir, manifest.name);
|
|
484
493
|
// Replacing what's installed there would delete the source itself.
|
|
485
494
|
if (path.resolve(from) === path.resolve(target))
|
|
@@ -506,12 +515,12 @@ export async function addExtension(source, scopeDir, options = {}) {
|
|
|
506
515
|
}
|
|
507
516
|
lock[manifest.name] = { source: origin, ...provenance, sha256, stored: true, ...locked, enabled: true, installed: new Date().toISOString() };
|
|
508
517
|
await writeLock(scopeDir, lock);
|
|
509
|
-
return { name: manifest.name, replaced, ...(commit ? { commit } : {}) };
|
|
518
|
+
return { name: manifest.name, replaced, missing, ...(commit ? { commit } : {}) };
|
|
510
519
|
}
|
|
511
520
|
if (options.link) {
|
|
512
521
|
lock[manifest.name] = { source: from, linked: true, sha256: "", ...locked, enabled: true, installed: new Date().toISOString() };
|
|
513
522
|
await writeLock(scopeDir, lock);
|
|
514
|
-
return { name: manifest.name, replaced };
|
|
523
|
+
return { name: manifest.name, replaced, missing };
|
|
515
524
|
}
|
|
516
525
|
await cp(from, target, { recursive: true, filter: (file) => path.basename(file) !== ".git" });
|
|
517
526
|
lock[manifest.name] = {
|
|
@@ -523,7 +532,7 @@ export async function addExtension(source, scopeDir, options = {}) {
|
|
|
523
532
|
installed: new Date().toISOString(),
|
|
524
533
|
};
|
|
525
534
|
await writeLock(scopeDir, lock);
|
|
526
|
-
return { name: manifest.name, replaced, ...(commit ? { commit } : {}) };
|
|
535
|
+
return { name: manifest.name, replaced, missing, ...(commit ? { commit } : {}) };
|
|
527
536
|
}
|
|
528
537
|
finally {
|
|
529
538
|
if (clone)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionsStatus } from "./session.js";
|
|
2
|
+
import { type ExtensionCapability, type UnavailableCapability } from "./extensions.js";
|
|
2
3
|
import type { SessionLive, StaticSessionFacts } from "./sessionFacts.js";
|
|
3
4
|
import type { ToolLabels } from "./toolLabels.js";
|
|
4
5
|
/**
|
|
@@ -43,6 +44,9 @@ export interface LiveExtension {
|
|
|
43
44
|
agents: string[];
|
|
44
45
|
description: string;
|
|
45
46
|
provides: string[];
|
|
47
|
+
capabilities: ExtensionCapability[];
|
|
48
|
+
/** What it can't do on this computer (#63). */
|
|
49
|
+
unavailable: UnavailableCapability[];
|
|
46
50
|
help: string[];
|
|
47
51
|
}
|
|
48
52
|
/** The session's own lists the switch keeps in step: the same objects the gates, the chat and the view read. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cp, lstat, mkdir, readdir, rm, symlink, unlink } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { aboutExtension, describeCapability } from "./extensions.js";
|
|
3
4
|
import { hashExtension, listInstalled, readLock } from "./externalExtensions.js";
|
|
4
5
|
import { cachedPackagesIntact, packageCacheDir } from "./npxPackages.js";
|
|
5
6
|
/**
|
|
@@ -122,9 +123,17 @@ function follow(extension, enabled, targets) {
|
|
|
122
123
|
targets.allowedSubagentTypes.push(...extension.agents);
|
|
123
124
|
Object.assign(targets.toolLabels, extension.toolLabels);
|
|
124
125
|
status.active.push(extension.name);
|
|
125
|
-
status.about[extension.name] = { description: extension.description, provides: extension.provides };
|
|
126
|
+
status.about[extension.name] = aboutExtension({ name: extension.name, description: extension.description, provides: extension.provides, requires: [], capabilities: extension.capabilities }, extension.servers, facts.language, extension.unavailable);
|
|
126
127
|
status.inactive = status.inactive.filter((entry) => entry.name !== extension.name);
|
|
127
|
-
facts.extensions.active.push({
|
|
128
|
+
facts.extensions.active.push({
|
|
129
|
+
name: extension.name,
|
|
130
|
+
description: extension.description,
|
|
131
|
+
provides: extension.provides,
|
|
132
|
+
capabilities: extension.capabilities.map((capability) => describeCapability(capability, "en")),
|
|
133
|
+
unavailable: extension.unavailable.map(({ capability, reason }) => ({ ...describeCapability(capability, "en"), reason })),
|
|
134
|
+
servers: extension.servers,
|
|
135
|
+
help: extension.help,
|
|
136
|
+
});
|
|
128
137
|
facts.extensions.inactive = facts.extensions.inactive.filter((entry) => entry.name !== extension.name);
|
|
129
138
|
}
|
|
130
139
|
else {
|
package/dist/core/messages/de.js
CHANGED
|
@@ -28,6 +28,9 @@ export const de = {
|
|
|
28
28
|
extensionsRunning: (names) => `Aktiv: ${names}`,
|
|
29
29
|
extensionsNone: "(keine Erweiterungen)",
|
|
30
30
|
extensionOff: (name, reason) => `${name} ist aus: ${reason}`,
|
|
31
|
+
extensionCan: (name) => `Was dein Agent mit ${name} kann:`,
|
|
32
|
+
capabilityToolsMissing: (tools) => `(sein Server bietet ${tools} nicht an)`,
|
|
33
|
+
capabilityUnavailable: (label, reason) => `${label}: auf diesem Computer nicht verfügbar: ${reason}`,
|
|
31
34
|
extensionsInstalled: "Installiert:",
|
|
32
35
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}, in derselben Sitzung)`,
|
|
33
36
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}: die Sitzung wird mit derselben Unterhaltung neu geöffnet)`,
|
|
@@ -47,6 +50,7 @@ export const de = {
|
|
|
47
50
|
extensionsNoMatch: "(keine Erweiterung passt: /extensions search mit anderen Wörtern, oder ohne, um alle zu sehen)",
|
|
48
51
|
extensionIsInstalled: "(installiert)",
|
|
49
52
|
extensionUpdateAvailable: (installed) => `(installiert: ${installed}, Update verfügbar)`,
|
|
53
|
+
extensionLacks: (name, what) => `(${name}: ${what})`,
|
|
50
54
|
extensionInstalled: (name) => `(${name} installiert: in einem neuen Gespräch da, oder sofort mit /extensions reload, ohne dieses zu verlieren)`,
|
|
51
55
|
extensionUpdated: (name, from, to) => `(${name} aktualisiert, ${from} → ${to}: in einem neuen Gespräch da, oder sofort mit /extensions reload)`,
|
|
52
56
|
extensionsUpToDate: "(alle Erweiterungen aus einem Marketplace sind aktuell)",
|
|
@@ -37,6 +37,12 @@ export interface Messages {
|
|
|
37
37
|
extensionsRunning(names: string): string;
|
|
38
38
|
extensionsNone: string;
|
|
39
39
|
extensionOff(name: string, reason: string): string;
|
|
40
|
+
/** Before an active extension's capabilities (#62), one per line after it. */
|
|
41
|
+
extensionCan(name: string): string;
|
|
42
|
+
/** A capability's tools its server doesn't offer in this session. */
|
|
43
|
+
capabilityToolsMissing(tools: string): string;
|
|
44
|
+
/** A capability that isn't available on this computer (#63), with why (in English, as every reason). */
|
|
45
|
+
capabilityUnavailable(label: string, reason: string): string;
|
|
40
46
|
extensionsInstalled: string;
|
|
41
47
|
/** An installed extension turned off or on in the running session (#49). */
|
|
42
48
|
extensionSwitched(name: string, enabled: boolean): string;
|
|
@@ -60,6 +66,8 @@ export interface Messages {
|
|
|
60
66
|
extensionIsInstalled: string;
|
|
61
67
|
extensionUpdateAvailable(installed: string): string;
|
|
62
68
|
extensionInstalled(name: string): string;
|
|
69
|
+
/** After installing: a program it needs that this computer lacks (#63); `what` is the reason, in English. */
|
|
70
|
+
extensionLacks(name: string, what: string): string;
|
|
63
71
|
extensionUpdated(name: string, from: string, to: string): string;
|
|
64
72
|
extensionsUpToDate: string;
|
|
65
73
|
extensionRemoved(name: string, withData: boolean): string;
|
package/dist/core/messages/en.js
CHANGED
|
@@ -27,6 +27,9 @@ export const en = {
|
|
|
27
27
|
extensionsRunning: (names) => `Running with: ${names}`,
|
|
28
28
|
extensionsNone: "(no extensions)",
|
|
29
29
|
extensionOff: (name, reason) => `${name} is off: it ${reason}`,
|
|
30
|
+
extensionCan: (name) => `What ${name} lets your agent do:`,
|
|
31
|
+
capabilityToolsMissing: (tools) => `(its server doesn't offer ${tools})`,
|
|
32
|
+
capabilityUnavailable: (label, reason) => `${label}: not on this computer, it ${reason}`,
|
|
30
33
|
extensionsInstalled: "Installed:",
|
|
31
34
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}, in this same session)`,
|
|
32
35
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}: the session reopens, with the same conversation)`,
|
|
@@ -47,6 +50,7 @@ export const en = {
|
|
|
47
50
|
extensionIsInstalled: "(installed)",
|
|
48
51
|
extensionUpdateAvailable: (installed) => `(installed: ${installed}, update available)`,
|
|
49
52
|
extensionInstalled: (name) => `(${name} installed: it's there in a new conversation, or now with /extensions reload, keeping this one)`,
|
|
53
|
+
extensionLacks: (name, what) => `(${name} ${what})`,
|
|
50
54
|
extensionUpdated: (name, from, to) => `(${name} updated, ${from} → ${to}: it's there in a new conversation, or now with /extensions reload)`,
|
|
51
55
|
extensionsUpToDate: "(every extension from a marketplace is up to date)",
|
|
52
56
|
extensionRemoved: (name, withData) => `(${name} removed${withData ? ", with its data" : "; its data is kept for a reinstall"})`,
|
package/dist/core/messages/es.js
CHANGED
|
@@ -28,6 +28,9 @@ export const es = {
|
|
|
28
28
|
extensionsRunning: (names) => `Funcionando con: ${names}`,
|
|
29
29
|
extensionsNone: "(ninguna extensión)",
|
|
30
30
|
extensionOff: (name, reason) => `${name} está desactivada: ${reason}`,
|
|
31
|
+
extensionCan: (name) => `Lo que tu agente puede hacer con ${name}:`,
|
|
32
|
+
capabilityToolsMissing: (tools) => `(su servidor no ofrece ${tools})`,
|
|
33
|
+
capabilityUnavailable: (label, reason) => `${label}: no disponible en este ordenador: ${reason}`,
|
|
31
34
|
extensionsInstalled: "Instaladas:",
|
|
32
35
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "activada" : "desactivada"}, en esta misma sesión)`,
|
|
33
36
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "activada" : "desactivada"}: la sesión se reabre, con la misma conversación)`,
|
|
@@ -47,6 +50,7 @@ export const es = {
|
|
|
47
50
|
extensionsNoMatch: "(ninguna extensión coincide: /extensions search con otras palabras, o sin ninguna para verlas todas)",
|
|
48
51
|
extensionIsInstalled: "(instalada)",
|
|
49
52
|
extensionUpdateAvailable: (installed) => `(instalada: ${installed}, hay actualización)`,
|
|
53
|
+
extensionLacks: (name, what) => `(${name}: ${what})`,
|
|
50
54
|
extensionInstalled: (name) => `(${name} instalada: está en una conversación nueva, o ya con /extensions reload, sin perder esta)`,
|
|
51
55
|
extensionUpdated: (name, from, to) => `(${name} actualizada, ${from} → ${to}: está en una conversación nueva, o ya con /extensions reload)`,
|
|
52
56
|
extensionsUpToDate: "(todas las extensiones de un marketplace están al día)",
|
package/dist/core/messages/fr.js
CHANGED
|
@@ -28,6 +28,9 @@ export const fr = {
|
|
|
28
28
|
extensionsRunning: (names) => `En service : ${names}`,
|
|
29
29
|
extensionsNone: "(aucune extension)",
|
|
30
30
|
extensionOff: (name, reason) => `${name} est désactivée : ${reason}`,
|
|
31
|
+
extensionCan: (name) => `Ce que votre agent peut faire avec ${name} :`,
|
|
32
|
+
capabilityToolsMissing: (tools) => `(son serveur ne propose pas ${tools})`,
|
|
33
|
+
capabilityUnavailable: (label, reason) => `${label} : indisponible sur cet ordinateur : ${reason}`,
|
|
31
34
|
extensionsInstalled: "Installées :",
|
|
32
35
|
extensionSwitched: (name, enabled) => `(${name} ${enabled ? "activée" : "désactivée"}, dans cette même session)`,
|
|
33
36
|
extensionToggled: (name, enabled) => `(${name} ${enabled ? "activée" : "désactivée"} : la session se rouvre, avec la même conversation)`,
|
|
@@ -47,6 +50,7 @@ export const fr = {
|
|
|
47
50
|
extensionsNoMatch: "(aucune extension ne correspond : /extensions search avec d'autres mots, ou sans pour toutes les voir)",
|
|
48
51
|
extensionIsInstalled: "(installée)",
|
|
49
52
|
extensionUpdateAvailable: (installed) => `(installée : ${installed}, mise à jour disponible)`,
|
|
53
|
+
extensionLacks: (name, what) => `(${name} : ${what})`,
|
|
50
54
|
extensionInstalled: (name) => `(${name} installée : elle est là dans une nouvelle conversation, ou dès maintenant avec /extensions reload, en gardant celle-ci)`,
|
|
51
55
|
extensionUpdated: (name, from, to) => `(${name} mise à jour, ${from} → ${to} : elle est là dans une nouvelle conversation, ou dès maintenant avec /extensions reload)`,
|
|
52
56
|
extensionsUpToDate: "(toutes les extensions d'un marketplace sont à jour)",
|
package/dist/core/session.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { 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
|
+
import { type AboutExtension } from "./extensions.js";
|
|
4
5
|
import { type ExtensionDirs, type InstalledExtension } from "./externalExtensions.js";
|
|
5
6
|
import type { ToolLabels } from "./toolLabels.js";
|
|
6
7
|
import type { AgentSpec, BaseSessionConfig } from "./agentSpec.js";
|
|
@@ -65,14 +66,13 @@ export interface ExtensionsStatus {
|
|
|
65
66
|
/** The names of the extensions this session runs with, the kit's and the agent's too. */
|
|
66
67
|
active: string[];
|
|
67
68
|
/** What each active one is, from its manifest: for a view that lists them. */
|
|
68
|
-
about: Record<string,
|
|
69
|
-
description: string;
|
|
70
|
-
provides: string[];
|
|
71
|
-
}>;
|
|
69
|
+
about: Record<string, AboutExtension>;
|
|
72
70
|
inactive: {
|
|
73
71
|
name: string;
|
|
74
72
|
reason: string;
|
|
75
73
|
}[];
|
|
74
|
+
/** The running session's tools, as the model names them (empty until it has started): what a capability's tools are checked against. */
|
|
75
|
+
sessionTools?: () => readonly string[];
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* The CLI's folder for a project, where it keeps each session's transcript and large tool
|
package/dist/core/session.js
CHANGED
|
@@ -13,7 +13,7 @@ import { createManualLoginServer } from "./tools/manualLogin.js";
|
|
|
13
13
|
import { createTimeServer } from "./tools/time.js";
|
|
14
14
|
import { TODO_TOOL } from "./todos.js";
|
|
15
15
|
import { createModeControl } from "./modeControl.js";
|
|
16
|
-
import { extensionsPromptSection, resolveExtensions, skillsMissingCapabilities } from "./extensions.js";
|
|
16
|
+
import { aboutExtension, describeCapability, extensionsPromptSection, resolveExtensions, skillsMissingCapabilities } from "./extensions.js";
|
|
17
17
|
import { allowAnyMcpTool } from "./mcpPermissions.js";
|
|
18
18
|
import { pluginAgents } from "./pluginAgents.js";
|
|
19
19
|
import { outsideArchive, unpackedClaudeExecutable } from "./packaged.js";
|
|
@@ -130,7 +130,11 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
130
130
|
// `requires` a capability no active extension provides is left out; since the SDK's
|
|
131
131
|
// `skillOverrides` doesn't reach plugin skills (confirmed empirically), leaving one out takes
|
|
132
132
|
// a list: the plugins' skills and the project's, without it.
|
|
133
|
-
|
|
133
|
+
// And the skills of a capability that isn't available on this computer (#63).
|
|
134
|
+
const missingCapabilities = [
|
|
135
|
+
...(await skillsMissingCapabilities(pluginRoots, resolved.capabilities)),
|
|
136
|
+
...resolved.active.flatMap(({ manifest, unavailable }) => unavailable.flatMap(({ capability }) => capability.skills.map((skill) => `${manifest.name}:${skill}`))),
|
|
137
|
+
];
|
|
134
138
|
const requested = spec.skills === "plugins" ? await pluginSkills(pluginRoots) : spec.skills;
|
|
135
139
|
const offeredSkills = missingCapabilities.length && (requested === undefined || requested === "all")
|
|
136
140
|
? [...(await pluginSkills(pluginRoots)), ...((spec.settingSources ?? ["project"]).includes("project") ? await projectSkills(config.projectDir) : [])]
|
|
@@ -352,7 +356,8 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
352
356
|
untrusted: external.untrusted.map((installed) => ({ name: installed.name, ...(installed.manifest?.version ? { version: installed.manifest.version } : {}), source: installed.lock.marketplace ?? installed.lock.source })),
|
|
353
357
|
installed: await listInstalled(extensionDirs),
|
|
354
358
|
active: resolved.active.map(({ extension }) => extension.name),
|
|
355
|
-
about: Object.fromEntries(resolved.active.map(({ extension, manifest }) => [extension.name,
|
|
359
|
+
about: Object.fromEntries(resolved.active.map(({ extension, manifest, unavailable }, index) => [extension.name, aboutExtension(manifest, Object.keys(added[index]?.mcpServers ?? {}), language, unavailable)])),
|
|
360
|
+
sessionTools: () => late.view?.controls().tools ?? [],
|
|
356
361
|
inactive: resolved.inactive,
|
|
357
362
|
};
|
|
358
363
|
// What the session is, for the extensions that ask (sessionFacts.ts, #43).
|
|
@@ -361,10 +366,12 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
361
366
|
kitVersion: agentKitVersion(),
|
|
362
367
|
language,
|
|
363
368
|
extensions: {
|
|
364
|
-
active: resolved.active.map(({ extension, manifest }, index) => ({
|
|
369
|
+
active: resolved.active.map(({ extension, manifest, unavailable }, index) => ({
|
|
365
370
|
name: extension.name,
|
|
366
371
|
description: manifest.description,
|
|
367
372
|
provides: manifest.provides,
|
|
373
|
+
capabilities: manifest.capabilities.map((capability) => describeCapability(capability, "en")),
|
|
374
|
+
unavailable: unavailable.map(({ capability, reason }) => ({ ...describeCapability(capability, "en"), reason })),
|
|
368
375
|
servers: Object.keys(added[index]?.mcpServers ?? {}),
|
|
369
376
|
help: added[index]?.helpLines ?? [],
|
|
370
377
|
})),
|
|
@@ -379,7 +386,7 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
379
386
|
registerSessionView(sdkOptions, late.view);
|
|
380
387
|
// Turning an installed extension off and on while the session runs (#49), and restarting its
|
|
381
388
|
// servers when its options change (#55).
|
|
382
|
-
const liveExtensions = resolved.active.flatMap(({ extension, manifest }, index) => {
|
|
389
|
+
const liveExtensions = resolved.active.flatMap(({ extension, manifest, unavailable }, index) => {
|
|
383
390
|
const installed = installedDirs.get(extension.name);
|
|
384
391
|
if (!installed)
|
|
385
392
|
return [];
|
|
@@ -396,6 +403,8 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
|
|
|
396
403
|
agents: Object.keys(fromPlugins).filter((agent) => agent.startsWith(`${extension.name}:`)),
|
|
397
404
|
description: manifest.description,
|
|
398
405
|
provides: manifest.provides,
|
|
406
|
+
capabilities: manifest.capabilities,
|
|
407
|
+
unavailable,
|
|
399
408
|
help: contribution?.helpLines ?? [],
|
|
400
409
|
},
|
|
401
410
|
];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServerStatus, Options, SlashCommand } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import type { AgentIdentity, Mode } from "./agentSpec.js";
|
|
3
|
+
import type { DescribedCapability } from "./extensions.js";
|
|
3
4
|
import type { Language } from "./language.js";
|
|
4
5
|
import type { ModeControl } from "./modeControl.js";
|
|
5
6
|
import type { ContextUsage } from "./runner.js";
|
|
@@ -20,10 +21,15 @@ export interface SessionFacts {
|
|
|
20
21
|
switchableModes: Mode[];
|
|
21
22
|
/** The extensions running (with their tools and what they say about themselves here), and the ones off, with why. */
|
|
22
23
|
extensions: {
|
|
24
|
+
/** Each with its capabilities (#62), in English: what the model reads. */
|
|
23
25
|
active: {
|
|
24
26
|
name: string;
|
|
25
27
|
description: string;
|
|
26
28
|
provides: string[];
|
|
29
|
+
capabilities: DescribedCapability[];
|
|
30
|
+
unavailable: (DescribedCapability & {
|
|
31
|
+
reason: string;
|
|
32
|
+
})[];
|
|
27
33
|
tools: string[];
|
|
28
34
|
help: string[];
|
|
29
35
|
}[];
|
|
@@ -17,9 +17,13 @@ export function describeSession(facts) {
|
|
|
17
17
|
const sections = [`# You, right now\n\nYou are ${who}, on agent-kit ${facts.kitVersion}.`];
|
|
18
18
|
const others = facts.switchableModes.filter((mode) => mode !== facts.mode);
|
|
19
19
|
sections.push(`## Mode\n\nYou are in **${facts.mode}** mode now. ${facts.switchableModes.length > 1 ? `The person can switch it with Shift+Tab to ${others.join(" or ")}.` : "It can't be switched in this session."}`);
|
|
20
|
-
const active = facts.extensions.active.map(({ name, description, tools, help }) => {
|
|
20
|
+
const active = facts.extensions.active.map(({ name, description, capabilities, unavailable, tools, help }) => {
|
|
21
21
|
const toolList = tools.length ? ` Tools: ${tools.map(short).join(", ")}.` : "";
|
|
22
|
-
|
|
22
|
+
// What it lets you do, as the person reads it in /extensions (#62).
|
|
23
|
+
const can = capabilities.map(({ id, label, description: what }) => `\n - Can: ${label} (${id})${what ? `: ${what}` : ""}`).join("");
|
|
24
|
+
// What it can't do on this computer, and what to install (#63).
|
|
25
|
+
const cant = unavailable.map(({ id, label, reason }) => `\n - Can't here: ${label} (${id}): it ${reason}.`).join("");
|
|
26
|
+
return `- **${name}**: ${description}${toolList}${can}${cant}${help.map((line) => `\n ${line}`).join("")}`;
|
|
23
27
|
});
|
|
24
28
|
const inactive = facts.extensions.inactive.map(({ name, reason }) => `- **${name}** is off: it ${reason}.`);
|
|
25
29
|
sections.push(`## Extensions\n\n${[...active, ...inactive].join("\n") || "None."}`);
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export { awareness, type AwarenessOptions } from "./extensions/awareness/index.j
|
|
|
15
15
|
export { sources, type SourcesOptions } from "./extensions/sources/index.js";
|
|
16
16
|
export { knowledge, type KnowledgeOptions } from "./extensions/knowledge/index.js";
|
|
17
17
|
export { memory, type MemoryOptions } from "./extensions/memory/index.js";
|
|
18
|
-
export { readExtensionManifest, type Extension, type ExtensionContext, type ExtensionContribution, type ExtensionManifest, type FolderOption, } from "./core/extensions.js";
|
|
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
|
+
export { checkRequirements, type ExtensionRequirement, type RequirementCheck } from "./core/extensionRequirements.js";
|
|
19
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
21
|
export type { ExtensionOption, OptionValue } from "./core/extensionOptions.js";
|
|
21
22
|
export type { ExtensionRestart, ExtensionSwitch } from "./core/liveExtensions.js";
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,8 @@ export { awareness } from "./extensions/awareness/index.js";
|
|
|
15
15
|
export { sources } from "./extensions/sources/index.js";
|
|
16
16
|
export { knowledge } from "./extensions/knowledge/index.js";
|
|
17
17
|
export { memory } from "./extensions/memory/index.js";
|
|
18
|
-
export { readExtensionManifest, } from "./core/extensions.js";
|
|
18
|
+
export { describeCapability, readExtensionManifest, } from "./core/extensions.js";
|
|
19
|
+
export { checkRequirements } from "./core/extensionRequirements.js";
|
|
19
20
|
export { addExtension, codeStoreDir, installTarget, scopePrivateDir, extensionDataDir, extensionOptionStates, setExtensionOptions, removeExtension, setExtensionEnabled, listInstalled, readExternalManifest, } from "./core/externalExtensions.js";
|
|
20
21
|
export { packageCacheDir } from "./core/npxPackages.js";
|
|
21
22
|
export { addMarketplace, findPlugin, inspectMarketplace, installFromMarketplace, listMarketplaces, readMarketplaceManifest, removeMarketplace, resolvePluginSource, updateMarketplace, } from "./core/marketplaces.js";
|
|
@@ -6,6 +6,8 @@ import { addExtension, extensionOptionStates, installTarget, folderSize, isGitSo
|
|
|
6
6
|
import { describe, describeOptions } from "../chat/extensions.js";
|
|
7
7
|
import { extensionCatalog, knownMarketplaces as catalogMarketplaces, restoreExtensions, updateExtension } from "../core/extensionCatalog.js";
|
|
8
8
|
import { trustWorkspace } from "../core/workspaceTrust.js";
|
|
9
|
+
import { describeCapability } from "../core/extensions.js";
|
|
10
|
+
import { getLanguage } from "../core/messages/index.js";
|
|
9
11
|
export { chatExtensionsCommand, describe } from "../chat/extensions.js";
|
|
10
12
|
/**
|
|
11
13
|
* The `extension` command an agent exposes in its own binary (#37), e.g. `captain extension add
|
|
@@ -126,6 +128,11 @@ export async function runExtensionCommand(argv, options) {
|
|
|
126
128
|
const packages = installTarget(options.dirs, scope);
|
|
127
129
|
// Installed into the workspace by the person: trusted there as it is.
|
|
128
130
|
const trusted = async (name) => void (scope === "project" && (await trustWorkspace(options.dirs, [name])));
|
|
131
|
+
// What it needs and this computer lacks (#63): said, never installed.
|
|
132
|
+
const lacking = (missing) => {
|
|
133
|
+
for (const line of missing)
|
|
134
|
+
write(`It ${line}.`);
|
|
135
|
+
};
|
|
129
136
|
// A plugin of a marketplace, unless it's a folder here or a git URL.
|
|
130
137
|
const isFolder = await stat(path.resolve(source)).then((found) => found.isDirectory(), () => false);
|
|
131
138
|
if (flag("link")) {
|
|
@@ -135,6 +142,7 @@ export async function runExtensionCommand(argv, options) {
|
|
|
135
142
|
await trusted(result.name);
|
|
136
143
|
await configure(result.name, dir);
|
|
137
144
|
write(`Linked ${result.name} in the ${scope} scope: it runs from ${path.resolve(source)}, whose files aren't checked. A rebuild takes effect when its server starts again (a new session, or \`/extensions disable\` and \`enable\` in the chat).`);
|
|
145
|
+
lacking(result.missing);
|
|
138
146
|
return true;
|
|
139
147
|
}
|
|
140
148
|
if (!isFolder && !isGitSource(source) && /^[A-Za-z0-9][\w.-]*(@[A-Za-z0-9][\w.-]*)?$/.test(source)) {
|
|
@@ -150,6 +158,7 @@ export async function runExtensionCommand(argv, options) {
|
|
|
150
158
|
await trusted(result.name);
|
|
151
159
|
await configure(result.name, dir);
|
|
152
160
|
write(`${result.replaced ? "Reinstalled" : "Installed"} ${result.name} from ${result.marketplace} in the ${scope} scope (${dir})${result.commit ? `, at ${result.commit.slice(0, 12)}` : ""}. It's enabled: it runs from the next session.`);
|
|
161
|
+
lacking(result.missing);
|
|
153
162
|
return true;
|
|
154
163
|
}
|
|
155
164
|
const subdir = value("path");
|
|
@@ -157,6 +166,7 @@ export async function runExtensionCommand(argv, options) {
|
|
|
157
166
|
await trusted(result.name);
|
|
158
167
|
await configure(result.name, dir);
|
|
159
168
|
write(`${result.replaced ? "Reinstalled" : "Installed"} ${result.name} in the ${scope} scope (${dir})${result.commit ? `, at ${result.commit.slice(0, 12)}` : ""}. It's enabled: it runs from the next session.`);
|
|
169
|
+
lacking(result.missing);
|
|
160
170
|
return true;
|
|
161
171
|
}
|
|
162
172
|
case "remove":
|
|
@@ -402,9 +412,15 @@ async function info(extension) {
|
|
|
402
412
|
...field("Repository", manifest.repository),
|
|
403
413
|
...field("Keywords", manifest.keywords?.join(", ")),
|
|
404
414
|
...field("Works with", manifest.kit && `agent-kit ${manifest.kit}`),
|
|
415
|
+
// What it lets the agent do (#62), in the kit's language: one per line.
|
|
416
|
+
...manifest.capabilities.flatMap((capability, index) => {
|
|
417
|
+
const { id, label, description } = describeCapability(capability, getLanguage());
|
|
418
|
+
return field(index ? "" : "Can do", `${label} (${id})${description ? `: ${description}` : ""}`);
|
|
419
|
+
}),
|
|
405
420
|
...field("Provides", manifest.provides.join(", ") || undefined),
|
|
406
421
|
...field("Requires", manifest.requires.join(", ") || undefined),
|
|
407
|
-
|
|
422
|
+
// The programs it needs on this computer (#63), one per line, with what for.
|
|
423
|
+
...manifest.requirements.flatMap((requirement, index) => field(index ? "" : "Needs", `${requirement.name} (${requirement.program}${requirement.version ? ` ${requirement.version.min} or later` : ""})${requirement.for.length ? ` for ${requirement.for.join(", ")}` : ""}`)),
|
|
408
424
|
...field("Servers", Object.keys(manifest.servers).join(", ") || undefined),
|
|
409
425
|
...field("Only read", manifest.readOnlyTools
|
|
410
426
|
? `${manifest.readOnlyTools.join(", ") || "none"} (its manifest)`
|
package/package.json
CHANGED
|
@@ -11,9 +11,18 @@
|
|
|
11
11
|
"type": "string",
|
|
12
12
|
"pattern": "^\\s*(?:(?:>=|<=|>|<|=)?\\s*v?\\d+(?:\\.\\d+){0,2})(?:\\s+(?:>=|<=|>|<|=)?\\s*v?\\d+(?:\\.\\d+){0,2})*\\s*$"
|
|
13
13
|
},
|
|
14
|
-
"provides": { "description": "Capabilities it lets the agent use.", "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
14
|
+
"provides": { "description": "Capabilities it lets the agent use, by name only. Its capabilities' ids count as provided too.", "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
15
|
+
"capabilities": {
|
|
16
|
+
"description": "What it lets the agent do, for the person: each one's skills and tools together, described per language. Its id is what requires names; neutral ids (lms-grading) let skills depend on what can be done, not on which extension does it.",
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": { "$ref": "#/$defs/capability" }
|
|
19
|
+
},
|
|
15
20
|
"requires": { "description": "Capabilities another enabled extension must provide.", "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
16
|
-
"
|
|
21
|
+
"requirements": {
|
|
22
|
+
"description": "Programs it needs on the computer, which the person installs: agent-kit checks them, says what's missing and how to install it, and never installs them. Without one the whole extension needs, it's off; without one only some capabilities need (for), those are.",
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": { "$ref": "#/$defs/requirement" }
|
|
25
|
+
},
|
|
17
26
|
"readOnlyTools": {
|
|
18
27
|
"description": "Its tools that only read (short names): plan mode lets them through. Without it, plan mode asks its servers (readOnlyHint).",
|
|
19
28
|
"type": "array",
|
|
@@ -54,6 +63,55 @@
|
|
|
54
63
|
}
|
|
55
64
|
},
|
|
56
65
|
"$defs": {
|
|
66
|
+
"requirement": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"additionalProperties": false,
|
|
69
|
+
"required": ["name", "program", "install"],
|
|
70
|
+
"properties": {
|
|
71
|
+
"name": { "description": "What the person reads: Docker.", "type": "string", "minLength": 1 },
|
|
72
|
+
"program": { "description": "Its bare name, found on the PATH (on Windows with PATHEXT's extensions): never a path.", "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]*$" },
|
|
73
|
+
"version": {
|
|
74
|
+
"description": "How to check its version: run with these arguments only (options or version, no shell), the first x.y.z it prints compared with min.",
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["args", "min"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"args": { "type": "array", "items": { "type": "string", "pattern": "^(--?[A-Za-z][A-Za-z0-9-]*|version)$" } },
|
|
80
|
+
"min": { "type": "string", "pattern": "^\\d+(\\.\\d+){0,2}$" }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"install": {
|
|
84
|
+
"description": "How to install it on each system: a command, or a link (url, the fallback).",
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"minProperties": 1,
|
|
88
|
+
"properties": { "windows": { "type": "string", "minLength": 1 }, "macos": { "type": "string", "minLength": 1 }, "linux": { "type": "string", "minLength": 1 }, "url": { "type": "string", "pattern": "^https?://" } }
|
|
89
|
+
},
|
|
90
|
+
"for": { "description": "The capabilities (ids it provides) that need it; without it, the whole extension does.", "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"capability": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"required": ["id", "texts"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"id": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" },
|
|
99
|
+
"skills": { "description": "Its skills, by their folder in the extension's skills/.", "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
100
|
+
"tools": { "description": "Its tools, by short name (of any of its servers).", "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
101
|
+
"texts": {
|
|
102
|
+
"description": "Its label and description per language (en, es, fr, de); English is shown where its language is missing.",
|
|
103
|
+
"type": "object",
|
|
104
|
+
"propertyNames": { "enum": ["en", "es", "fr", "de"] },
|
|
105
|
+
"minProperties": 1,
|
|
106
|
+
"additionalProperties": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"additionalProperties": false,
|
|
109
|
+
"required": ["label"],
|
|
110
|
+
"properties": { "label": { "type": "string", "minLength": 1 }, "description": { "type": "string" } }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
57
115
|
"image": {
|
|
58
116
|
"type": "string",
|
|
59
117
|
"pattern": "^(?!/)(?![A-Za-z]:)(?!.*(?:^|[\\\\/])\\.\\.(?:[\\\\/]|$)).+\\.(?:svg|png|jpe?g|webp)$"
|
|
@@ -56,6 +56,16 @@
|
|
|
56
56
|
"agentKit": { "description": "After the agent's own command (<agent> …).", "type": "string" }
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
|
+
"capabilities": {
|
|
60
|
+
"description": "What it lets the agent do: its metadata[\"agent-kit\"].capabilities as it declares them, texts in every language it gives.",
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "$ref": "https://falkenslab.github.io/agent-kit/schemas/agent-kit-metadata.schema.json#/$defs/capability" }
|
|
63
|
+
},
|
|
64
|
+
"requirements": {
|
|
65
|
+
"description": "The programs it needs on the computer: its metadata[\"agent-kit\"].requirements as it declares them.",
|
|
66
|
+
"type": "array",
|
|
67
|
+
"items": { "$ref": "https://falkenslab.github.io/agent-kit/schemas/agent-kit-metadata.schema.json#/$defs/requirement" }
|
|
68
|
+
},
|
|
59
69
|
"logo": { "description": "A URL anyone can load.", "$ref": "#/$defs/url" },
|
|
60
70
|
"banner": { "description": "A URL anyone can load.", "$ref": "#/$defs/url" }
|
|
61
71
|
}
|