@falkenslab/agent-kit 0.21.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.
Files changed (57) hide show
  1. package/README.md +1 -4
  2. package/assets/extension-launcher.mjs +28 -5
  3. package/dist/chat/chatController.d.ts +60 -0
  4. package/dist/chat/chatController.js +212 -2
  5. package/dist/chat/extensions.d.ts +36 -2
  6. package/dist/chat/extensions.js +87 -3
  7. package/dist/chat/runs.d.ts +3 -1
  8. package/dist/chat/runs.js +1 -1
  9. package/dist/core/agentSpec.d.ts +11 -0
  10. package/dist/core/elicitation.d.ts +54 -0
  11. package/dist/core/elicitation.js +120 -0
  12. package/dist/core/extensionCatalog.d.ts +103 -0
  13. package/dist/core/extensionCatalog.js +235 -0
  14. package/dist/core/extensionOptions.d.ts +56 -0
  15. package/dist/core/extensionOptions.js +139 -0
  16. package/dist/core/extensionRequirements.d.ts +66 -0
  17. package/dist/core/extensionRequirements.js +112 -0
  18. package/dist/core/extensions.d.ts +75 -2
  19. package/dist/core/extensions.js +78 -6
  20. package/dist/core/externalExtensions.d.ts +101 -11
  21. package/dist/core/externalExtensions.js +263 -51
  22. package/dist/core/hooks/planGate.d.ts +2 -0
  23. package/dist/core/hooks/planGate.js +3 -0
  24. package/dist/core/interaction.d.ts +7 -0
  25. package/dist/core/liveExtensions.d.ts +15 -0
  26. package/dist/core/liveExtensions.js +33 -2
  27. package/dist/core/marketplaces.d.ts +7 -2
  28. package/dist/core/marketplaces.js +6 -4
  29. package/dist/core/messages/de.js +37 -1
  30. package/dist/core/messages/en.d.ts +44 -0
  31. package/dist/core/messages/en.js +37 -1
  32. package/dist/core/messages/es.js +37 -1
  33. package/dist/core/messages/fr.js +37 -1
  34. package/dist/core/npxPackages.d.ts +48 -0
  35. package/dist/core/npxPackages.js +121 -0
  36. package/dist/core/pluginDownloads.d.ts +2 -1
  37. package/dist/core/pluginDownloads.js +2 -2
  38. package/dist/core/readOnlyHints.d.ts +13 -0
  39. package/dist/core/readOnlyHints.js +26 -0
  40. package/dist/core/runner.js +2 -0
  41. package/dist/core/session.d.ts +17 -5
  42. package/dist/core/session.js +44 -11
  43. package/dist/core/sessionFacts.d.ts +11 -1
  44. package/dist/core/workspaceTrust.d.ts +12 -0
  45. package/dist/core/workspaceTrust.js +60 -0
  46. package/dist/extensions/awareness/tools.js +6 -2
  47. package/dist/index.d.ts +9 -2
  48. package/dist/index.js +7 -2
  49. package/dist/tui/extensionCommand.d.ts +10 -2
  50. package/dist/tui/extensionCommand.js +199 -34
  51. package/extensions/awareness/.claude-plugin/plugin.json +13 -5
  52. package/extensions/knowledge/.claude-plugin/plugin.json +13 -5
  53. package/extensions/memory/.claude-plugin/plugin.json +13 -5
  54. package/extensions/sources/.claude-plugin/plugin.json +13 -5
  55. package/package.json +7 -5
  56. package/schemas/agent-kit-metadata.schema.json +120 -0
  57. package/schemas/marketplace-catalog.schema.json +74 -0
@@ -1,12 +1,13 @@
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";
7
8
  import { type Language } from "./language.js";
8
9
  import { type RunFolder } from "./runs.js";
9
- import { type ExtensionSwitch } from "./liveExtensions.js";
10
+ import { type ExtensionRestart, type ExtensionSwitch } from "./liveExtensions.js";
10
11
  /**
11
12
  * Builds the `options` object passed to the Agent SDK's `query()` — everything about
12
13
  * *how* the agent runs a session (system prompt, tools, MCP servers, hooks), independent
@@ -44,23 +45,34 @@ export declare function buildSessionOptions<TConfig extends BaseSessionConfig>(c
44
45
  apis: Record<string, Record<string, unknown>>;
45
46
  /** Turns an installed extension off or on in the running session; false when that takes opening it again (#49). */
46
47
  switchExtension: ExtensionSwitch;
48
+ /** Restarts an installed extension's servers in the running session, with its options as they are now (#55). */
49
+ restartExtension: ExtensionRestart;
47
50
  }>;
48
51
  export { createModeControl, togglePlanMode, type ModeControl } from "./modeControl.js";
49
52
  /** The extensions of a session: where they're installed, what's installed, what runs and what can't (with why). */
50
53
  export interface ExtensionsStatus {
51
54
  dirs: ExtensionDirs;
55
+ /** The agent's own marketplace (`officialMarketplace`), for the chats' catalog. */
56
+ official?: string;
57
+ /** The topics the chats' catalog shows (`catalogTopics`); none, every extension. */
58
+ catalogTopics?: string[];
59
+ /** The workspace's extensions that don't run until the person trusts them there (#57): the chats ask. */
60
+ untrusted: {
61
+ name: string;
62
+ version?: string;
63
+ source: string;
64
+ }[];
52
65
  installed: InstalledExtension[];
53
66
  /** The names of the extensions this session runs with, the kit's and the agent's too. */
54
67
  active: string[];
55
68
  /** What each active one is, from its manifest: for a view that lists them. */
56
- about: Record<string, {
57
- description: string;
58
- provides: string[];
59
- }>;
69
+ about: Record<string, AboutExtension>;
60
70
  inactive: {
61
71
  name: string;
62
72
  reason: string;
63
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[];
64
76
  }
65
77
  /**
66
78
  * The CLI's folder for a project, where it keeps each session's transcript and large tool
@@ -13,16 +13,19 @@ 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";
20
- import { listInstalled, loadExternalExtensions } from "./externalExtensions.js";
20
+ import { listInstalled, loadExternalExtensions, scopePrivateDir } from "./externalExtensions.js";
21
21
  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 { createExtensionSwitch, mountPlugin } from "./liveExtensions.js";
25
+ import { createExtensionRestart, createExtensionSwitch, mountPlugin } from "./liveExtensions.js";
26
+ import { secretsDir } from "./extensionOptions.js";
27
+ import { createElicitationHandler } from "./elicitation.js";
28
+ import { createReadOnlyHints } from "./readOnlyHints.js";
26
29
  import { agentKitVersion } from "./version.js";
27
30
  /**
28
31
  * Builds the `options` object passed to the Agent SDK's `query()` — everything about
@@ -127,7 +130,11 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
127
130
  // `requires` a capability no active extension provides is left out; since the SDK's
128
131
  // `skillOverrides` doesn't reach plugin skills (confirmed empirically), leaving one out takes
129
132
  // a list: the plugins' skills and the project's, without it.
130
- const missingCapabilities = await skillsMissingCapabilities(pluginRoots, resolved.capabilities);
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
+ ];
131
138
  const requested = spec.skills === "plugins" ? await pluginSkills(pluginRoots) : spec.skills;
132
139
  const offeredSkills = missingCapabilities.length && (requested === undefined || requested === "all")
133
140
  ? [...(await pluginSkills(pluginRoots)), ...((spec.settingSources ?? ["project"]).includes("project") ? await projectSkills(config.projectDir) : [])]
@@ -170,8 +177,18 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
170
177
  const allowedSubagentTypes = [...new Set([...(subagents?.allowedSubagentTypes ?? []), ...Object.keys(fromPlugins)])];
171
178
  // The extensions' read-only tools, for the plan gate: a list the live switch changes (liveExtensions.ts).
172
179
  const readOnlyTools = fromExtensions((contribution) => contribution.readOnlyTools);
180
+ // Those whose manifest lists none: their servers say, asked while the session runs (#53).
181
+ const readOnlyHintServers = fromExtensions((contribution) => contribution.readOnlyHintServers);
173
182
  // What no file tool may touch: the agent's denied paths and the SDK's own credentials.
174
- const deniedPaths = [...(config.deniedPaths ?? []), path.join(claudeConfigDir(), ".credentials.json")];
183
+ // And the installed extensions' sensitive option values (#55), in each scope's .secrets/.
184
+ const deniedPaths = [
185
+ ...(config.deniedPaths ?? []),
186
+ path.join(claudeConfigDir(), ".credentials.json"),
187
+ ...["agent", "project"].flatMap((scope) => {
188
+ const privateDir = config.extensionDirs ? scopePrivateDir(config.extensionDirs, scope) : undefined;
189
+ return privateDir ? [secretsDir(privateDir)] : [];
190
+ }),
191
+ ];
175
192
  const transcriptPath = path.join(runDir, "transcript.jsonl");
176
193
  const transcriptLogger = createTranscriptLogger(transcriptPath, config.secrets ?? []);
177
194
  const promptSections = [
@@ -269,6 +286,9 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
269
286
  // The date and date arithmetic, in every session and mode: only reads (tools/time.ts).
270
287
  time: createTimeServer(config.timeZone),
271
288
  },
289
+ // A server asking the person for something mid-tool (MCP elicitation, #58): through the
290
+ // interaction port, never the model; declined when no one can be asked (autonomous).
291
+ onElicitation: createElicitationHandler(runDir, () => modeControl.mode !== "autonomous"),
272
292
  hooks: {
273
293
  PreToolUse: [
274
294
  { hooks: [transcriptLogger.preToolUse] },
@@ -303,6 +323,7 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
303
323
  createPlanGate({
304
324
  projectDir: config.projectDir,
305
325
  readOnlyTools,
326
+ ...(readOnlyHintServers.length ? { isReadOnlyHinted: createReadOnlyHints(readOnlyHintServers, () => late.view?.controls().mcpServerStatus) } : {}),
306
327
  ...(spec.planMode?.isPlanFile ? { isPlanFile: (filePath) => spec.planMode.isPlanFile(filePath, config) } : {}),
307
328
  ...(spec.planMode?.isReadOnlyTool ? { isReadOnlyTool: spec.planMode.isReadOnlyTool } : {}),
308
329
  }, () => modeControl.mode === "plan"),
@@ -330,9 +351,13 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
330
351
  const toolLabels = Object.assign({}, ...added.map((contribution) => contribution.toolLabels ?? {}));
331
352
  const extensions = {
332
353
  dirs: extensionDirs,
354
+ ...(config.officialMarketplace ? { official: config.officialMarketplace } : {}),
355
+ ...(config.catalogTopics?.length ? { catalogTopics: config.catalogTopics } : {}),
356
+ untrusted: external.untrusted.map((installed) => ({ name: installed.name, ...(installed.manifest?.version ? { version: installed.manifest.version } : {}), source: installed.lock.marketplace ?? installed.lock.source })),
333
357
  installed: await listInstalled(extensionDirs),
334
358
  active: resolved.active.map(({ extension }) => extension.name),
335
- about: Object.fromEntries(resolved.active.map(({ extension, manifest }) => [extension.name, { description: manifest.description, provides: manifest.provides }])),
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 ?? [],
336
361
  inactive: resolved.inactive,
337
362
  };
338
363
  // What the session is, for the extensions that ask (sessionFacts.ts, #43).
@@ -341,10 +366,12 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
341
366
  kitVersion: agentKitVersion(),
342
367
  language,
343
368
  extensions: {
344
- active: resolved.active.map(({ extension, manifest }, index) => ({
369
+ active: resolved.active.map(({ extension, manifest, unavailable }, index) => ({
345
370
  name: extension.name,
346
371
  description: manifest.description,
347
372
  provides: manifest.provides,
373
+ capabilities: manifest.capabilities.map((capability) => describeCapability(capability, "en")),
374
+ unavailable: unavailable.map(({ capability, reason }) => ({ ...describeCapability(capability, "en"), reason })),
348
375
  servers: Object.keys(added[index]?.mcpServers ?? {}),
349
376
  help: added[index]?.helpLines ?? [],
350
377
  })),
@@ -357,8 +384,9 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
357
384
  };
358
385
  late.view = createSessionView(viewBase, modeControl);
359
386
  registerSessionView(sdkOptions, late.view);
360
- // Turning an installed extension off and on while the session runs (#49).
361
- const switchExtension = createExtensionSwitch(resolved.active.flatMap(({ extension, manifest }, index) => {
387
+ // Turning an installed extension off and on while the session runs (#49), and restarting its
388
+ // servers when its options change (#55).
389
+ const liveExtensions = resolved.active.flatMap(({ extension, manifest, unavailable }, index) => {
362
390
  const installed = installedDirs.get(extension.name);
363
391
  if (!installed)
364
392
  return [];
@@ -375,12 +403,17 @@ export async function buildSessionOptions(config, runDir, spec, options = {}) {
375
403
  agents: Object.keys(fromPlugins).filter((agent) => agent.startsWith(`${extension.name}:`)),
376
404
  description: manifest.description,
377
405
  provides: manifest.provides,
406
+ capabilities: manifest.capabilities,
407
+ unavailable,
378
408
  help: contribution?.helpLines ?? [],
379
409
  },
380
410
  ];
381
- }), { readOnlyTools, allowedSubagentTypes, toolLabels, status: extensions, facts: viewBase, controls: () => late.view?.controls() ?? {} });
411
+ });
412
+ const liveTargets = { readOnlyTools, allowedSubagentTypes, toolLabels, status: extensions, facts: viewBase, controls: () => late.view?.controls() ?? {} };
413
+ const switchExtension = createExtensionSwitch(liveExtensions, liveTargets);
414
+ const restartExtension = createExtensionRestart(liveExtensions, liveTargets);
382
415
  const apis = Object.fromEntries(resolved.active.flatMap(({ extension }, index) => (added[index]?.api ? [[extension.name, added[index].api]] : [])));
383
- return { options: sdkOptions, transcriptLogger, transcriptPath, modeControl, language, toolLabels, extensions, apis, switchExtension };
416
+ return { options: sdkOptions, transcriptLogger, transcriptPath, modeControl, language, toolLabels, extensions, apis, switchExtension, restartExtension };
384
417
  }
385
418
  export { createModeControl, togglePlanMode } from "./modeControl.js";
386
419
  /** The Claude Code configuration folder the SDK's CLI uses: `CLAUDE_CONFIG_DIR`, or `~/.claude`. */
@@ -1,5 +1,6 @@
1
- import type { Options, SlashCommand } from "@anthropic-ai/claude-agent-sdk";
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
  }[];
@@ -61,6 +67,10 @@ export interface SessionLive {
61
67
  /** The running session's controls an installed extension is turned off and on with (liveExtensions.ts). */
62
68
  toggleMcpServer?: (server: string, enabled: boolean) => Promise<void>;
63
69
  reloadPlugins?: () => Promise<unknown>;
70
+ /** Restarts one of its MCP servers: an installed extension's, when its options change (#55). */
71
+ reconnectMcpServer?: (server: string) => Promise<void>;
72
+ /** Its MCP servers as they are now: their tools and what each says of itself (plan mode's read-only hints). */
73
+ mcpServerStatus?: () => Promise<McpServerStatus[]>;
64
74
  }
65
75
  /** The facts known when the options are built: everything but the mode and what only the running session knows. */
66
76
  export type StaticSessionFacts = Omit<SessionFacts, "mode" | "switchableModes" | "tools" | "commands" | "context" | "extensions"> & {
@@ -0,0 +1,12 @@
1
+ import { type ExtensionDirs, type LockEntry } from "./externalExtensions.js";
2
+ /** What's trusted of an extension: its code, its files' hash and its npx packages'. */
3
+ export declare function codeHash(lock: LockEntry): string;
4
+ /** Whether an extension of a project's scope is trusted in that workspace, with the code its lock has now. */
5
+ export declare function isTrusted(agentDir: string, projectScope: string, name: string, lock: LockEntry): Promise<boolean>;
6
+ /** Trusts these extensions of a project's scope, with the code their lock has now. */
7
+ export declare function trust(agentDir: string, projectScope: string, entries: Record<string, LockEntry>): Promise<void>;
8
+ /**
9
+ * Trusts a workspace's extensions (its project scope's, all or `names`) with the code its lock
10
+ * has now: what the person does when the chat asks, or with `extension trust`. The names trusted.
11
+ */
12
+ export declare function trustWorkspace(dirs: ExtensionDirs, names?: readonly string[]): Promise<string[]>;
@@ -0,0 +1,60 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { readLock } from "./externalExtensions.js";
5
+ /**
6
+ * Trusting a workspace's extensions (#57): a project's scope comes with the project, from whoever
7
+ * shared it (a clone, a download), and its extensions' servers run code on this computer. They
8
+ * run only once the person trusts them in that workspace: what's trusted is each one's code (its
9
+ * files' SHA-256 and its npx packages'), kept in the agent's folder, outside the project, so a
10
+ * project can't trust itself. Another version (a changed lock, an update from someone else) asks
11
+ * again; turning one on or off, or its options, don't. What the person installs into the
12
+ * project's scope themselves is trusted as they install it. The agent's own scope is the
13
+ * person's: it never asks.
14
+ */
15
+ const TRUST_FILE = ".trust.json";
16
+ /** What's trusted of an extension: its code, its files' hash and its npx packages'. */
17
+ export function codeHash(lock) {
18
+ const packages = Object.entries(lock.packages ?? {})
19
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
20
+ .map(([spec, cached]) => `${spec}=${cached.sha256}`);
21
+ // A linked one has no hash of its own: its folder is what's trusted.
22
+ return createHash("sha256").update([lock.linked ? `linked:${path.resolve(lock.source)}` : lock.sha256, ...packages].join("\n")).digest("hex");
23
+ }
24
+ async function readTrust(agentDir) {
25
+ try {
26
+ return JSON.parse(await readFile(path.join(agentDir, TRUST_FILE), "utf8"));
27
+ }
28
+ catch (error) {
29
+ if (error.code === "ENOENT")
30
+ return {};
31
+ throw error;
32
+ }
33
+ }
34
+ /** The workspace a project scope is, as the trust file names it. */
35
+ const workspaceKey = (projectScope) => path.resolve(projectScope);
36
+ /** Whether an extension of a project's scope is trusted in that workspace, with the code its lock has now. */
37
+ export async function isTrusted(agentDir, projectScope, name, lock) {
38
+ return (await readTrust(agentDir))[workspaceKey(projectScope)]?.[name] === codeHash(lock);
39
+ }
40
+ /** Trusts these extensions of a project's scope, with the code their lock has now. */
41
+ export async function trust(agentDir, projectScope, entries) {
42
+ const file = await readTrust(agentDir);
43
+ const workspace = (file[workspaceKey(projectScope)] ??= {});
44
+ for (const [name, lock] of Object.entries(entries))
45
+ workspace[name] = codeHash(lock);
46
+ await mkdir(agentDir, { recursive: true });
47
+ await writeFile(path.join(agentDir, TRUST_FILE), `${JSON.stringify(file, null, 2)}\n`, "utf8");
48
+ }
49
+ /**
50
+ * Trusts a workspace's extensions (its project scope's, all or `names`) with the code its lock
51
+ * has now: what the person does when the chat asks, or with `extension trust`. The names trusted.
52
+ */
53
+ export async function trustWorkspace(dirs, names) {
54
+ if (!dirs.agent || !dirs.project)
55
+ return [];
56
+ const lock = await readLock(dirs.project);
57
+ const entries = Object.fromEntries(Object.entries(lock).filter(([name]) => !names || names.includes(name)));
58
+ await trust(dirs.agent, dirs.project, entries);
59
+ return Object.keys(entries);
60
+ }
@@ -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
- return `- **${name}**: ${description}${toolList}${help.map((line) => `\n ${line}`).join("")}`;
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,9 +15,15 @@ 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";
19
- export { addExtension, extensionDataDir, removeExtension, setExtensionEnabled, listInstalled, readExternalManifest, type ExtensionAuthor, type ExtensionDirs, type ExtensionScope, type ExternalManifest, type ExternalToolLabel, type PluginServer, type InstalledExtension, type LockEntry, } from "./core/externalExtensions.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";
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";
21
+ export type { ExtensionOption, OptionValue } from "./core/extensionOptions.js";
22
+ export type { ExtensionRestart, ExtensionSwitch } from "./core/liveExtensions.js";
23
+ export { packageCacheDir, type CachedPackage } from "./core/npxPackages.js";
20
24
  export { addMarketplace, findPlugin, inspectMarketplace, installFromMarketplace, listMarketplaces, readMarketplaceManifest, removeMarketplace, resolvePluginSource, updateMarketplace, type KnownMarketplace, type MarketplaceManifest, type MarketplacePlugin, type PluginSource, } from "./core/marketplaces.js";
25
+ export { extensionCatalog, installExtension, restoreExtensions, updateExtension, type CatalogEntry } from "./core/extensionCatalog.js";
26
+ export { trustWorkspace } from "./core/workspaceTrust.js";
21
27
  export { runExtensionCommand } from "./tui/extensionCommand.js";
22
28
  export { agentKitVersion } from "./core/version.js";
23
29
  export { BUILT_IN_PAGE_TYPES, isSlug, parseId, linkedIds, type KnowledgeStore, type PageType, type KnowledgePage, type PageInfo, type SearchHit, type CheckReport, type FieldChanges, type NewPage, } from "./extensions/knowledge/knowledgeStore.js";
@@ -37,6 +43,7 @@ export { createFileScopeGate, checkFileScope, type FileScope } from "./core/hook
37
43
  export { createPlanGate, checkPlanScope, type PlanScope } from "./core/hooks/planGate.js";
38
44
  export { askForDecision, askForManualIntervention, askForChoice, askForText, type ChoiceAnswer } from "./core/hooks/humanInput.js";
39
45
  export { setInteractionPort, getInteractionPort, type InteractionPort, type ApprovalPrompt, type ChoiceSettings } from "./core/interaction.js";
46
+ export { formFields, type FormAnswer, type FormField, type FormRequest } from "./core/elicitation.js";
40
47
  export { terminalInteractionPort, setSharedReadline, getSharedReadline } from "./tui/terminalInteraction.js";
41
48
  export { createHumanApprovalServer, DEFAULT_HUMAN_APPROVAL_TEXTS, type HumanApprovalTexts } from "./core/tools/humanApproval.js";
42
49
  export { createManualLoginServer, type ManualInterventionTexts } from "./core/tools/manualLogin.js";
package/dist/index.js CHANGED
@@ -15,9 +15,13 @@ 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";
19
- export { addExtension, extensionDataDir, removeExtension, setExtensionEnabled, listInstalled, readExternalManifest, } from "./core/externalExtensions.js";
18
+ export { describeCapability, readExtensionManifest, } from "./core/extensions.js";
19
+ export { checkRequirements } from "./core/extensionRequirements.js";
20
+ export { addExtension, codeStoreDir, installTarget, scopePrivateDir, extensionDataDir, extensionOptionStates, setExtensionOptions, removeExtension, setExtensionEnabled, listInstalled, readExternalManifest, } from "./core/externalExtensions.js";
21
+ export { packageCacheDir } from "./core/npxPackages.js";
20
22
  export { addMarketplace, findPlugin, inspectMarketplace, installFromMarketplace, listMarketplaces, readMarketplaceManifest, removeMarketplace, resolvePluginSource, updateMarketplace, } from "./core/marketplaces.js";
23
+ export { extensionCatalog, installExtension, restoreExtensions, updateExtension } from "./core/extensionCatalog.js";
24
+ export { trustWorkspace } from "./core/workspaceTrust.js";
21
25
  export { runExtensionCommand } from "./tui/extensionCommand.js";
22
26
  export { agentKitVersion } from "./core/version.js";
23
27
  export { BUILT_IN_PAGE_TYPES, isSlug, parseId, linkedIds, } from "./extensions/knowledge/knowledgeStore.js";
@@ -38,6 +42,7 @@ export { createFileScopeGate, checkFileScope } from "./core/hooks/fileScopeGate.
38
42
  export { createPlanGate, checkPlanScope } from "./core/hooks/planGate.js";
39
43
  export { askForDecision, askForManualIntervention, askForChoice, askForText } from "./core/hooks/humanInput.js";
40
44
  export { setInteractionPort, getInteractionPort } from "./core/interaction.js";
45
+ export { formFields } from "./core/elicitation.js";
41
46
  export { terminalInteractionPort, setSharedReadline, getSharedReadline } from "./tui/terminalInteraction.js";
42
47
  // The checkpoints answer on the terminal by default, as they always have; a non-terminal
43
48
  // host replaces the port with its own (or null, to answer through the response file only).
@@ -8,10 +8,14 @@ export { chatExtensionsCommand, describe } from "../chat/extensions.js";
8
8
  *
9
9
  * - `extension list`
10
10
  * - `extension info <name>`: its metadata, what it offers and where its README is
11
- * - `extension add <folder | git URL[#ref] | plugin[@marketplace]> [--project] [--path <subfolder>] [--yes]`
11
+ * - `extension add <folder | git URL[#ref] | plugin[@marketplace]> [--project] [--path <subfolder>] [--set key=value…] [--yes]`: asks for its options (#55) unless `--set` gives them
12
12
  * - `extension add <folder> --link [--project]`: registered where it is, to develop it (not copied, its files not checked)
13
13
  * - `extension remove <name> [--project | --agent] [--yes | --keep-data]`: asks before deleting its data folder
14
14
  * - `extension enable|disable <name> [--project | --agent]`
15
+ * - `extension config <name> [key=value… | key… | --unset key…] [--project | --agent]`: its options; `key` alone asks for it (without echo when it's a secret)
16
+ * - `extension update [name] [--yes]`: one installed from a marketplace (or all that have a newer version there) to the version it offers now, its data and options kept
17
+ * - `extension restore [--trust]`: gets the code a workspace's lock lists and this computer lacks (#57); `--trust` trusts it too
18
+ * - `extension trust [name…]`: lets a workspace's extensions run, as their code is now
15
19
  * - `extension search [words]`: what the known marketplaces offer
16
20
  * - `extension marketplace add <folder | git URL[#ref] | owner/repo> [--yes]`, `list`, `update [name]`, `remove <name>`
17
21
  *
@@ -27,6 +31,10 @@ export declare function runExtensionCommand(argv: readonly string[], options: {
27
31
  write?: (line: string) => void;
28
32
  /** The agent's official marketplace: a folder or a git URL (`#ref`). */
29
33
  official?: string;
30
- /** Asks the person (a confirmation); by default on the terminal, and nothing without one. */
34
+ /** The topics of the extensions `search` shows (#59), as the agent's `catalogTopics`; without it, every one. */
35
+ catalogTopics?: string[];
36
+ /** Asks the person (a confirmation, an option's value); by default on the terminal, and nothing without one. */
31
37
  ask?: (question: string) => Promise<string>;
38
+ /** Asks for a secret (a sensitive option), without echo; by default on the terminal. */
39
+ askSecret?: (question: string) => Promise<string>;
32
40
  }): Promise<boolean>;