@illuminis/comprism 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/out/agent/command.js +4 -4
  2. package/out/agent/session.d.ts +6 -1
  3. package/out/agent/session.js +17 -8
  4. package/out/commands/agents.js +3 -3
  5. package/out/commands/ask.js +8 -8
  6. package/out/commands/codemap.d.ts +1 -1
  7. package/out/commands/codemap.js +3 -3
  8. package/out/commands/commands-thin.js +4 -4
  9. package/out/commands/config.js +1 -1
  10. package/out/commands/cost.js +4 -4
  11. package/out/commands/hooks.js +1 -1
  12. package/out/commands/install.d.ts +1 -1
  13. package/out/commands/install.js +3 -2
  14. package/out/commands/instructions.js +1 -1
  15. package/out/commands/integrations.js +4 -4
  16. package/out/commands/keys.js +6 -6
  17. package/out/commands/login.js +37 -21
  18. package/out/commands/permissions.js +2 -2
  19. package/out/commands/plugins.js +3 -3
  20. package/out/commands/privacy.js +2 -2
  21. package/out/commands/repl.js +107 -83
  22. package/out/commands/review.js +6 -6
  23. package/out/commands/settings.js +30 -123
  24. package/out/commands/skills.js +2 -2
  25. package/out/commands/unattended.js +6 -6
  26. package/out/commands/update.js +1 -1
  27. package/out/commands/welcome.js +20 -29
  28. package/out/commands/worktrees.d.ts +1 -1
  29. package/out/graph/sync.d.ts +1 -1
  30. package/out/graph/sync.js +6 -6
  31. package/out/lib/attach.js +4 -3
  32. package/out/lib/commandlist.d.ts +1 -1
  33. package/out/lib/commandlist.js +2 -2
  34. package/out/lib/config.d.ts +3 -2
  35. package/out/lib/config.js +7 -8
  36. package/out/lib/connection.js +1 -1
  37. package/out/lib/gateway.d.ts +5 -5
  38. package/out/lib/machine.js +1 -1
  39. package/out/lib/project-ops.d.ts +1 -1
  40. package/out/lib/project-ops.js +4 -4
  41. package/out/lib/queue.js +1 -1
  42. package/out/lib/readiness.d.ts +3 -30
  43. package/out/lib/readiness.js +13 -82
  44. package/out/lib/servicecommand.d.ts +14 -0
  45. package/out/lib/servicecommand.js +74 -0
  46. package/out/lib/sessions.js +3 -3
  47. package/out/lib/ui.d.ts +9 -6
  48. package/out/lib/ui.js +20 -26
  49. package/out/lib/voice.js +1 -1
  50. package/out/lib/words.d.ts +20 -0
  51. package/out/lib/words.js +27 -0
  52. package/out/machine/codemap/build.d.ts +45 -0
  53. package/out/machine/codemap/build.js +91 -0
  54. package/out/machine/codemap/facts.d.ts +47 -0
  55. package/out/machine/codemap/facts.js +12 -0
  56. package/out/machine/codemap/files.d.ts +45 -0
  57. package/out/machine/codemap/files.js +207 -0
  58. package/out/machine/codemap/read-locales.d.ts +29 -0
  59. package/out/machine/codemap/read-locales.js +246 -0
  60. package/out/machine/codemap/read-python.d.ts +11 -0
  61. package/out/machine/codemap/read-python.js +116 -0
  62. package/out/machine/codemap/read-typescript.d.ts +16 -0
  63. package/out/machine/codemap/read-typescript.js +292 -0
  64. package/out/machine/executor/browser.d.ts +14 -0
  65. package/out/machine/executor/browser.js +270 -0
  66. package/out/machine/executor/diagnostics.d.ts +2 -0
  67. package/out/machine/executor/diagnostics.js +181 -0
  68. package/out/machine/executor/documents.d.ts +40 -0
  69. package/out/machine/executor/documents.js +170 -0
  70. package/out/machine/executor/files.d.ts +2 -0
  71. package/out/machine/executor/files.js +590 -0
  72. package/out/machine/executor/git.d.ts +48 -0
  73. package/out/machine/executor/git.js +145 -0
  74. package/out/machine/executor/hooks.d.ts +51 -0
  75. package/out/machine/executor/hooks.js +154 -0
  76. package/out/machine/executor/index.d.ts +45 -0
  77. package/out/machine/executor/index.js +367 -0
  78. package/out/machine/executor/notebook.d.ts +2 -0
  79. package/out/machine/executor/notebook.js +147 -0
  80. package/out/machine/executor/paths.d.ts +20 -0
  81. package/out/machine/executor/paths.js +154 -0
  82. package/out/machine/executor/sandbox.d.ts +40 -0
  83. package/out/machine/executor/sandbox.js +299 -0
  84. package/out/machine/executor/shell.d.ts +86 -0
  85. package/out/machine/executor/shell.js +582 -0
  86. package/out/machine/executor/toolservers.d.ts +20 -0
  87. package/out/machine/executor/toolservers.js +189 -0
  88. package/out/machine/executor/worktree.d.ts +9 -0
  89. package/out/machine/executor/worktree.js +119 -0
  90. package/out/machine/folder/project.d.ts +28 -0
  91. package/out/machine/folder/project.js +114 -0
  92. package/out/machine/runtime/home.d.ts +2 -0
  93. package/out/machine/runtime/home.js +48 -0
  94. package/out/machine/runtime/needs.d.ts +30 -0
  95. package/out/machine/runtime/needs.js +89 -0
  96. package/out/machine/runtime/self.d.ts +23 -0
  97. package/out/machine/runtime/self.js +124 -0
  98. package/out/providers/index.js +2 -1
  99. package/out/thin.js +11 -10
  100. 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,2 @@
1
+ import { RunResult } from "./shell";
2
+ export declare function readNotebook(root: string, path: string, includeOutput?: boolean): RunResult;
@@ -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;