@falkenslab/agent-kit 0.18.0 → 0.19.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/assets/extension-launcher.mjs +24 -0
- package/dist/chat/chatController.d.ts +264 -0
- package/dist/chat/chatController.js +510 -0
- package/dist/chat/extensions.d.ts +14 -0
- package/dist/chat/extensions.js +41 -0
- package/dist/chat/history.d.ts +17 -0
- package/dist/chat/history.js +39 -0
- package/dist/{tui → chat}/runs.d.ts +8 -1
- package/dist/{tui → chat}/runs.js +1 -1
- package/dist/core/agentSpec.d.ts +30 -16
- package/dist/core/extensions.d.ts +119 -0
- package/dist/core/extensions.js +97 -0
- package/dist/core/externalExtensions.d.ts +131 -0
- package/dist/core/externalExtensions.js +340 -0
- package/dist/core/hooks/planGate.d.ts +2 -0
- package/dist/core/hooks/planGate.js +1 -10
- package/dist/core/hooks/stepGate.d.ts +1 -1
- package/dist/core/hooks/stepGate.js +3 -3
- package/dist/core/messages/de.js +11 -32
- package/dist/core/messages/en.d.ts +13 -16
- package/dist/core/messages/en.js +11 -32
- package/dist/core/messages/es.js +11 -32
- package/dist/core/messages/fr.js +11 -32
- package/dist/core/messages/index.d.ts +6 -0
- package/dist/core/messages/index.js +12 -1
- package/dist/core/packaged.d.ts +10 -0
- package/dist/core/packaged.js +38 -0
- package/dist/core/pluginAgents.d.ts +20 -0
- package/dist/core/pluginAgents.js +76 -0
- package/dist/core/runner.js +16 -9
- package/dist/core/session.d.ts +30 -5
- package/dist/core/session.js +143 -94
- package/dist/core/sessionFacts.d.ts +80 -0
- package/dist/core/sessionFacts.js +48 -0
- package/dist/core/toolLabels.d.ts +24 -6
- package/dist/core/toolLabels.js +24 -39
- package/dist/extensions/awareness/index.d.ts +13 -0
- package/dist/extensions/awareness/index.js +39 -0
- package/dist/extensions/awareness/labels.d.ts +3 -0
- package/dist/extensions/awareness/labels.js +12 -0
- package/dist/extensions/awareness/tools.d.ts +5 -0
- package/dist/extensions/awareness/tools.js +54 -0
- package/dist/extensions/knowledge/index.d.ts +8 -0
- package/dist/extensions/knowledge/index.js +35 -0
- package/dist/{core → extensions/knowledge}/knowledgeStore.d.ts +1 -0
- package/dist/extensions/knowledge/labels.d.ts +3 -0
- package/dist/extensions/knowledge/labels.js +119 -0
- package/dist/{core/knowledge.d.ts → extensions/knowledge/prompt.d.ts} +1 -1
- package/dist/{core/knowledge.js → extensions/knowledge/prompt.js} +7 -4
- package/dist/{core/tools/knowledgeTools.d.ts → extensions/knowledge/tools.d.ts} +1 -1
- package/dist/{core/tools/knowledgeTools.js → extensions/knowledge/tools.js} +2 -2
- package/dist/extensions/memory/index.d.ts +12 -0
- package/dist/extensions/memory/index.js +56 -0
- package/dist/extensions/memory/labels.d.ts +3 -0
- package/dist/extensions/memory/labels.js +58 -0
- package/dist/extensions/memory/memoryStore.d.ts +53 -0
- package/dist/extensions/memory/memoryStore.js +88 -0
- package/dist/extensions/memory/tools.d.ts +9 -0
- package/dist/extensions/memory/tools.js +82 -0
- package/dist/extensions/sources/index.d.ts +9 -0
- package/dist/extensions/sources/index.js +40 -0
- package/dist/extensions/sources/labels.d.ts +3 -0
- package/dist/extensions/sources/labels.js +82 -0
- package/dist/{core/tools/saveToSources.d.ts → extensions/sources/tools.d.ts} +1 -1
- package/dist/{core/tools/saveToSources.js → extensions/sources/tools.js} +5 -5
- package/dist/index.d.ts +14 -9
- package/dist/index.js +11 -7
- package/dist/tui/chatTui.d.ts +11 -20
- package/dist/tui/chatTui.js +75 -230
- package/dist/tui/extensionCommand.d.ts +21 -0
- package/dist/tui/extensionCommand.js +133 -0
- package/dist/tui/ink/runChatInk.d.ts +4 -3
- package/dist/tui/ink/runChatInk.js +101 -217
- package/extensions/awareness/.claude-plugin/plugin.json +10 -0
- package/{assets/agent-help → extensions/awareness/skills/help}/SKILL.md +4 -3
- package/{assets/knowledge-plugin → extensions/knowledge}/.claude-plugin/plugin.json +7 -1
- package/extensions/memory/.claude-plugin/plugin.json +10 -0
- package/extensions/memory/commands/forget.md +8 -0
- package/extensions/memory/commands/list.md +5 -0
- package/extensions/sources/.claude-plugin/plugin.json +10 -0
- package/package.json +3 -2
- package/dist/core/agentHelp.d.ts +0 -30
- package/dist/core/agentHelp.js +0 -55
- /package/dist/{core → extensions/knowledge}/fileKnowledgeStore.d.ts +0 -0
- /package/dist/{core → extensions/knowledge}/fileKnowledgeStore.js +0 -0
- /package/dist/{core → extensions/knowledge}/knowledgeStore.js +0 -0
- /package/dist/{core → extensions/sources}/extractText.d.ts +0 -0
- /package/dist/{core → extensions/sources}/extractText.js +0 -0
- /package/dist/{core → extensions/sources}/sources.d.ts +0 -0
- /package/dist/{core → extensions/sources}/sources.js +0 -0
- /package/{assets/knowledge-plugin → extensions/knowledge}/commands/ingest.md +0 -0
- /package/{assets/knowledge-plugin → extensions/knowledge}/commands/lint.md +0 -0
- /package/{assets/knowledge-plugin → extensions/knowledge}/commands/query.md +0 -0
- /package/{assets/knowledge-plugin → extensions/knowledge}/skills/knowledge-ingest/SKILL.md +0 -0
- /package/{assets/knowledge-plugin → extensions/knowledge}/skills/knowledge-lint/SKILL.md +0 -0
- /package/{assets/knowledge-plugin → extensions/knowledge}/skills/knowledge-query/SKILL.md +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { cp, mkdtemp, readdir, readFile, rm, stat, writeFile, mkdir } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
import { getLanguage } from "./messages/index.js";
|
|
9
|
+
import { truncate } from "./toolLabels.js";
|
|
10
|
+
import { agentKitVersion } from "./version.js";
|
|
11
|
+
import { outsideArchive } from "./packaged.js";
|
|
12
|
+
export const LOCK_FILE = "extensions.lock.json";
|
|
13
|
+
/** A scope's lock: its extensions by name. */
|
|
14
|
+
export async function readLock(scopeDir) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(await readFile(path.join(scopeDir, LOCK_FILE), "utf8"));
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error.code === "ENOENT")
|
|
20
|
+
return {};
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function writeLock(scopeDir, lock) {
|
|
25
|
+
await mkdir(scopeDir, { recursive: true });
|
|
26
|
+
const sorted = Object.fromEntries(Object.keys(lock).sort().map((name) => [name, lock[name]]));
|
|
27
|
+
await writeFile(path.join(scopeDir, LOCK_FILE), `${JSON.stringify(sorted, null, 2)}\n`, "utf8");
|
|
28
|
+
}
|
|
29
|
+
/** Reads an extension's manifest, checking what the kit needs from it. */
|
|
30
|
+
export async function readExternalManifest(dir) {
|
|
31
|
+
const file = path.join(dir, ".claude-plugin", "plugin.json");
|
|
32
|
+
let raw;
|
|
33
|
+
try {
|
|
34
|
+
raw = JSON.parse(await readFile(file, "utf8"));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error(`${dir} isn't an extension: it has no readable .claude-plugin/plugin.json.`);
|
|
38
|
+
}
|
|
39
|
+
const name = raw.name;
|
|
40
|
+
if (typeof name !== "string" || !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name))
|
|
41
|
+
throw new Error(`${file}: "name" must be kebab-case.`);
|
|
42
|
+
const kit = (raw["agent-kit"] ?? {});
|
|
43
|
+
const text = (key) => (typeof raw[key] === "string" ? { [key]: raw[key] } : {});
|
|
44
|
+
const author = raw.author;
|
|
45
|
+
// `repository` may be a URL or `{ url }`, as in package.json.
|
|
46
|
+
const repository = typeof raw.repository === "string" ? raw.repository : raw.repository?.url;
|
|
47
|
+
return {
|
|
48
|
+
name,
|
|
49
|
+
description: typeof raw.description === "string" ? raw.description : "",
|
|
50
|
+
...text("version"),
|
|
51
|
+
...(author && typeof author === "object" && typeof author.name === "string"
|
|
52
|
+
? { author: { name: author.name, ...(author.email ? { email: author.email } : {}), ...(author.url ? { url: author.url } : {}) } }
|
|
53
|
+
: {}),
|
|
54
|
+
...text("homepage"),
|
|
55
|
+
...(typeof repository === "string" ? { repository } : {}),
|
|
56
|
+
...text("license"),
|
|
57
|
+
...(Array.isArray(raw.keywords) ? { keywords: raw.keywords.filter((keyword) => typeof keyword === "string") } : {}),
|
|
58
|
+
provides: kit.provides ?? [],
|
|
59
|
+
requires: kit.requires ?? [],
|
|
60
|
+
...(kit.kit ? { kit: kit.kit } : {}),
|
|
61
|
+
servers: await pluginServers(dir, raw),
|
|
62
|
+
readOnlyTools: kit.readOnlyTools ?? [],
|
|
63
|
+
labels: kit.labels ?? {},
|
|
64
|
+
...(kit.help ? { help: kit.help } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A plugin's MCP servers, where Claude Code looks for them: `mcpServers` in `plugin.json` (inline,
|
|
69
|
+
* or a path to a JSON file), else `.mcp.json` at its root, with or without the `mcpServers` key.
|
|
70
|
+
*/
|
|
71
|
+
async function pluginServers(dir, raw) {
|
|
72
|
+
const read = async (file) => {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(await readFile(path.join(dir, file), "utf8"));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error.code === "ENOENT")
|
|
78
|
+
return undefined;
|
|
79
|
+
throw new Error(`${file} can't be read as JSON.`, { cause: error });
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
let declared = raw.mcpServers;
|
|
83
|
+
if (typeof declared === "string")
|
|
84
|
+
declared = await read(declared);
|
|
85
|
+
if (declared === undefined)
|
|
86
|
+
declared = await read(".mcp.json");
|
|
87
|
+
if (declared && typeof declared === "object" && "mcpServers" in declared)
|
|
88
|
+
declared = declared.mcpServers;
|
|
89
|
+
if (declared === undefined || declared === null)
|
|
90
|
+
return {};
|
|
91
|
+
if (typeof declared !== "object")
|
|
92
|
+
throw new Error("its MCP servers aren't a JSON object.");
|
|
93
|
+
const servers = {};
|
|
94
|
+
for (const [name, server] of Object.entries(declared)) {
|
|
95
|
+
if (!server || typeof server.command !== "string")
|
|
96
|
+
throw new Error(`its MCP server "${name}" has no command.`);
|
|
97
|
+
servers[name] = { command: server.command, ...(Array.isArray(server.args) ? { args: server.args.map(String) } : {}), ...(server.env ? { env: server.env } : {}) };
|
|
98
|
+
}
|
|
99
|
+
return servers;
|
|
100
|
+
}
|
|
101
|
+
/** `${CLAUDE_PLUGIN_ROOT}` (the extension's folder) and `${VAR}` (the agent's variable) in a server's values. */
|
|
102
|
+
function expand(value, root) {
|
|
103
|
+
return value.replace(/\$\{(\w+)\}/g, (_, name) => (name === "CLAUDE_PLUGIN_ROOT" ? root : (process.env[name] ?? "")));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Why one of an extension's servers can't run through the launcher (only Node, its script
|
|
107
|
+
* first, inside the extension), or how the kit starts it.
|
|
108
|
+
*/
|
|
109
|
+
function serverLaunch(name, server, root) {
|
|
110
|
+
if (!/^node(\.exe)?$/i.test(path.basename(server.command)))
|
|
111
|
+
return `runs its server "${name}" with "${server.command}": only Node servers can be installed`;
|
|
112
|
+
const [script, ...args] = (server.args ?? []).map((arg) => expand(arg, root));
|
|
113
|
+
if (!script)
|
|
114
|
+
return `gives its server "${name}" no script to run`;
|
|
115
|
+
const entry = path.resolve(root, script);
|
|
116
|
+
if (path.relative(root, entry).startsWith(".."))
|
|
117
|
+
return `runs its server "${name}" from outside its folder (${script})`;
|
|
118
|
+
return { entry, args, env: Object.fromEntries(Object.entries(server.env ?? {}).map(([key, value]) => [key, expand(String(value), root)])) };
|
|
119
|
+
}
|
|
120
|
+
/** Why an extension's servers can't run, or nothing: each must be Node, with its script there. */
|
|
121
|
+
async function serversProblem(manifest, root) {
|
|
122
|
+
for (const [name, server] of Object.entries(manifest.servers)) {
|
|
123
|
+
const launch = serverLaunch(name, server, root);
|
|
124
|
+
if (typeof launch === "string")
|
|
125
|
+
return launch;
|
|
126
|
+
if (!(await stat(launch.entry).catch(() => null))?.isFile())
|
|
127
|
+
return `has no script at ${path.relative(root, launch.entry)} for its server "${name}"`;
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
/** SHA-256 over an extension's files (their paths and contents, in order), `.git` aside. */
|
|
132
|
+
export async function hashExtension(dir) {
|
|
133
|
+
const hash = createHash("sha256");
|
|
134
|
+
async function walk(relative) {
|
|
135
|
+
const entries = (await readdir(path.join(dir, relative), { withFileTypes: true })).filter((entry) => entry.name !== ".git");
|
|
136
|
+
for (const entry of entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
137
|
+
const child = relative ? `${relative}/${entry.name}` : entry.name;
|
|
138
|
+
if (entry.isDirectory())
|
|
139
|
+
await walk(child);
|
|
140
|
+
else if (entry.isFile())
|
|
141
|
+
hash.update(`${child}\0`).update(await readFile(path.join(dir, child))).update("\0");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
await walk("");
|
|
145
|
+
return hash.digest("hex");
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Whether `version` (x.y.z) is in `range`: comparators separated by spaces, all of which must
|
|
149
|
+
* hold (`>=0.19.0 <0.21.0`, `=0.19.2`, `0.19.2`).
|
|
150
|
+
*/
|
|
151
|
+
export function inKitRange(version, range) {
|
|
152
|
+
const parse = (text) => text.split("-")[0].split(".").map((part) => Number(part) || 0);
|
|
153
|
+
const compare = (a, b) => {
|
|
154
|
+
for (let i = 0; i < 3; i++)
|
|
155
|
+
if ((a[i] ?? 0) !== (b[i] ?? 0))
|
|
156
|
+
return (a[i] ?? 0) - (b[i] ?? 0);
|
|
157
|
+
return 0;
|
|
158
|
+
};
|
|
159
|
+
const current = parse(version);
|
|
160
|
+
return range
|
|
161
|
+
.trim()
|
|
162
|
+
.split(/\s+/)
|
|
163
|
+
.every((comparator) => {
|
|
164
|
+
const match = /^(>=|<=|>|<|=)?\s*v?(\d+(?:\.\d+){0,2})$/.exec(comparator);
|
|
165
|
+
if (!match)
|
|
166
|
+
return false;
|
|
167
|
+
const order = compare(current, parse(match[2]));
|
|
168
|
+
switch (match[1]) {
|
|
169
|
+
case ">=":
|
|
170
|
+
return order >= 0;
|
|
171
|
+
case "<=":
|
|
172
|
+
return order <= 0;
|
|
173
|
+
case ">":
|
|
174
|
+
return order > 0;
|
|
175
|
+
case "<":
|
|
176
|
+
return order < 0;
|
|
177
|
+
default:
|
|
178
|
+
return order === 0;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/** Every extension installed in the scopes, the project's first. */
|
|
183
|
+
export async function listInstalled(dirs) {
|
|
184
|
+
const installed = [];
|
|
185
|
+
for (const scope of ["project", "agent"]) {
|
|
186
|
+
const scopeDir = dirs[scope];
|
|
187
|
+
if (!scopeDir)
|
|
188
|
+
continue;
|
|
189
|
+
for (const [name, lock] of Object.entries(await readLock(scopeDir))) {
|
|
190
|
+
const dir = path.join(scopeDir, name);
|
|
191
|
+
const manifest = await readExternalManifest(dir).catch(() => undefined);
|
|
192
|
+
installed.push({ name, scope, dir, lock, shadowed: installed.some((other) => other.name === name), ...(manifest ? { manifest } : {}) });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return installed;
|
|
196
|
+
}
|
|
197
|
+
/** The launcher that starts an external extension's server with a clean environment. */
|
|
198
|
+
export function extensionLauncherPath() {
|
|
199
|
+
// Node runs it as another process: outside the archive when packaged (#42).
|
|
200
|
+
return outsideArchive(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "extension-launcher.mjs"));
|
|
201
|
+
}
|
|
202
|
+
/** Its tools' chat labels in the kit's language, from its manifest: `{field}` takes the input; for each of its servers. */
|
|
203
|
+
function manifestToolLabels(manifest) {
|
|
204
|
+
const language = getLanguage();
|
|
205
|
+
const fill = (template, input) => template.replace(/\{(\w+)\}/g, (_, field) => (input[field] === undefined ? "" : truncate(String(input[field]), 60)));
|
|
206
|
+
return Object.fromEntries(Object.entries(manifest.labels).flatMap(([tool, byLanguage]) => {
|
|
207
|
+
const texts = byLanguage[language] ?? byLanguage.en;
|
|
208
|
+
if (!texts)
|
|
209
|
+
return [];
|
|
210
|
+
const label = { label: (input) => fill(texts.label, input), ...(texts.phrase ? { phrase: texts.phrase } : {}) };
|
|
211
|
+
return Object.keys(manifest.servers).map((server) => [`mcp__${server}__${tool}`, label]);
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
/** The extension an installed one makes: its servers started through the launcher, its rules from its manifest. */
|
|
215
|
+
function externalExtension(installed, manifest) {
|
|
216
|
+
return {
|
|
217
|
+
name: manifest.name,
|
|
218
|
+
plugin: installed.dir,
|
|
219
|
+
external: true,
|
|
220
|
+
async contribute() {
|
|
221
|
+
const servers = {};
|
|
222
|
+
for (const [name, server] of Object.entries(manifest.servers)) {
|
|
223
|
+
const launch = serverLaunch(name, server, installed.dir);
|
|
224
|
+
if (typeof launch === "string")
|
|
225
|
+
continue; // checked when it loaded
|
|
226
|
+
// Its `env` reaches it (the SDK merges it with the agent's); the launcher removes the rest.
|
|
227
|
+
// Inside Electron, process.execPath is the app itself: ELECTRON_RUN_AS_NODE makes it run
|
|
228
|
+
// the launcher as Node (the launcher removes it, with the rest, once it runs).
|
|
229
|
+
const env = process.versions.electron ? { ...launch.env, ELECTRON_RUN_AS_NODE: "1" } : launch.env;
|
|
230
|
+
servers[name] = { type: "stdio", command: process.execPath, args: [extensionLauncherPath(), launch.entry, JSON.stringify(Object.keys(launch.env)), ...launch.args], env };
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
mcpServers: servers,
|
|
234
|
+
readOnlyTools: Object.keys(manifest.servers).flatMap((server) => manifest.readOnlyTools.map((tool) => `mcp__${server}__${tool}`)),
|
|
235
|
+
toolLabels: manifestToolLabels(manifest),
|
|
236
|
+
...(manifest.help ? { helpLines: [manifest.help] } : {}),
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** The installed extensions a session runs with, and those it can't (with why). Disabled ones are neither. */
|
|
242
|
+
export async function loadExternalExtensions(dirs) {
|
|
243
|
+
const extensions = [];
|
|
244
|
+
const off = [];
|
|
245
|
+
for (const installed of await listInstalled(dirs)) {
|
|
246
|
+
if (installed.shadowed || !installed.lock.enabled)
|
|
247
|
+
continue;
|
|
248
|
+
const reason = await problem(installed);
|
|
249
|
+
if (typeof reason === "string")
|
|
250
|
+
off.push({ name: installed.name, reason });
|
|
251
|
+
else
|
|
252
|
+
extensions.push(externalExtension(installed, reason));
|
|
253
|
+
}
|
|
254
|
+
return { extensions, off };
|
|
255
|
+
}
|
|
256
|
+
/** Why an installed extension can't load, or its manifest when it can. */
|
|
257
|
+
async function problem(installed) {
|
|
258
|
+
let manifest;
|
|
259
|
+
try {
|
|
260
|
+
if ((await hashExtension(installed.dir)) !== installed.lock.sha256)
|
|
261
|
+
return "has files that changed since it was installed: install it again to trust them";
|
|
262
|
+
manifest = await readExternalManifest(installed.dir);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
return `can't be read (${error instanceof Error ? error.message : String(error)})`;
|
|
266
|
+
}
|
|
267
|
+
if (manifest.name !== installed.name)
|
|
268
|
+
return `is installed as "${installed.name}" but its manifest says "${manifest.name}"`;
|
|
269
|
+
if (manifest.kit && !inKitRange(agentKitVersion(), manifest.kit))
|
|
270
|
+
return `works with agent-kit ${manifest.kit}, and this agent runs ${agentKitVersion()}`;
|
|
271
|
+
return (await serversProblem(manifest, installed.dir)) ?? manifest;
|
|
272
|
+
}
|
|
273
|
+
const exec = promisify(execFile);
|
|
274
|
+
/** Whether `source` names a git repository rather than a folder. */
|
|
275
|
+
export function isGitSource(source) {
|
|
276
|
+
return /^(https?:\/\/|git@|ssh:\/\/|git:\/\/|file:\/\/|github:)/.test(source) || /\.git(#.*)?$/.test(source);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Installs an extension into a scope from a folder or a git repository (`url#ref`, and `subdir`
|
|
280
|
+
* for one inside it): copies its files (never runs a script), checks its manifest, and locks it,
|
|
281
|
+
* enabled. An extension with the same name in that scope is replaced.
|
|
282
|
+
*/
|
|
283
|
+
export async function addExtension(source, scopeDir, options = {}) {
|
|
284
|
+
let from = path.resolve(source);
|
|
285
|
+
let commit;
|
|
286
|
+
let clone;
|
|
287
|
+
if (isGitSource(source)) {
|
|
288
|
+
const [url, ref] = source.replace(/^github:/, "https://github.com/").split("#");
|
|
289
|
+
clone = await mkdtemp(path.join(os.tmpdir(), "agent-kit-extension-"));
|
|
290
|
+
await exec("git", ["clone", "--depth", "1", ...(ref ? ["--branch", ref] : []), url, clone]);
|
|
291
|
+
commit = (await exec("git", ["-C", clone, "rev-parse", "HEAD"])).stdout.trim();
|
|
292
|
+
from = clone;
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
if (options.subdir)
|
|
296
|
+
from = path.join(from, options.subdir);
|
|
297
|
+
const manifest = await readExternalManifest(from);
|
|
298
|
+
const servers = await serversProblem(manifest, from);
|
|
299
|
+
if (servers)
|
|
300
|
+
throw new Error(`${manifest.name} can't be installed: it ${servers}.`);
|
|
301
|
+
const target = path.join(scopeDir, manifest.name);
|
|
302
|
+
const lock = await readLock(scopeDir);
|
|
303
|
+
const replaced = Boolean(lock[manifest.name]);
|
|
304
|
+
await rm(target, { recursive: true, force: true });
|
|
305
|
+
await mkdir(scopeDir, { recursive: true });
|
|
306
|
+
await cp(from, target, { recursive: true, filter: (file) => path.basename(file) !== ".git" });
|
|
307
|
+
lock[manifest.name] = {
|
|
308
|
+
source: isGitSource(source) ? `${source}${options.subdir ? ` (${options.subdir})` : ""}` : from,
|
|
309
|
+
...(commit ? { commit } : {}),
|
|
310
|
+
sha256: await hashExtension(target),
|
|
311
|
+
enabled: true,
|
|
312
|
+
installed: new Date().toISOString(),
|
|
313
|
+
};
|
|
314
|
+
await writeLock(scopeDir, lock);
|
|
315
|
+
return { name: manifest.name, replaced, ...(commit ? { commit } : {}) };
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
if (clone)
|
|
319
|
+
await rm(clone, { recursive: true, force: true });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/** Removes an extension from a scope (its files and its lock entry); false when it wasn't there. */
|
|
323
|
+
export async function removeExtension(scopeDir, name) {
|
|
324
|
+
const lock = await readLock(scopeDir);
|
|
325
|
+
if (!lock[name])
|
|
326
|
+
return false;
|
|
327
|
+
delete lock[name];
|
|
328
|
+
await rm(path.join(scopeDir, name), { recursive: true, force: true });
|
|
329
|
+
await writeLock(scopeDir, lock);
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
/** Enables or disables an installed extension in a scope; false when it isn't there. */
|
|
333
|
+
export async function setExtensionEnabled(scopeDir, name, enabled) {
|
|
334
|
+
const lock = await readLock(scopeDir);
|
|
335
|
+
if (!lock[name])
|
|
336
|
+
return false;
|
|
337
|
+
lock[name] = { ...lock[name], enabled };
|
|
338
|
+
await writeLock(scopeDir, lock);
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
@@ -7,6 +7,8 @@ export interface PlanScope {
|
|
|
7
7
|
isPlanFile?(filePath: string): boolean;
|
|
8
8
|
/** Whether one of the agent's own MCP tools only reads. */
|
|
9
9
|
isReadOnlyTool?(toolName: string, input: Record<string, unknown>): boolean;
|
|
10
|
+
/** The enabled extensions' MCP tools that only read (`mcp__<server>__<tool>`). */
|
|
11
|
+
readOnlyTools?: readonly string[];
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* The reason to deny `toolName` with `input` in plan mode, or `undefined` to let it through.
|
|
@@ -3,15 +3,6 @@ import path from "node:path";
|
|
|
3
3
|
const READING_TOOLS = new Set(["Read", "Glob", "Grep", "WebFetch", "WebSearch", "Skill", "Agent", "TodoWrite"]);
|
|
4
4
|
/** The kit's own MCP servers whose tools only ask a human (approvals, manual intervention) or only read (the date and time). */
|
|
5
5
|
const ASKING_SERVERS = ["mcp__approvals__", "mcp__manualLogin__", "mcp__time__"];
|
|
6
|
-
/** The kit's own MCP tools that only read, on servers that also have tools that write. */
|
|
7
|
-
const READING_KIT_TOOLS = new Set([
|
|
8
|
-
"mcp__sourceFiles__list_sources",
|
|
9
|
-
"mcp__sourceFiles__extract_text",
|
|
10
|
-
"mcp__knowledge__knowledge_index",
|
|
11
|
-
"mcp__knowledge__knowledge_search",
|
|
12
|
-
"mcp__knowledge__knowledge_read",
|
|
13
|
-
"mcp__knowledge__knowledge_check",
|
|
14
|
-
]);
|
|
15
6
|
/** Why a call is denied in plan mode, and what the model should do instead. */
|
|
16
7
|
const denial = "Plan mode: nothing can be changed until the user leaves plan mode. Keep reading and researching, then present the plan; the user switches out of plan mode when they want it carried out.";
|
|
17
8
|
/**
|
|
@@ -22,7 +13,7 @@ const denial = "Plan mode: nothing can be changed until the user leaves plan mod
|
|
|
22
13
|
export function checkPlanScope(scope, toolName, input) {
|
|
23
14
|
if (READING_TOOLS.has(toolName))
|
|
24
15
|
return undefined;
|
|
25
|
-
if (ASKING_SERVERS.some((prefix) => toolName.startsWith(prefix)) ||
|
|
16
|
+
if (ASKING_SERVERS.some((prefix) => toolName.startsWith(prefix)) || scope.readOnlyTools?.includes(toolName))
|
|
26
17
|
return undefined;
|
|
27
18
|
if (toolName === "Write" || toolName === "Edit") {
|
|
28
19
|
const target = typeof input.file_path === "string" && input.file_path !== "" ? path.resolve(scope.projectDir, input.file_path) : undefined;
|
|
@@ -8,4 +8,4 @@ import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
|
8
8
|
* session.ts's `ModeControl`): the hook stays registered and, while inactive, gives no
|
|
9
9
|
* decision at all, so the tool call goes on exactly as if the hook weren't there.
|
|
10
10
|
*/
|
|
11
|
-
export declare function createStepGate(runDir: string, isActive?: () => boolean): HookCallback;
|
|
11
|
+
export declare function createStepGate(runDir: string, isActive?: () => boolean, selfAskingTools?: readonly string[]): HookCallback;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { askForDecision } from "./humanInput.js";
|
|
2
2
|
import { t } from "../messages/index.js";
|
|
3
3
|
const SELF_ASKING_SERVERS = ["mcp__approvals__", "mcp__manualLogin__"];
|
|
4
|
-
const SELF_ASKING_TOOLS = new Set(["
|
|
4
|
+
const SELF_ASKING_TOOLS = new Set(["TodoWrite"]);
|
|
5
5
|
/**
|
|
6
6
|
* PreToolUse hook for "interactive" mode: pauses before every action and asks for
|
|
7
7
|
* confirmation (by keyboard or by file, see humanInput.ts), like reviewing a plan step
|
|
@@ -11,14 +11,14 @@ const SELF_ASKING_TOOLS = new Set(["mcp__sourceFiles__request_file", "mcp__sourc
|
|
|
11
11
|
* session.ts's `ModeControl`): the hook stays registered and, while inactive, gives no
|
|
12
12
|
* decision at all, so the tool call goes on exactly as if the hook weren't there.
|
|
13
13
|
*/
|
|
14
|
-
export function createStepGate(runDir, isActive = () => true) {
|
|
14
|
+
export function createStepGate(runDir, isActive = () => true, selfAskingTools = []) {
|
|
15
15
|
return async (input) => {
|
|
16
16
|
if (!isActive())
|
|
17
17
|
return {};
|
|
18
18
|
const pre = input;
|
|
19
19
|
// A tool that asks the person itself (an approval, a question, the plan, a file, retiring
|
|
20
20
|
// a source) or only keeps the task list isn't asked about first: that would be asking to ask.
|
|
21
|
-
if (SELF_ASKING_SERVERS.some((prefix) => pre.tool_name.startsWith(prefix)) || SELF_ASKING_TOOLS.has(pre.tool_name))
|
|
21
|
+
if (SELF_ASKING_SERVERS.some((prefix) => pre.tool_name.startsWith(prefix)) || SELF_ASKING_TOOLS.has(pre.tool_name) || selfAskingTools.includes(pre.tool_name))
|
|
22
22
|
return {};
|
|
23
23
|
const answer = await askForDecision(runDir, {
|
|
24
24
|
title: t().proposedAction,
|
package/dist/core/messages/de.js
CHANGED
|
@@ -22,6 +22,17 @@ export const de = {
|
|
|
22
22
|
resumeHint: "↑/↓ wählen · Enter fortsetzen · Esc abbrechen",
|
|
23
23
|
resumeQuestion: "Nummer des Gesprächs, das fortgesetzt werden soll (Enter zum Abbrechen): ",
|
|
24
24
|
noEarlierRuns: "(keine früheren Gespräche zum Fortsetzen)",
|
|
25
|
+
commandResume: "Ein früheres Gespräch wählen und fortsetzen",
|
|
26
|
+
commandPlan: "In den Planmodus und zurück",
|
|
27
|
+
commandExtensions: "Was läuft, was aus ist und warum; eine ein- oder ausschalten",
|
|
28
|
+
extensionsRunning: (names) => `Aktiv: ${names}`,
|
|
29
|
+
extensionsNone: "(keine Erweiterungen)",
|
|
30
|
+
extensionOff: (name, reason) => `${name} ist aus: ${reason}`,
|
|
31
|
+
extensionsInstalled: "Installiert:",
|
|
32
|
+
extensionToggled: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}: die Sitzung wird mit derselben Unterhaltung neu geöffnet)`,
|
|
33
|
+
extensionNotInstalled: (name) => `(${name} ist nicht installiert: /extensions listet sie auf)`,
|
|
34
|
+
extensionsUsage: "(Verwendung: /extensions, /extensions enable <Name>, /extensions disable <Name>)",
|
|
35
|
+
extensionsCantReopen: "(dieser Chat kann seine Sitzung nicht neu öffnen: im Terminal aktivieren oder deaktivieren und neu starten)",
|
|
25
36
|
currentRun: "(dieses)",
|
|
26
37
|
resumed: (date) => `(Gespräch vom ${date} fortgesetzt)`,
|
|
27
38
|
suggestionKey: "Tab",
|
|
@@ -91,22 +102,6 @@ export const de = {
|
|
|
91
102
|
Task: ["hat {n} Subagenten gestartet", "hat {n} Subagenten gestartet"],
|
|
92
103
|
mcp__time__current_time: ["Uhrzeit abgefragt", "Uhrzeit {n}-mal abgefragt"],
|
|
93
104
|
mcp__time__date_math: ["Daten berechnet", "Daten {n}-mal berechnet"],
|
|
94
|
-
mcp__sourceFiles__list_sources: ["Quellen geprüft", "Quellen {n}-mal geprüft"],
|
|
95
|
-
mcp__sourceFiles__extract_text: ["{n} Dokument gelesen", "{n} Dokumente gelesen"],
|
|
96
|
-
mcp__sourceFiles__save_to_sources: ["{n} Quelle gespeichert", "{n} Quellen gespeichert"],
|
|
97
|
-
mcp__sourceFiles__download_to_sources: ["{n} Quelle heruntergeladen", "{n} Quellen heruntergeladen"],
|
|
98
|
-
mcp__sourceFiles__request_file: ["um {n} Datei gebeten", "um {n} Dateien gebeten"],
|
|
99
|
-
mcp__sourceFiles__retire_source: ["{n} Quelle zurückgezogen", "{n} Quellen zurückgezogen"],
|
|
100
|
-
mcp__knowledge__knowledge_index: ["Index gelesen", "Index {n}-mal gelesen"],
|
|
101
|
-
mcp__knowledge__knowledge_search: ["Wissensbasis durchsucht", "Wissensbasis {n}-mal durchsucht"],
|
|
102
|
-
mcp__knowledge__knowledge_read: ["{n} Seite gelesen", "{n} Seiten gelesen"],
|
|
103
|
-
mcp__knowledge__knowledge_create: ["{n} Seite angelegt", "{n} Seiten angelegt"],
|
|
104
|
-
mcp__knowledge__knowledge_edit: ["{n} Seite bearbeitet", "{n} Seiten bearbeitet"],
|
|
105
|
-
mcp__knowledge__knowledge_rewrite: ["{n} Seite neu geschrieben", "{n} Seiten neu geschrieben"],
|
|
106
|
-
mcp__knowledge__knowledge_supersede: ["{n} Seite ersetzt", "{n} Seiten ersetzt"],
|
|
107
|
-
mcp__knowledge__knowledge_retire: ["{n} Seite zurückgezogen", "{n} Seiten zurückgezogen"],
|
|
108
|
-
mcp__knowledge__knowledge_log: ["Protokoll aktualisiert", "Protokoll {n}-mal aktualisiert"],
|
|
109
|
-
mcp__knowledge__knowledge_check: ["Wissensbasis geprüft", "Wissensbasis {n}-mal geprüft"],
|
|
110
105
|
mcp__approvals__ask_human: ["{n} Frage gestellt", "{n} Fragen gestellt"],
|
|
111
106
|
mcp__approvals__present_plan: ["Plan vorgelegt", "Plan {n}-mal vorgelegt"],
|
|
112
107
|
},
|
|
@@ -120,7 +115,6 @@ export const de = {
|
|
|
120
115
|
labels: {
|
|
121
116
|
askingApproval: (summary) => `Bittet um menschliche Genehmigung: ${summary}`,
|
|
122
117
|
waitingManual: "Wartet auf einen manuellen Eingriff",
|
|
123
|
-
savingToSources: (destination) => `Speichert ${destination} in den Quellen`,
|
|
124
118
|
checkingTime: "Datum und Uhrzeit abfragen",
|
|
125
119
|
calculatingDates: "Daten berechnen",
|
|
126
120
|
reading: (file) => `Liest ${file}`,
|
|
@@ -139,21 +133,6 @@ export const de = {
|
|
|
139
133
|
applyingSkill: (skill) => `Wendet den Skill "${skill}" an`,
|
|
140
134
|
aSkill: "einen Skill",
|
|
141
135
|
updatingTasks: "Aufgabenliste aktualisieren",
|
|
142
|
-
listingSources: "Quellen prüfen",
|
|
143
|
-
extractingText: (source) => `${source} lesen`,
|
|
144
|
-
downloading: (url) => `${url} in die Quellen herunterladen`,
|
|
145
|
-
requestingFile: (description) => `Um eine Datei bitten: ${description}`,
|
|
146
|
-
retiringSource: (source) => `Quelle ${source} zurückziehen`,
|
|
147
|
-
knowledgeIndex: "Index der Wissensbasis lesen",
|
|
148
|
-
knowledgeSearch: (query) => `Wissensbasis nach "${query}" durchsuchen`,
|
|
149
|
-
knowledgeRead: (page) => `${page} lesen`,
|
|
150
|
-
knowledgeCreate: (page) => `${page} anlegen`,
|
|
151
|
-
knowledgeEdit: (page) => `${page} bearbeiten`,
|
|
152
|
-
knowledgeRewrite: (page) => `${page} neu schreiben`,
|
|
153
|
-
knowledgeSupersede: (page) => `${page} als ersetzt markieren`,
|
|
154
|
-
knowledgeRetire: (page) => `${page} zurückziehen`,
|
|
155
|
-
knowledgeLog: "Protokoll der Wissensbasis aktualisieren",
|
|
156
|
-
knowledgeCheck: "Wissensbasis prüfen",
|
|
157
136
|
askingHuman: (question) => `Frage: ${question}`,
|
|
158
137
|
presentingPlan: "Plan vorlegen",
|
|
159
138
|
},
|
|
@@ -29,6 +29,19 @@ export interface Messages {
|
|
|
29
29
|
resumeHint: string;
|
|
30
30
|
resumeQuestion: string;
|
|
31
31
|
noEarlierRuns: string;
|
|
32
|
+
/** What the chat's own commands do, for a view that lists them. */
|
|
33
|
+
commandResume: string;
|
|
34
|
+
commandPlan: string;
|
|
35
|
+
commandExtensions: string;
|
|
36
|
+
/** /extensions (#37): what the session runs with, what's off and why, and enabling or disabling one. */
|
|
37
|
+
extensionsRunning(names: string): string;
|
|
38
|
+
extensionsNone: string;
|
|
39
|
+
extensionOff(name: string, reason: string): string;
|
|
40
|
+
extensionsInstalled: string;
|
|
41
|
+
extensionToggled(name: string, enabled: boolean): string;
|
|
42
|
+
extensionNotInstalled(name: string): string;
|
|
43
|
+
extensionsUsage: string;
|
|
44
|
+
extensionsCantReopen: string;
|
|
32
45
|
currentRun: string;
|
|
33
46
|
resumed(date: string): string;
|
|
34
47
|
suggestionKey: string;
|
|
@@ -82,7 +95,6 @@ export interface Messages {
|
|
|
82
95
|
labels: {
|
|
83
96
|
askingApproval(summary: string): string;
|
|
84
97
|
waitingManual: string;
|
|
85
|
-
savingToSources(destination: string): string;
|
|
86
98
|
checkingTime: string;
|
|
87
99
|
calculatingDates: string;
|
|
88
100
|
reading(file: string): string;
|
|
@@ -101,21 +113,6 @@ export interface Messages {
|
|
|
101
113
|
applyingSkill(skill: string): string;
|
|
102
114
|
aSkill: string;
|
|
103
115
|
updatingTasks: string;
|
|
104
|
-
listingSources: string;
|
|
105
|
-
extractingText(source: string): string;
|
|
106
|
-
downloading(url: string): string;
|
|
107
|
-
requestingFile(description: string): string;
|
|
108
|
-
retiringSource(source: string): string;
|
|
109
|
-
knowledgeIndex: string;
|
|
110
|
-
knowledgeSearch(query: string): string;
|
|
111
|
-
knowledgeRead(page: string): string;
|
|
112
|
-
knowledgeCreate(page: string): string;
|
|
113
|
-
knowledgeEdit(page: string): string;
|
|
114
|
-
knowledgeRewrite(page: string): string;
|
|
115
|
-
knowledgeSupersede(page: string): string;
|
|
116
|
-
knowledgeRetire(page: string): string;
|
|
117
|
-
knowledgeLog: string;
|
|
118
|
-
knowledgeCheck: string;
|
|
119
116
|
askingHuman(question: string): string;
|
|
120
117
|
presentingPlan: string;
|
|
121
118
|
};
|
package/dist/core/messages/en.js
CHANGED
|
@@ -21,6 +21,17 @@ export const en = {
|
|
|
21
21
|
resumeHint: "↑/↓ choose · Enter resume · Esc cancel",
|
|
22
22
|
resumeQuestion: "Number of the conversation to resume (Enter to cancel): ",
|
|
23
23
|
noEarlierRuns: "(no earlier conversations to resume)",
|
|
24
|
+
commandResume: "Pick an earlier conversation to go on with",
|
|
25
|
+
commandPlan: "Into plan mode, or back",
|
|
26
|
+
commandExtensions: "What runs, what's off and why; turn one on or off",
|
|
27
|
+
extensionsRunning: (names) => `Running with: ${names}`,
|
|
28
|
+
extensionsNone: "(no extensions)",
|
|
29
|
+
extensionOff: (name, reason) => `${name} is off: it ${reason}`,
|
|
30
|
+
extensionsInstalled: "Installed:",
|
|
31
|
+
extensionToggled: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}: the session reopens, with the same conversation)`,
|
|
32
|
+
extensionNotInstalled: (name) => `(${name} isn't installed: /extensions lists them)`,
|
|
33
|
+
extensionsUsage: "(usage: /extensions, /extensions enable <name>, /extensions disable <name>)",
|
|
34
|
+
extensionsCantReopen: "(this chat can't reopen its session: enable or disable it from the terminal, and start again)",
|
|
24
35
|
currentRun: "(this one)",
|
|
25
36
|
resumed: (date) => `(resumed the conversation of ${date})`,
|
|
26
37
|
suggestionKey: "tab",
|
|
@@ -90,22 +101,6 @@ export const en = {
|
|
|
90
101
|
Task: ["ran {n} subagent", "ran {n} subagents"],
|
|
91
102
|
mcp__time__current_time: ["checked the time", "checked the time {n} times"],
|
|
92
103
|
mcp__time__date_math: ["calculated dates", "calculated dates {n} times"],
|
|
93
|
-
mcp__sourceFiles__list_sources: ["listed the sources", "listed the sources {n} times"],
|
|
94
|
-
mcp__sourceFiles__extract_text: ["read {n} document", "read {n} documents"],
|
|
95
|
-
mcp__sourceFiles__save_to_sources: ["saved {n} source", "saved {n} sources"],
|
|
96
|
-
mcp__sourceFiles__download_to_sources: ["downloaded {n} source", "downloaded {n} sources"],
|
|
97
|
-
mcp__sourceFiles__request_file: ["asked for {n} file", "asked for {n} files"],
|
|
98
|
-
mcp__sourceFiles__retire_source: ["retired {n} source", "retired {n} sources"],
|
|
99
|
-
mcp__knowledge__knowledge_index: ["read the index", "read the index {n} times"],
|
|
100
|
-
mcp__knowledge__knowledge_search: ["searched the knowledge base", "searched the knowledge base {n} times"],
|
|
101
|
-
mcp__knowledge__knowledge_read: ["read {n} page", "read {n} pages"],
|
|
102
|
-
mcp__knowledge__knowledge_create: ["created {n} page", "created {n} pages"],
|
|
103
|
-
mcp__knowledge__knowledge_edit: ["edited {n} page", "edited {n} pages"],
|
|
104
|
-
mcp__knowledge__knowledge_rewrite: ["rewrote {n} page", "rewrote {n} pages"],
|
|
105
|
-
mcp__knowledge__knowledge_supersede: ["superseded {n} page", "superseded {n} pages"],
|
|
106
|
-
mcp__knowledge__knowledge_retire: ["retired {n} page", "retired {n} pages"],
|
|
107
|
-
mcp__knowledge__knowledge_log: ["updated the log", "updated the log {n} times"],
|
|
108
|
-
mcp__knowledge__knowledge_check: ["checked the knowledge base", "checked the knowledge base {n} times"],
|
|
109
104
|
mcp__approvals__ask_human: ["asked {n} question", "asked {n} questions"],
|
|
110
105
|
mcp__approvals__present_plan: ["presented the plan", "presented the plan {n} times"],
|
|
111
106
|
},
|
|
@@ -119,7 +114,6 @@ export const en = {
|
|
|
119
114
|
labels: {
|
|
120
115
|
askingApproval: (summary) => `Asking for human approval: ${summary}`,
|
|
121
116
|
waitingManual: "Waiting for a human to intervene manually",
|
|
122
|
-
savingToSources: (destination) => `Saving ${destination} to the sources`,
|
|
123
117
|
checkingTime: "Checking the date and time",
|
|
124
118
|
calculatingDates: "Calculating dates",
|
|
125
119
|
reading: (file) => `Reading ${file}`,
|
|
@@ -138,21 +132,6 @@ export const en = {
|
|
|
138
132
|
applyingSkill: (skill) => `Applying the "${skill}" skill`,
|
|
139
133
|
aSkill: "a skill",
|
|
140
134
|
updatingTasks: "Updating the task list",
|
|
141
|
-
listingSources: "Listing the sources",
|
|
142
|
-
extractingText: (source) => `Reading ${source}`,
|
|
143
|
-
downloading: (url) => `Downloading ${url} to the sources`,
|
|
144
|
-
requestingFile: (description) => `Asking for a file: ${description}`,
|
|
145
|
-
retiringSource: (source) => `Retiring the source ${source}`,
|
|
146
|
-
knowledgeIndex: "Reading the knowledge base's index",
|
|
147
|
-
knowledgeSearch: (query) => `Searching the knowledge base for "${query}"`,
|
|
148
|
-
knowledgeRead: (page) => `Reading ${page}`,
|
|
149
|
-
knowledgeCreate: (page) => `Creating ${page}`,
|
|
150
|
-
knowledgeEdit: (page) => `Editing ${page}`,
|
|
151
|
-
knowledgeRewrite: (page) => `Rewriting ${page}`,
|
|
152
|
-
knowledgeSupersede: (page) => `Marking ${page} superseded`,
|
|
153
|
-
knowledgeRetire: (page) => `Retiring ${page}`,
|
|
154
|
-
knowledgeLog: "Updating the knowledge base's log",
|
|
155
|
-
knowledgeCheck: "Checking the knowledge base",
|
|
156
135
|
askingHuman: (question) => `Asking: ${question}`,
|
|
157
136
|
presentingPlan: "Presenting the plan",
|
|
158
137
|
},
|