@illuminis/comprism 0.1.5 → 0.1.7
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/out/agent/command.js +4 -4
- package/out/agent/render.d.ts +24 -0
- package/out/agent/render.js +35 -3
- package/out/agent/session.d.ts +6 -1
- package/out/agent/session.js +17 -8
- package/out/commands/agents.js +3 -3
- package/out/commands/ask.js +8 -8
- package/out/commands/codemap.d.ts +1 -1
- package/out/commands/codemap.js +3 -3
- package/out/commands/commands-thin.js +4 -4
- package/out/commands/config.js +1 -1
- package/out/commands/cost.js +4 -4
- package/out/commands/decisionEngine.d.ts +1 -0
- package/out/commands/decisionEngine.js +66 -0
- package/out/commands/hooks.js +1 -1
- package/out/commands/install.d.ts +1 -1
- package/out/commands/install.js +3 -2
- package/out/commands/instructions.js +1 -1
- package/out/commands/integrations.js +4 -4
- package/out/commands/keys.js +6 -6
- package/out/commands/login.js +37 -21
- package/out/commands/permissions.js +2 -2
- package/out/commands/plugins.js +3 -3
- package/out/commands/privacy.js +2 -2
- package/out/commands/repl.js +107 -83
- package/out/commands/review.js +6 -6
- package/out/commands/settings.js +30 -123
- package/out/commands/skills.js +2 -2
- package/out/commands/unattended.js +6 -6
- package/out/commands/update.js +1 -1
- package/out/commands/welcome.js +20 -29
- package/out/commands/worktrees.d.ts +1 -1
- package/out/graph/sync.d.ts +1 -1
- package/out/graph/sync.js +6 -6
- package/out/lib/attach.js +4 -3
- package/out/lib/commandlist.d.ts +1 -1
- package/out/lib/commandlist.js +2 -2
- package/out/lib/config.d.ts +3 -2
- package/out/lib/config.js +7 -8
- package/out/lib/connection.js +1 -1
- package/out/lib/gateway.d.ts +5 -5
- package/out/lib/machine.js +1 -1
- package/out/lib/project-ops.d.ts +1 -1
- package/out/lib/project-ops.js +4 -4
- package/out/lib/queue.js +1 -1
- package/out/lib/readiness.d.ts +3 -30
- package/out/lib/readiness.js +13 -82
- package/out/lib/servicecommand.d.ts +14 -0
- package/out/lib/servicecommand.js +74 -0
- package/out/lib/sessions.js +3 -3
- package/out/lib/ui.d.ts +9 -6
- package/out/lib/ui.js +20 -26
- package/out/lib/voice.d.ts +2 -39
- package/out/lib/voice.js +7 -148
- package/out/lib/words.d.ts +20 -0
- package/out/lib/words.js +27 -0
- package/out/machine/codemap/build.d.ts +45 -0
- package/out/machine/codemap/build.js +91 -0
- package/out/machine/codemap/facts.d.ts +47 -0
- package/out/machine/codemap/facts.js +12 -0
- package/out/machine/codemap/files.d.ts +45 -0
- package/out/machine/codemap/files.js +207 -0
- package/out/machine/codemap/read-locales.d.ts +29 -0
- package/out/machine/codemap/read-locales.js +246 -0
- package/out/machine/codemap/read-python.d.ts +11 -0
- package/out/machine/codemap/read-python.js +116 -0
- package/out/machine/codemap/read-typescript.d.ts +16 -0
- package/out/machine/codemap/read-typescript.js +292 -0
- package/out/machine/executor/browser.d.ts +14 -0
- package/out/machine/executor/browser.js +270 -0
- package/out/machine/executor/diagnostics.d.ts +2 -0
- package/out/machine/executor/diagnostics.js +181 -0
- package/out/machine/executor/documents.d.ts +40 -0
- package/out/machine/executor/documents.js +170 -0
- package/out/machine/executor/files.d.ts +2 -0
- package/out/machine/executor/files.js +590 -0
- package/out/machine/executor/git.d.ts +48 -0
- package/out/machine/executor/git.js +145 -0
- package/out/machine/executor/hooks.d.ts +51 -0
- package/out/machine/executor/hooks.js +154 -0
- package/out/machine/executor/index.d.ts +45 -0
- package/out/machine/executor/index.js +367 -0
- package/out/machine/executor/notebook.d.ts +2 -0
- package/out/machine/executor/notebook.js +147 -0
- package/out/machine/executor/paths.d.ts +20 -0
- package/out/machine/executor/paths.js +154 -0
- package/out/machine/executor/sandbox.d.ts +40 -0
- package/out/machine/executor/sandbox.js +299 -0
- package/out/machine/executor/shell.d.ts +86 -0
- package/out/machine/executor/shell.js +582 -0
- package/out/machine/executor/toolservers.d.ts +20 -0
- package/out/machine/executor/toolservers.js +189 -0
- package/out/machine/executor/worktree.d.ts +9 -0
- package/out/machine/executor/worktree.js +119 -0
- package/out/machine/folder/project.d.ts +28 -0
- package/out/machine/folder/project.js +114 -0
- package/out/machine/runtime/home.d.ts +2 -0
- package/out/machine/runtime/home.js +48 -0
- package/out/machine/runtime/needs.d.ts +30 -0
- package/out/machine/runtime/needs.js +89 -0
- package/out/machine/runtime/self.d.ts +23 -0
- package/out/machine/runtime/self.js +124 -0
- package/out/machine/voice/record.d.ts +38 -0
- package/out/machine/voice/record.js +208 -0
- package/out/providers/index.js +2 -1
- package/out/thin.js +18 -10
- package/package.json +4 -4
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.NativeExecutor = exports.NATIVE_CAPABILITIES = exports.guessTestCommand = exports.stopForeground = exports.stopEverything = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* The executor: one implementation of every action, for every native client.
|
|
39
|
+
*
|
|
40
|
+
* Specification: docs/modules/CODING_AGENT_BUILD_SPECIFICATION.md §7.1 and
|
|
41
|
+
* register item E2.
|
|
42
|
+
*
|
|
43
|
+
* The terminal runs this today. **The editor extension and the desktop app
|
|
44
|
+
* bundle this same file rather than writing their own**, and that is the point
|
|
45
|
+
* of it existing separately from the command line interface around it. Three
|
|
46
|
+
* implementations of "read a file" would be three different answers to what a
|
|
47
|
+
* file contains, and the one that differs is always the one nobody is looking at.
|
|
48
|
+
*
|
|
49
|
+
* It decides nothing. Whether an action is allowed, which model asked for it and
|
|
50
|
+
* what it cost are all the server's business. This performs and reports.
|
|
51
|
+
*/
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const files_1 = require("./files");
|
|
55
|
+
const documents_1 = require("./documents");
|
|
56
|
+
const hooks = __importStar(require("./hooks"));
|
|
57
|
+
const browser_1 = require("./browser");
|
|
58
|
+
const notebook_1 = require("./notebook");
|
|
59
|
+
const diagnostics_1 = require("./diagnostics");
|
|
60
|
+
const worktree_1 = require("./worktree");
|
|
61
|
+
const paths_1 = require("./paths");
|
|
62
|
+
const git_1 = require("./git");
|
|
63
|
+
const toolservers_1 = require("./toolservers");
|
|
64
|
+
const shell_1 = require("./shell");
|
|
65
|
+
Object.defineProperty(exports, "guessTestCommand", { enumerable: true, get: function () { return shell_1.guessTestCommand; } });
|
|
66
|
+
Object.defineProperty(exports, "stopEverything", { enumerable: true, get: function () { return shell_1.stopEverything; } });
|
|
67
|
+
Object.defineProperty(exports, "stopForeground", { enumerable: true, get: function () { return shell_1.stopForeground; } });
|
|
68
|
+
/** Everything a machine with a shell can do. Sent to the server on hello, and
|
|
69
|
+
* the catalog offered to the model is narrowed to it, so a client is never
|
|
70
|
+
* told about an action it cannot perform. */
|
|
71
|
+
exports.NATIVE_CAPABILITIES = [
|
|
72
|
+
"read_file", "read_notebook", "read_pdf", "list_dir", "file_map", "glob", "grep",
|
|
73
|
+
// The project's own checker for a changed file (manual 5.11).
|
|
74
|
+
"read_diagnostics",
|
|
75
|
+
"write_file", "edit_file", "multi_edit", "create_dir", "delete_file",
|
|
76
|
+
"move_file", "apply_patch",
|
|
77
|
+
// Bytes, for a document the server built. Never offered to the model; the
|
|
78
|
+
// loop issues it after `make_document`, so a deck lands in the folder the
|
|
79
|
+
// person is standing in rather than behind a link in a web page they are not
|
|
80
|
+
// looking at.
|
|
81
|
+
"write_bytes",
|
|
82
|
+
"run_command", "run_tests", "run_background", "read_output", "stop_process",
|
|
83
|
+
"install_dependency",
|
|
84
|
+
"git_status", "git_diff", "git_log", "git_branch", "git_commit",
|
|
85
|
+
// Pushing is here because it needs the repository. Pull requests, reviews,
|
|
86
|
+
// checks and releases are NOT: they need the tenant's credential, so they run
|
|
87
|
+
// on the server and are never offered to a client.
|
|
88
|
+
"git_push",
|
|
89
|
+
"todo_write",
|
|
90
|
+
// A helper's own copy of the project (manual 5.26). Never offered to the
|
|
91
|
+
// model: the service opens, reads and removes copies for editing helpers.
|
|
92
|
+
"worktree_open", "worktree_diff", "worktree_close",
|
|
93
|
+
// Tool servers on this machine the person approved (manual 9.10). Never
|
|
94
|
+
// offered to the model: the service lists and calls them by name.
|
|
95
|
+
"tool_server_list", "tool_server_call",
|
|
96
|
+
// The hooks of plugins installed here (manual 9.15), handed over by the
|
|
97
|
+
// service at the start of a job. Never offered to the model.
|
|
98
|
+
"plugin_hooks",
|
|
99
|
+
// This machine runs the hooks the service names, with each action and at
|
|
100
|
+
// the moments of a job (manual 10.9). Never offered to the model.
|
|
101
|
+
"run_hooks",
|
|
102
|
+
// A whole file, byte for byte, for a checkpoint (manual 7.3). Never offered
|
|
103
|
+
// to the model: `read_file` pages a long file and trims its last line break.
|
|
104
|
+
"read_raw",
|
|
105
|
+
];
|
|
106
|
+
/** What a helper may do inside its own copy (manual 5.26). */
|
|
107
|
+
const IN_COPY = new Set([
|
|
108
|
+
"read_file", "list_dir", "file_map", "glob", "grep",
|
|
109
|
+
"write_file", "edit_file", "multi_edit", "apply_patch", "create_dir", "git_status", "git_diff",
|
|
110
|
+
]);
|
|
111
|
+
/** How a package is added, per ecosystem.
|
|
112
|
+
*
|
|
113
|
+
* Named rather than passed through as a raw command, because "install a
|
|
114
|
+
* dependency" is a thing a person can meaningfully approve and
|
|
115
|
+
* `npm i --silent --prefix ../.. something` is not. The approval is only worth
|
|
116
|
+
* asking for if what is being approved is legible. */
|
|
117
|
+
function installCommand(root, pkg, manager, dev) {
|
|
118
|
+
const has = (f) => fs.existsSync(`${root}/${f}`);
|
|
119
|
+
const m = manager
|
|
120
|
+
|| (has("pnpm-lock.yaml") ? "pnpm" : has("yarn.lock") ? "yarn"
|
|
121
|
+
: has("package.json") ? "npm" : has("Cargo.toml") ? "cargo"
|
|
122
|
+
: has("go.mod") ? "go" : has("pyproject.toml") || has("requirements.txt") ? "pip" : "");
|
|
123
|
+
switch (m) {
|
|
124
|
+
case "npm": return `npm install ${dev ? "--save-dev " : ""}${pkg}`;
|
|
125
|
+
case "pnpm": return `pnpm add ${dev ? "-D " : ""}${pkg}`;
|
|
126
|
+
case "yarn": return `yarn add ${dev ? "-D " : ""}${pkg}`;
|
|
127
|
+
case "pip": return `python -m pip install ${pkg}`;
|
|
128
|
+
case "cargo": return `cargo add ${pkg}${dev ? " --dev" : ""}`;
|
|
129
|
+
case "go": return `go get ${pkg}`;
|
|
130
|
+
default: return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** A file's exact contents and their SHA-256, for a checkpoint (manual 7.3).
|
|
134
|
+
* Refused, never trimmed, when it is too large or not text. */
|
|
135
|
+
function readRaw(root, rel) {
|
|
136
|
+
const crypto = require("crypto");
|
|
137
|
+
const base = fs.realpathSync(root);
|
|
138
|
+
const where = path.resolve(base, rel);
|
|
139
|
+
if (!where.startsWith(base + path.sep))
|
|
140
|
+
return { isError: true, content: `${rel} is outside this project.` };
|
|
141
|
+
let bytes;
|
|
142
|
+
try {
|
|
143
|
+
bytes = fs.readFileSync(where);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return { isError: true, content: `${rel} does not exist.`, facts: { missing: 1 } };
|
|
147
|
+
}
|
|
148
|
+
if (bytes.length > 400_000)
|
|
149
|
+
return { isError: true, content: `${rel} is too large to copy.`, facts: { too_large: 1 } };
|
|
150
|
+
let text;
|
|
151
|
+
try {
|
|
152
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return { isError: true, content: `${rel} is not text, so it cannot be copied.`, facts: { binary: 1 } };
|
|
156
|
+
}
|
|
157
|
+
return { isError: false, content: text, summary: `${bytes.length} bytes`,
|
|
158
|
+
facts: { sha256: crypto.createHash("sha256").update(bytes).digest("hex") } };
|
|
159
|
+
}
|
|
160
|
+
class NativeExecutor {
|
|
161
|
+
/** What this machine can do. The browser actions only where a browser is
|
|
162
|
+
* installed (manual 10.16); the service offers them only when switched on. */
|
|
163
|
+
capabilities = (0, browser_1.findBrowser)() ? [...exports.NATIVE_CAPABILITIES, ...browser_1.BROWSER_ACTIONS] : exports.NATIVE_CAPABILITIES;
|
|
164
|
+
/** The job's browser, started on first use (10.16). */
|
|
165
|
+
browser;
|
|
166
|
+
/** Close what this job opened on the machine: its browser. */
|
|
167
|
+
close() {
|
|
168
|
+
this.browser?.close();
|
|
169
|
+
this.browser = undefined;
|
|
170
|
+
}
|
|
171
|
+
root;
|
|
172
|
+
onOutput;
|
|
173
|
+
/** The test command, once it has been worked out or supplied. Remembered so a
|
|
174
|
+
* job that had to be told does not have to be told again. */
|
|
175
|
+
testCommand;
|
|
176
|
+
/** Whether the last test run passed. The grading signal the whole product
|
|
177
|
+
* rests on, kept so a session can report it when the job ends. */
|
|
178
|
+
lastTestsPassed;
|
|
179
|
+
/** The operating system's sandbox, set when the service turns it on for a
|
|
180
|
+
* job (manual 4.12). Absent, commands run with the person's own access. */
|
|
181
|
+
sandbox;
|
|
182
|
+
constructor(opts) {
|
|
183
|
+
this.root = fs.realpathSync(opts.root);
|
|
184
|
+
this.toolServers = new toolservers_1.ToolServers(this.root);
|
|
185
|
+
(0, paths_1.allowFolders)(this.root, opts.added ?? []);
|
|
186
|
+
this.onOutput = opts.onOutput;
|
|
187
|
+
}
|
|
188
|
+
/** One executor per helper copy, rooted there and nowhere else. */
|
|
189
|
+
inCopies = new Map();
|
|
190
|
+
/** Tool servers this job started (manual 9.10), stopped when it ends. */
|
|
191
|
+
toolServers;
|
|
192
|
+
async execute(name, a, plan) {
|
|
193
|
+
// An action for a helper's copy runs there, rooted in the copy, so it can
|
|
194
|
+
// never touch the person's own folder (manual 5.26).
|
|
195
|
+
if (typeof a._worktree === "string") {
|
|
196
|
+
const id = a._worktree;
|
|
197
|
+
const where = (0, worktree_1.worktreePath)(id);
|
|
198
|
+
if (!where)
|
|
199
|
+
return { isError: true, content: `There is no copy called ${id}.` };
|
|
200
|
+
if (!IN_COPY.has(name))
|
|
201
|
+
return { isError: true, content: `Refused: a helper cannot ${name} in its copy.` };
|
|
202
|
+
let inner = this.inCopies.get(id);
|
|
203
|
+
if (!inner) {
|
|
204
|
+
inner = new NativeExecutor({ root: where });
|
|
205
|
+
this.inCopies.set(id, inner);
|
|
206
|
+
}
|
|
207
|
+
const { _worktree: _dropped, ...rest } = a;
|
|
208
|
+
void _dropped;
|
|
209
|
+
return inner.execute(name, rest, plan);
|
|
210
|
+
}
|
|
211
|
+
// A tool server's list or call is carried, never gated by the project's
|
|
212
|
+
// own hooks: the service already decided whether it may happen.
|
|
213
|
+
if (name === "plugin_hooks") {
|
|
214
|
+
// An older service handing over plugin hooks in bulk. This machine runs
|
|
215
|
+
// the hooks the service names with each action instead, so nothing is
|
|
216
|
+
// kept here and nothing is matched here (manual 10.9).
|
|
217
|
+
const given = Array.isArray(a.hooks) ? a.hooks.length : 0;
|
|
218
|
+
return { isError: false, content: `${given} plugin hooks noted; the service names them per action` };
|
|
219
|
+
}
|
|
220
|
+
if (name === "read_raw") {
|
|
221
|
+
return readRaw(this.root, String(a.path ?? ""));
|
|
222
|
+
}
|
|
223
|
+
if (name === "run_hooks") {
|
|
224
|
+
// A moment of the job: job_start, job_end or waiting (manual 10.9).
|
|
225
|
+
const given = Array.isArray(a.hooks) ? a.hooks : [];
|
|
226
|
+
const done = await hooks.runHooks(this.root, given, {
|
|
227
|
+
when: String(a.when ?? ""), facts: (a.facts ?? {}),
|
|
228
|
+
});
|
|
229
|
+
const lines = hooks.reportLines(done);
|
|
230
|
+
return {
|
|
231
|
+
isError: done.failed.length > 0,
|
|
232
|
+
content: lines.join("\n") || "no hooks ran",
|
|
233
|
+
summary: done.failed.length ? `hook failed: ${done.failed.join("; ")}`
|
|
234
|
+
: `${done.runs.length} ${String(a.when)} hook${done.runs.length === 1 ? "" : "s"} ran`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (name === "tool_server_list") {
|
|
238
|
+
return this.toolServers.list(String(a.server ?? ""), String(a.command ?? ""));
|
|
239
|
+
}
|
|
240
|
+
if (name === "tool_server_call") {
|
|
241
|
+
return this.toolServers.call(String(a.server ?? ""), String(a.command ?? ""), String(a.tool ?? ""), (a.arguments ?? {}));
|
|
242
|
+
}
|
|
243
|
+
// The hooks the service named for this action, run in its order (10.9).
|
|
244
|
+
// A `before` hook may REFUSE this action, and that is the one place a
|
|
245
|
+
// project's own script changes what happens. It can only ever refuse: there
|
|
246
|
+
// is no exit code that approves something the agent was not already allowed
|
|
247
|
+
// to do, because a permission system inside the repository would live
|
|
248
|
+
// exactly where an attacker who got that far already is.
|
|
249
|
+
const gate = await hooks.runHooks(this.root, plan?.before ?? [], { when: "before", action: name, args: a });
|
|
250
|
+
if (gate.refusedBy) {
|
|
251
|
+
return { isError: true, content: gate.detail ?? "A hook in this project refused it.",
|
|
252
|
+
summary: gate.detail };
|
|
253
|
+
}
|
|
254
|
+
const shell = {
|
|
255
|
+
root: this.root,
|
|
256
|
+
cwd: typeof a.cwd === "string" ? a.cwd : undefined,
|
|
257
|
+
timeoutMs: typeof a.timeout_s === "number" ? a.timeout_s * 1000 : undefined,
|
|
258
|
+
onOutput: this.onOutput,
|
|
259
|
+
sandbox: this.sandbox,
|
|
260
|
+
};
|
|
261
|
+
try {
|
|
262
|
+
const done = await this.perform(name, a, shell);
|
|
263
|
+
// And afterwards. A failure here is reported and nothing else: the action
|
|
264
|
+
// has already happened, and the overwhelming case is a formatter, which is
|
|
265
|
+
// worth knowing about and not worth losing the work over.
|
|
266
|
+
if (!done.isError) {
|
|
267
|
+
const ran = await hooks.runHooks(this.root, plan?.after ?? [], { when: "after", action: name, args: a });
|
|
268
|
+
const shown = hooks.reportLines(ran);
|
|
269
|
+
if (ran.failed.length) {
|
|
270
|
+
// Said on the action's own line, so the person sees it, and told to
|
|
271
|
+
// the model as what it is: the project's hook, not the file.
|
|
272
|
+
return {
|
|
273
|
+
...done,
|
|
274
|
+
summary: `${done.summary ? `${done.summary}; ` : ""}hook: ${ran.failed.join("; ")}`,
|
|
275
|
+
// The service words the action line from these (manual 10.9).
|
|
276
|
+
facts: { ...(done.facts ?? {}), hook_note: ran.failed.join("; ") },
|
|
277
|
+
content: `${done.content}\n\n[CompletionPrism ran this project's after hooks for this `
|
|
278
|
+
+ `action, as its settings ask. They reported: ${ran.failed.join("; ")}. This note is `
|
|
279
|
+
+ `from the tool, not part of the result.]`,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// Each hook's output, for the full view (manual 10.10).
|
|
283
|
+
if (shown.length)
|
|
284
|
+
return { ...done, content: `${done.content}\n\n${shown.join("\n")}` };
|
|
285
|
+
}
|
|
286
|
+
return done;
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
// Never thrown to the loop. A failure here is a RESULT the model reads and
|
|
290
|
+
// works around; an exception would end a job over something it could have
|
|
291
|
+
// handled itself, and the person would lose everything already done.
|
|
292
|
+
return {
|
|
293
|
+
isError: true,
|
|
294
|
+
content: `This action could not be completed: ${err?.message ?? err}`,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async browse(name, a) {
|
|
299
|
+
if (!this.browser)
|
|
300
|
+
this.browser = new browser_1.Browser(this.root);
|
|
301
|
+
return this.browser.perform(name, a);
|
|
302
|
+
}
|
|
303
|
+
async perform(name, a, shell) {
|
|
304
|
+
{
|
|
305
|
+
if (name.startsWith("browser_"))
|
|
306
|
+
return await this.browse(name, a);
|
|
307
|
+
switch (name) {
|
|
308
|
+
case "run_command":
|
|
309
|
+
return await (0, shell_1.runCommand)(String(a.command ?? ""), shell);
|
|
310
|
+
case "run_tests": {
|
|
311
|
+
const r = await (0, shell_1.runTests)({
|
|
312
|
+
...shell,
|
|
313
|
+
command: a.command || this.testCommand,
|
|
314
|
+
path: a.path,
|
|
315
|
+
});
|
|
316
|
+
if (r.command)
|
|
317
|
+
this.testCommand = r.command;
|
|
318
|
+
if (typeof r.passed === "boolean")
|
|
319
|
+
this.lastTestsPassed = r.passed;
|
|
320
|
+
return r;
|
|
321
|
+
}
|
|
322
|
+
case "run_background":
|
|
323
|
+
return (0, shell_1.startBackground)(String(a.command ?? ""), shell);
|
|
324
|
+
case "read_output":
|
|
325
|
+
return (0, shell_1.readOutput)(String(a.process_id ?? ""));
|
|
326
|
+
case "stop_process":
|
|
327
|
+
return (0, shell_1.stopProcess)(String(a.process_id ?? ""));
|
|
328
|
+
case "install_dependency": {
|
|
329
|
+
const cmd = installCommand(this.root, String(a.package ?? ""), a.manager, Boolean(a.dev));
|
|
330
|
+
if (!cmd) {
|
|
331
|
+
return {
|
|
332
|
+
isError: true,
|
|
333
|
+
content: "I could not work out how this project installs packages. Tell " +
|
|
334
|
+
"me which package manager it uses.",
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return await (0, shell_1.runCommand)(cmd, shell);
|
|
338
|
+
}
|
|
339
|
+
case "apply_patch":
|
|
340
|
+
return await (0, documents_1.applyPatch)(String(a.patch ?? ""), shell);
|
|
341
|
+
case "read_pdf":
|
|
342
|
+
return await (0, documents_1.readPdf)(this.root, String(a.path ?? ""), a.pages, shell);
|
|
343
|
+
case "read_notebook":
|
|
344
|
+
return (0, notebook_1.readNotebook)(this.root, String(a.path ?? ""), a.include_output !== false);
|
|
345
|
+
case "read_diagnostics":
|
|
346
|
+
return await (0, diagnostics_1.readDiagnostics)(this.root, a.path, a.severity, shell);
|
|
347
|
+
case "worktree_open": return await (0, worktree_1.openWorktree)(String(a.id ?? ""), shell);
|
|
348
|
+
case "worktree_diff": return await (0, worktree_1.diffWorktree)(String(a.id ?? ""), shell);
|
|
349
|
+
case "worktree_close": {
|
|
350
|
+
this.inCopies.delete(String(a.id ?? ""));
|
|
351
|
+
return await (0, worktree_1.closeWorktree)(String(a.id ?? ""), shell);
|
|
352
|
+
}
|
|
353
|
+
case "git_status": return await (0, git_1.gitStatus)(shell);
|
|
354
|
+
case "git_diff": return await (0, git_1.gitDiff)({ ...shell, path: a.path, staged: Boolean(a.staged) });
|
|
355
|
+
case "git_log": return await (0, git_1.gitLog)({ ...shell, path: a.path, limit: a.limit });
|
|
356
|
+
case "git_branch": return await (0, git_1.gitBranch)(String(a.name ?? ""), shell);
|
|
357
|
+
case "git_commit": return await (0, git_1.gitCommit)(String(a.message ?? ""), { ...shell, paths: a.paths });
|
|
358
|
+
case "git_push": return await (0, git_1.gitPush)(String(a.branch ?? ""), {
|
|
359
|
+
...shell, createRemote: Boolean(a.create_remote),
|
|
360
|
+
});
|
|
361
|
+
default:
|
|
362
|
+
return (0, files_1.performFileAction)(this.root, name, a);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
exports.NativeExecutor = NativeExecutor;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readNotebook = readNotebook;
|
|
37
|
+
/**
|
|
38
|
+
* Reading a Jupyter notebook.
|
|
39
|
+
*
|
|
40
|
+
* Specification: docs/modules/CODING_AGENT_BUILD_SPECIFICATION.md, register
|
|
41
|
+
* item D4.
|
|
42
|
+
*
|
|
43
|
+
* A notebook is JSON on disk, and reading it with `read_file` gives you that
|
|
44
|
+
* JSON: source split into one string per line, escaped newlines, and base64
|
|
45
|
+
* images that can run to hundreds of kilobytes. A model handed that spends a
|
|
46
|
+
* fortune reading almost nothing.
|
|
47
|
+
*
|
|
48
|
+
* So this reads the cells. Code, markdown, and what each cell actually printed,
|
|
49
|
+
* because in a notebook the output IS the state: a cell that raised is the
|
|
50
|
+
* whole reason somebody is looking, and a variable's value three cells up is
|
|
51
|
+
* often the only record of what the data looked like.
|
|
52
|
+
*
|
|
53
|
+
* **Images are named, never included.** A plot is the point of many cells and
|
|
54
|
+
* it is also a quarter of a megabyte of base64. The cell says a plot was
|
|
55
|
+
* produced, which is the useful fact, at a cost of nine words.
|
|
56
|
+
*/
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const paths_1 = require("./paths");
|
|
59
|
+
/** What one notebook may contribute. Beyond this it is cut and says so. */
|
|
60
|
+
const MAX_CHARS = 60_000;
|
|
61
|
+
/** What one cell's output may contribute. A cell that printed a whole dataframe
|
|
62
|
+
* is common, and the first thirty lines of it say the same thing as all of it. */
|
|
63
|
+
const MAX_OUTPUT_CHARS = 2_000;
|
|
64
|
+
/** Source arrives as one string per line, with the newlines still on them. */
|
|
65
|
+
function text(source) {
|
|
66
|
+
if (Array.isArray(source))
|
|
67
|
+
return source.join("");
|
|
68
|
+
return String(source ?? "");
|
|
69
|
+
}
|
|
70
|
+
function outputOf(cell) {
|
|
71
|
+
const parts = [];
|
|
72
|
+
for (const out of cell.outputs ?? []) {
|
|
73
|
+
const kind = String(out.output_type ?? "");
|
|
74
|
+
if (kind === "stream") {
|
|
75
|
+
parts.push(text(out.text));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (kind === "error") {
|
|
79
|
+
// The most valuable thing in the file. A traceback is usually the reason
|
|
80
|
+
// somebody opened the notebook at all, so it is never trimmed away first.
|
|
81
|
+
const name = String(out.ename ?? "error");
|
|
82
|
+
const message = String(out.evalue ?? "");
|
|
83
|
+
const trace = Array.isArray(out.traceback) ? out.traceback.join("\n") : "";
|
|
84
|
+
parts.push(`${name}: ${message}\n${trace}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (kind === "execute_result" || kind === "display_data") {
|
|
88
|
+
const data = (out.data ?? {});
|
|
89
|
+
if (data["text/plain"]) {
|
|
90
|
+
parts.push(text(data["text/plain"]));
|
|
91
|
+
}
|
|
92
|
+
// Named, not included. A plot is the point of the cell and also a quarter
|
|
93
|
+
// of a megabyte of base64; "a plot was produced" is the useful fact.
|
|
94
|
+
for (const mime of Object.keys(data)) {
|
|
95
|
+
if (mime.startsWith("image/"))
|
|
96
|
+
parts.push(`[${mime} produced, not shown]`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const joined = parts.join("\n").trim();
|
|
101
|
+
if (joined.length <= MAX_OUTPUT_CHARS)
|
|
102
|
+
return joined;
|
|
103
|
+
return `${joined.slice(0, MAX_OUTPUT_CHARS)}\n[... output truncated]`;
|
|
104
|
+
}
|
|
105
|
+
function readNotebook(root, path, includeOutput = true) {
|
|
106
|
+
let raw;
|
|
107
|
+
try {
|
|
108
|
+
raw = fs.readFileSync((0, paths_1.resolveInside)(root, path, true), "utf8");
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
return { isError: true, content: `Could not read ${path}: ${err.message}` };
|
|
112
|
+
}
|
|
113
|
+
let parsed;
|
|
114
|
+
try {
|
|
115
|
+
parsed = JSON.parse(raw);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return {
|
|
119
|
+
isError: true,
|
|
120
|
+
content: `${path} is not a readable notebook. It may be corrupt, or it may not be a notebook at all.`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const cells = parsed.cells ?? [];
|
|
124
|
+
if (!cells.length) {
|
|
125
|
+
return { content: `${path} has no cells in it.`, summary: "empty notebook" };
|
|
126
|
+
}
|
|
127
|
+
const lines = [`${path}: ${cells.length} cells`];
|
|
128
|
+
let used = lines[0].length;
|
|
129
|
+
for (let i = 0; i < cells.length; i += 1) {
|
|
130
|
+
const cell = cells[i];
|
|
131
|
+
const kind = cell.cell_type ?? "code";
|
|
132
|
+
// Numbered, because that is how a person refers to a cell and how an edit
|
|
133
|
+
// will have to name one later.
|
|
134
|
+
const header = `\n--- cell ${i + 1} (${kind}) ---`;
|
|
135
|
+
const source = text(cell.source).trimEnd();
|
|
136
|
+
const output = includeOutput && kind === "code" ? outputOf(cell) : "";
|
|
137
|
+
const block = [header, source, output ? `\n[output]\n${output}` : ""]
|
|
138
|
+
.filter(Boolean).join("\n");
|
|
139
|
+
if (used + block.length > MAX_CHARS) {
|
|
140
|
+
lines.push(`\n[... ${cells.length - i} more cells not read. Ask for a range if you need them.]`);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
lines.push(block);
|
|
144
|
+
used += block.length;
|
|
145
|
+
}
|
|
146
|
+
return { content: lines.join("\n"), summary: `read ${path} (${cells.length} cells)` };
|
|
147
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class OutsideWorkspace extends Error {
|
|
2
|
+
readonly attempted: string;
|
|
3
|
+
/** `real` is where the path actually leads, when that differs from what was
|
|
4
|
+
* asked for: a link is refused by naming its real location (manual 2.4). */
|
|
5
|
+
constructor(attempted: string, real?: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function allowFolders(root: string, added: string[]): void;
|
|
8
|
+
/** Every folder a job in `root` may touch, the project first. */
|
|
9
|
+
export declare function allowedFolders(root: string): string[];
|
|
10
|
+
export declare function isSecret(p: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* An absolute path inside the workspace, or a refusal.
|
|
13
|
+
*
|
|
14
|
+
* `mustExist` is false when the caller is about to CREATE the thing. A file that
|
|
15
|
+
* does not exist yet cannot be resolved, so the check is applied to the deepest
|
|
16
|
+
* parent that does exist: creating `src/new/deep/file.ts` is inside the project
|
|
17
|
+
* exactly when `src` is, and demanding the leaf exist first would make it
|
|
18
|
+
* impossible to write a new file at all.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveInside(root: string, rel: string, mustExist?: boolean): string;
|