@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.
- package/README.md +1 -4
- package/assets/extension-launcher.mjs +28 -5
- package/dist/chat/chatController.d.ts +60 -0
- package/dist/chat/chatController.js +212 -2
- package/dist/chat/extensions.d.ts +36 -2
- package/dist/chat/extensions.js +87 -3
- package/dist/chat/runs.d.ts +3 -1
- package/dist/chat/runs.js +1 -1
- package/dist/core/agentSpec.d.ts +11 -0
- package/dist/core/elicitation.d.ts +54 -0
- package/dist/core/elicitation.js +120 -0
- package/dist/core/extensionCatalog.d.ts +103 -0
- package/dist/core/extensionCatalog.js +235 -0
- package/dist/core/extensionOptions.d.ts +56 -0
- package/dist/core/extensionOptions.js +139 -0
- package/dist/core/extensionRequirements.d.ts +66 -0
- package/dist/core/extensionRequirements.js +112 -0
- package/dist/core/extensions.d.ts +75 -2
- package/dist/core/extensions.js +78 -6
- package/dist/core/externalExtensions.d.ts +101 -11
- package/dist/core/externalExtensions.js +263 -51
- package/dist/core/hooks/planGate.d.ts +2 -0
- package/dist/core/hooks/planGate.js +3 -0
- package/dist/core/interaction.d.ts +7 -0
- package/dist/core/liveExtensions.d.ts +15 -0
- package/dist/core/liveExtensions.js +33 -2
- package/dist/core/marketplaces.d.ts +7 -2
- package/dist/core/marketplaces.js +6 -4
- package/dist/core/messages/de.js +37 -1
- package/dist/core/messages/en.d.ts +44 -0
- package/dist/core/messages/en.js +37 -1
- package/dist/core/messages/es.js +37 -1
- package/dist/core/messages/fr.js +37 -1
- package/dist/core/npxPackages.d.ts +48 -0
- package/dist/core/npxPackages.js +121 -0
- package/dist/core/pluginDownloads.d.ts +2 -1
- package/dist/core/pluginDownloads.js +2 -2
- package/dist/core/readOnlyHints.d.ts +13 -0
- package/dist/core/readOnlyHints.js +26 -0
- package/dist/core/runner.js +2 -0
- package/dist/core/session.d.ts +17 -5
- package/dist/core/session.js +44 -11
- package/dist/core/sessionFacts.d.ts +11 -1
- package/dist/core/workspaceTrust.d.ts +12 -0
- package/dist/core/workspaceTrust.js +60 -0
- package/dist/extensions/awareness/tools.js +6 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +7 -2
- package/dist/tui/extensionCommand.d.ts +10 -2
- package/dist/tui/extensionCommand.js +199 -34
- package/extensions/awareness/.claude-plugin/plugin.json +13 -5
- package/extensions/knowledge/.claude-plugin/plugin.json +13 -5
- package/extensions/memory/.claude-plugin/plugin.json +13 -5
- package/extensions/sources/.claude-plugin/plugin.json +13 -5
- package/package.json +7 -5
- package/schemas/agent-kit-metadata.schema.json +120 -0
- package/schemas/marketplace-catalog.schema.json +74 -0
package/dist/core/session.d.ts
CHANGED
|
@@ -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
|
package/dist/core/session.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
})
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
/**
|
|
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>;
|