@illuminis/comprism 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +281 -0
  3. package/out/agent/command.d.ts +86 -0
  4. package/out/agent/command.js +259 -0
  5. package/out/agent/render.d.ts +97 -0
  6. package/out/agent/render.js +255 -0
  7. package/out/agent/session.d.ts +175 -0
  8. package/out/agent/session.js +573 -0
  9. package/out/commands/ask.d.ts +1 -0
  10. package/out/commands/ask.js +146 -0
  11. package/out/commands/codemap.d.ts +2 -0
  12. package/out/commands/codemap.js +151 -0
  13. package/out/commands/commands-thin.d.ts +39 -0
  14. package/out/commands/commands-thin.js +182 -0
  15. package/out/commands/install.d.ts +163 -0
  16. package/out/commands/install.js +543 -0
  17. package/out/commands/keys.d.ts +55 -0
  18. package/out/commands/keys.js +344 -0
  19. package/out/commands/login.d.ts +9 -0
  20. package/out/commands/login.js +384 -0
  21. package/out/commands/repl.d.ts +1 -0
  22. package/out/commands/repl.js +752 -0
  23. package/out/commands/settings.d.ts +21 -0
  24. package/out/commands/settings.js +244 -0
  25. package/out/commands/welcome.d.ts +1 -0
  26. package/out/commands/welcome.js +196 -0
  27. package/out/executor/documents.d.ts +40 -0
  28. package/out/executor/documents.js +170 -0
  29. package/out/executor/files.d.ts +2 -0
  30. package/out/executor/files.js +360 -0
  31. package/out/executor/git.d.ts +48 -0
  32. package/out/executor/git.js +132 -0
  33. package/out/executor/hooks.d.ts +67 -0
  34. package/out/executor/hooks.js +247 -0
  35. package/out/executor/index.d.ts +29 -0
  36. package/out/executor/index.js +221 -0
  37. package/out/executor/notebook.d.ts +2 -0
  38. package/out/executor/notebook.js +147 -0
  39. package/out/executor/paths.d.ts +15 -0
  40. package/out/executor/paths.js +126 -0
  41. package/out/executor/shell.d.ts +41 -0
  42. package/out/executor/shell.js +336 -0
  43. package/out/graph/build.d.ts +45 -0
  44. package/out/graph/build.js +91 -0
  45. package/out/graph/facts.d.ts +47 -0
  46. package/out/graph/facts.js +12 -0
  47. package/out/graph/files.d.ts +45 -0
  48. package/out/graph/files.js +207 -0
  49. package/out/graph/read-locales.d.ts +29 -0
  50. package/out/graph/read-locales.js +246 -0
  51. package/out/graph/read-python.d.ts +11 -0
  52. package/out/graph/read-python.js +115 -0
  53. package/out/graph/read-typescript.d.ts +16 -0
  54. package/out/graph/read-typescript.js +292 -0
  55. package/out/graph/sync.d.ts +66 -0
  56. package/out/graph/sync.js +242 -0
  57. package/out/lib/attach.d.ts +62 -0
  58. package/out/lib/attach.js +228 -0
  59. package/out/lib/config.d.ts +93 -0
  60. package/out/lib/config.js +198 -0
  61. package/out/lib/connection.d.ts +73 -0
  62. package/out/lib/connection.js +188 -0
  63. package/out/lib/gateway.d.ts +239 -0
  64. package/out/lib/gateway.js +171 -0
  65. package/out/lib/prompt.d.ts +34 -0
  66. package/out/lib/prompt.js +108 -0
  67. package/out/lib/types.d.ts +417 -0
  68. package/out/lib/types.js +21 -0
  69. package/out/lib/ui.d.ts +114 -0
  70. package/out/lib/ui.js +265 -0
  71. package/out/lib/version.d.ts +24 -0
  72. package/out/lib/version.js +27 -0
  73. package/out/lib/voice.d.ts +50 -0
  74. package/out/lib/voice.js +218 -0
  75. package/out/postinstall.d.ts +2 -0
  76. package/out/postinstall.js +92 -0
  77. package/out/thin.d.ts +2 -0
  78. package/out/thin.js +259 -0
  79. package/package.json +101 -0
  80. package/scripts/read_python.py +270 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * What a coding agent looks like in a terminal.
3
+ *
4
+ * Specification: docs/modules/CODING_AGENT_BUILD_SPECIFICATION.md, register
5
+ * items A6 (a real coding agent in the terminal), I8 (a terminal panel, real
6
+ * output, watchable) and I3 (a live action timeline).
7
+ *
8
+ * A terminal has no panels, so the whole interface is a stream of lines and the
9
+ * scrollback IS the record. That constrains everything here:
10
+ *
11
+ * **Every line has to make sense on its own, later.** Somebody scrolls back
12
+ * through a twenty minute run looking for the moment it went wrong. A line that
13
+ * only means something next to the line above it has failed them.
14
+ *
15
+ * **Nothing is redrawn or erased.** Spinners that rewrite their own line leave
16
+ * nothing behind, and a terminal being piped to a file fills with escape codes.
17
+ * What is printed stays printed.
18
+ *
19
+ * **Color is never the only signal.** Terminals get piped, logged and read by
20
+ * people who cannot separate red from green, so every state carries a word or a
21
+ * symbol as well as a color.
22
+ */
23
+ export declare const dim: (t: string) => string;
24
+ export declare const bold: (t: string) => string;
25
+ export declare const red: (t: string) => string;
26
+ export declare const green: (t: string) => string;
27
+ export declare const yellow: (t: string) => string;
28
+ export declare const gray: (t: string) => string;
29
+ /** Money, at the precision a person can act on.
30
+ *
31
+ * Four decimal places below a cent, because a coding step often costs less
32
+ * than one and rounding to $0.00 would show the cheapest work, which is what
33
+ * this product is best at, as free. */
34
+ export declare function money(usd: number | null | undefined): string;
35
+ export declare function duration(ms: number | null | undefined): string;
36
+ /** One action, as it happens. A word as well as a color, so this still reads
37
+ * when piped to a file. */
38
+ export declare function action(name: string, target: string, ok: boolean, ms?: number): string;
39
+ /** The share of a step's context the provider served from its cache, or null
40
+ * when it reported no counts. Anthropic counts cached tokens OUTSIDE the input
41
+ * figure, so the whole context is the three counts added together. */
42
+ export declare function reusePct(tokensIn: number | null | undefined, cacheRead: number | null | undefined, cacheWrite: number | null | undefined): number | null;
43
+ /** One model call: which model, what it cost, and how much of its context the
44
+ * provider had already seen. The last figure is the one study 005 found
45
+ * missing from our receipt: without it nobody could tell a warm step from a
46
+ * cold one, and every step was cold. */
47
+ export declare function step(index: number, model: string, usd: number | null, ms: number | null, reused?: number | null): string;
48
+ /** The task list, reprinted whenever it changes.
49
+ *
50
+ * Reprinted rather than redrawn in place: what the agent intended at each point
51
+ * is worth keeping, and it is often the fastest way to see where a job went off
52
+ * course. */
53
+ export declare function todos(items: Array<{
54
+ content: string;
55
+ status: string;
56
+ }>): string;
57
+ /** A change, before it is made.
58
+ *
59
+ * Full text for a small edit and a summary for a large one. Pasting four
60
+ * hundred lines into a terminal is not review, it is a wall somebody presses
61
+ * through, and an approval people press through protects nobody. */
62
+ export declare function diff(name: string, input: Record<string, unknown>): string;
63
+ /** How the job ended, in the words a person reads. */
64
+ export declare function outcome(o: string, detail: string | null | undefined): string;
65
+ /** One model's share of a job, as our server reported it. */
66
+ export interface ModelShare {
67
+ model: string | null;
68
+ steps: number;
69
+ usd: number | null;
70
+ unpriced?: number;
71
+ }
72
+ /**
73
+ * The receipt under a finished job.
74
+ *
75
+ * ── The terminal does not compute this, and must not ──────────────────────
76
+ *
77
+ * This used to assemble its own sentence from four loose numbers, so the
78
+ * terminal quoted a different receipt from the browser and the desktop app for
79
+ * the same job: no models, no comparison model and no saving, which is the
80
+ * entire claim the product is sold on.
81
+ *
82
+ * Our server owns the receipt. It holds the price list, the comparison model
83
+ * and the arithmetic, it writes the wording once, and every client shows that
84
+ * wording. All this function does is make it read well in a terminal: indent
85
+ * it, quieten it, and turn the address in it into something clickable. That is
86
+ * formatting, which belongs to the client, and it is the only part that does.
87
+ */
88
+ export declare function receipt(text: string, models?: ModelShare[], testsPassed?: boolean): string;
89
+ /**
90
+ * Plain text, from a model that writes markdown.
91
+ *
92
+ * A terminal has no bold and no bullet glyphs, so `**like this**` arrives on
93
+ * screen as literal asterisks and a heading looks like a typing mistake. The
94
+ * emphasis is real, so it is kept as terminal bold rather than thrown away;
95
+ * everything that only exists to be rendered by a browser is removed.
96
+ */
97
+ export declare function plain(text: string): string;
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ /**
3
+ * What a coding agent looks like in a terminal.
4
+ *
5
+ * Specification: docs/modules/CODING_AGENT_BUILD_SPECIFICATION.md, register
6
+ * items A6 (a real coding agent in the terminal), I8 (a terminal panel, real
7
+ * output, watchable) and I3 (a live action timeline).
8
+ *
9
+ * A terminal has no panels, so the whole interface is a stream of lines and the
10
+ * scrollback IS the record. That constrains everything here:
11
+ *
12
+ * **Every line has to make sense on its own, later.** Somebody scrolls back
13
+ * through a twenty minute run looking for the moment it went wrong. A line that
14
+ * only means something next to the line above it has failed them.
15
+ *
16
+ * **Nothing is redrawn or erased.** Spinners that rewrite their own line leave
17
+ * nothing behind, and a terminal being piped to a file fills with escape codes.
18
+ * What is printed stays printed.
19
+ *
20
+ * **Color is never the only signal.** Terminals get piped, logged and read by
21
+ * people who cannot separate red from green, so every state carries a word or a
22
+ * symbol as well as a color.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.gray = exports.yellow = exports.green = exports.red = exports.bold = exports.dim = void 0;
26
+ exports.money = money;
27
+ exports.duration = duration;
28
+ exports.action = action;
29
+ exports.reusePct = reusePct;
30
+ exports.step = step;
31
+ exports.todos = todos;
32
+ exports.diff = diff;
33
+ exports.outcome = outcome;
34
+ exports.receipt = receipt;
35
+ exports.plain = plain;
36
+ /** Built from a character code rather than written as a literal escape, so this
37
+ * file contains no control characters and stays safe to grep, diff and paste. */
38
+ const CSI = String.fromCharCode(27) + "[";
39
+ const supportsColour = process.stdout.isTTY && process.env.NO_COLOR === undefined;
40
+ function paint(code, text) {
41
+ return supportsColour ? CSI + code + "m" + text + CSI + "0m" : text;
42
+ }
43
+ const dim = (t) => paint("2", t);
44
+ exports.dim = dim;
45
+ const bold = (t) => paint("1", t);
46
+ exports.bold = bold;
47
+ const red = (t) => paint("31", t);
48
+ exports.red = red;
49
+ const green = (t) => paint("32", t);
50
+ exports.green = green;
51
+ const yellow = (t) => paint("33", t);
52
+ exports.yellow = yellow;
53
+ const gray = (t) => paint("90", t);
54
+ exports.gray = gray;
55
+ /** Money, at the precision a person can act on.
56
+ *
57
+ * Four decimal places below a cent, because a coding step often costs less
58
+ * than one and rounding to $0.00 would show the cheapest work, which is what
59
+ * this product is best at, as free. */
60
+ function money(usd) {
61
+ if (usd === null || usd === undefined)
62
+ return "unpriced";
63
+ return usd < 0.01 ? `$${usd.toFixed(4)}` : `$${usd.toFixed(2)}`;
64
+ }
65
+ function duration(ms) {
66
+ if (!ms)
67
+ return "";
68
+ return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
69
+ }
70
+ /** One action, as it happens. A word as well as a color, so this still reads
71
+ * when piped to a file. */
72
+ function action(name, target, ok, ms) {
73
+ const mark = ok ? (0, exports.green)("ok ") : (0, exports.red)("fail");
74
+ const tail = ms ? (0, exports.dim)(` ${duration(ms)}`) : "";
75
+ return ` ${mark} ${(0, exports.bold)(name)} ${(0, exports.dim)(target)}${tail}`;
76
+ }
77
+ /** The share of a step's context the provider served from its cache, or null
78
+ * when it reported no counts. Anthropic counts cached tokens OUTSIDE the input
79
+ * figure, so the whole context is the three counts added together. */
80
+ function reusePct(tokensIn, cacheRead, cacheWrite) {
81
+ if (cacheRead === null || cacheRead === undefined)
82
+ return null;
83
+ const whole = (tokensIn ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);
84
+ if (!whole)
85
+ return null;
86
+ return Math.round((cacheRead / whole) * 100);
87
+ }
88
+ /** One model call: which model, what it cost, and how much of its context the
89
+ * provider had already seen. The last figure is the one study 005 found
90
+ * missing from our receipt: without it nobody could tell a warm step from a
91
+ * cold one, and every step was cold. */
92
+ function step(index, model, usd, ms, reused = null) {
93
+ const warm = reused === null ? "" : ` cached ${reused}%`;
94
+ return (0, exports.dim)(` step ${index} ${model || "?"} ${money(usd)} ${duration(ms)}${warm}`);
95
+ }
96
+ /** The task list, reprinted whenever it changes.
97
+ *
98
+ * Reprinted rather than redrawn in place: what the agent intended at each point
99
+ * is worth keeping, and it is often the fastest way to see where a job went off
100
+ * course. */
101
+ function todos(items) {
102
+ if (!items.length)
103
+ return "";
104
+ const rows = items.map((t) => {
105
+ const mark = t.status === "completed" ? (0, exports.green)("done")
106
+ : t.status === "in_progress" ? (0, exports.yellow)("now ") : (0, exports.dim)("todo");
107
+ const text = t.status === "completed" ? (0, exports.dim)(t.content) : t.content;
108
+ return ` ${mark} ${text}`;
109
+ });
110
+ return `\n ${(0, exports.bold)("Plan")}\n${rows.join("\n")}\n`;
111
+ }
112
+ /** A change, before it is made.
113
+ *
114
+ * Full text for a small edit and a summary for a large one. Pasting four
115
+ * hundred lines into a terminal is not review, it is a wall somebody presses
116
+ * through, and an approval people press through protects nobody. */
117
+ function diff(name, input) {
118
+ const target = String(input.path ?? input.to ?? "");
119
+ const head = `\n ${(0, exports.bold)(name)} ${target}\n`;
120
+ const show = (from, to) => {
121
+ const before = from.split("\n"), after = to.split("\n");
122
+ if (before.length + after.length > 60) {
123
+ return ` ${(0, exports.dim)(`${before.length} lines replaced by ${after.length} lines`)}\n`;
124
+ }
125
+ return [
126
+ ...before.map((l) => ` ${(0, exports.red)("-")} ${l}`),
127
+ ...after.map((l) => ` ${(0, exports.green)("+")} ${l}`),
128
+ ].join("\n") + "\n";
129
+ };
130
+ if (name === "edit_file") {
131
+ return head + show(String(input.old_text ?? ""), String(input.new_text ?? ""));
132
+ }
133
+ if (name === "multi_edit") {
134
+ const edits = input.edits ?? [];
135
+ return head + edits.map((e) => show(e.old_text, e.new_text)).join(` ${(0, exports.dim)("--")}\n`);
136
+ }
137
+ if (name === "write_file") {
138
+ const bodyText = String(input.content ?? "");
139
+ const lines = bodyText.split("\n");
140
+ if (lines.length <= 40) {
141
+ return head + lines.map((l) => ` ${(0, exports.green)("+")} ${l}`).join("\n") + "\n";
142
+ }
143
+ return head + ` ${(0, exports.dim)(`${lines.length} lines, ${bodyText.length.toLocaleString()} characters`)}\n`;
144
+ }
145
+ if (name === "run_command" || name === "run_background") {
146
+ return `\n ${(0, exports.bold)("run")} ${String(input.command ?? "")}\n`;
147
+ }
148
+ if (name === "install_dependency") {
149
+ return `\n ${(0, exports.bold)("install")} ${String(input.package ?? "")}`
150
+ + ` ${(0, exports.dim)("(this changes the lockfile for everyone who works here)")}\n`;
151
+ }
152
+ if (name === "delete_file") {
153
+ return head + ` ${(0, exports.dim)("A copy is kept, so this can be undone.")}\n`;
154
+ }
155
+ return head;
156
+ }
157
+ /** How the job ended, in the words a person reads. */
158
+ function outcome(o, detail) {
159
+ const word = {
160
+ finished: (0, exports.green)("Done"),
161
+ cancelled: (0, exports.yellow)("Stopped"),
162
+ step_limit: (0, exports.yellow)("Stopped after too many steps"),
163
+ spend_limit: (0, exports.yellow)("Stopped at the spending limit"),
164
+ failed: (0, exports.red)("Could not finish"),
165
+ refused: (0, exports.red)("Refused"),
166
+ };
167
+ return `\n${word[o] ?? o}${detail ? (0, exports.dim)(` ${detail}`) : ""}\n`;
168
+ }
169
+ /**
170
+ * A link a person can click, where the terminal supports one.
171
+ *
172
+ * Modern terminals understand an escape that carries an address behind a piece
173
+ * of text; older ones print it as ordinary characters, so the address is shown
174
+ * in words there instead. Never a bare escape with no fallback: a receipt whose
175
+ * proof is unreachable is not proof.
176
+ */
177
+ function link(text, url) {
178
+ if (!process.stdout.isTTY)
179
+ return `${text} (${url})`;
180
+ return `]8;;${url}${text}]8;;`;
181
+ }
182
+ /**
183
+ * The receipt under a finished job.
184
+ *
185
+ * ── The terminal does not compute this, and must not ──────────────────────
186
+ *
187
+ * This used to assemble its own sentence from four loose numbers, so the
188
+ * terminal quoted a different receipt from the browser and the desktop app for
189
+ * the same job: no models, no comparison model and no saving, which is the
190
+ * entire claim the product is sold on.
191
+ *
192
+ * Our server owns the receipt. It holds the price list, the comparison model
193
+ * and the arithmetic, it writes the wording once, and every client shows that
194
+ * wording. All this function does is make it read well in a terminal: indent
195
+ * it, quieten it, and turn the address in it into something clickable. That is
196
+ * formatting, which belongs to the client, and it is the only part that does.
197
+ */
198
+ function receipt(text, models = [], testsPassed) {
199
+ const lines = [];
200
+ for (const line of String(text || "").split("\n")) {
201
+ if (!line.trim())
202
+ continue;
203
+ // The address our server put in the sentence becomes a real link, and the
204
+ // words around it are left exactly as they were written.
205
+ const shown = line.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, label, url) => link(String(label), String(url)));
206
+ lines.push((0, exports.dim)(` ${shown}`));
207
+ }
208
+ // Which models did the work, cheapest first, because the cheap ones doing
209
+ // most of the steps IS the mechanism and a reader should see it at a glance.
210
+ const priced = models.filter((m) => m.model);
211
+ if (priced.length > 1) {
212
+ const order = [...priced].sort((a, b) => (a.usd ?? 0) - (b.usd ?? 0));
213
+ const width = Math.max(...order.map((m) => String(m.model).length));
214
+ for (const m of order) {
215
+ const steps = `${m.steps} step${m.steps === 1 ? "" : "s"}`;
216
+ const cost = m.usd === null || m.usd === undefined
217
+ // Never a zero standing in for a price nobody has. A step we could not
218
+ // price is said so, because a total that hides one is a smaller total.
219
+ ? "not priced"
220
+ : money(m.usd);
221
+ lines.push((0, exports.dim)(` ${String(m.model).padEnd(width)} ${steps.padEnd(9)} ${cost}`));
222
+ }
223
+ }
224
+ if (testsPassed !== undefined) {
225
+ lines.push(testsPassed ? (0, exports.green)(" tests passed") : (0, exports.red)(" tests failed"));
226
+ }
227
+ return lines.length > 0 ? `${lines.join("\n")}\n` : "";
228
+ }
229
+ /**
230
+ * Plain text, from a model that writes markdown.
231
+ *
232
+ * A terminal has no bold and no bullet glyphs, so `**like this**` arrives on
233
+ * screen as literal asterisks and a heading looks like a typing mistake. The
234
+ * emphasis is real, so it is kept as terminal bold rather than thrown away;
235
+ * everything that only exists to be rendered by a browser is removed.
236
+ */
237
+ function plain(text) {
238
+ return text
239
+ // Fenced code keeps its content and loses its fence markers.
240
+ .replace(/^```[a-zA-Z0-9]*\n?/gm, '')
241
+ .replace(/^```$/gm, '')
242
+ // Headings become their own words, emphasized.
243
+ .replace(/^#{1,6}\s+(.+)$/gm, (_m, t) => (0, exports.bold)(String(t)))
244
+ // Bold and italic become the terminal's own emphasis.
245
+ .replace(/\*\*([^*]+)\*\*/g, (_m, t) => (0, exports.bold)(String(t)))
246
+ .replace(/__([^_]+)__/g, (_m, t) => (0, exports.bold)(String(t)))
247
+ .replace(/(^|[^*])\*([^*\n]+)\*/g, (_m, pre, t) => pre + String(t))
248
+ // A bullet is a bullet, not an asterisk.
249
+ .replace(/^\s*[*-]\s+/gm, ' . ')
250
+ // Inline code keeps its text; the backticks were never for a terminal.
251
+ .replace(/`([^`]+)`/g, (_m, t) => String(t))
252
+ // A link reads as its words, with the address after it.
253
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, t, u) => `${t} (${u})`)
254
+ .replace(/\n{3,}/g, '\n\n');
255
+ }
@@ -0,0 +1,175 @@
1
+ import * as ui from "./render";
2
+ export interface SessionOptions {
3
+ /** Where the workspace lives, and the URL of the server that owns it. */
4
+ baseUrl: string;
5
+ /** The workspace credential the device flow minted. Never a decodable token. */
6
+ credential: string;
7
+ root: string;
8
+ /** read_only, approve_writes, auto_edit or full_auto. Named, not numbered. */
9
+ permissionMode?: string;
10
+ /** Investigate and propose, change nothing. */
11
+ planOnly?: boolean;
12
+ /** The plan was read and approved, so the files in it are not asked about
13
+ * again. The server checks this against what the account permits; sending it
14
+ * is a statement of what the person said, not a grant. */
15
+ planApproved?: boolean;
16
+ /** A model the person named. Absent means the router chooses, per step, which
17
+ * is the point of the product. On its own this TELLS the router what is
18
+ * running, so the receipt can price the saving against it; the router is
19
+ * still free to move up. Set `pinModel` to be given that model and no other. */
20
+ model?: string;
21
+ /** Run `model` and nothing else, whatever the router would prefer. Off by
22
+ * default. For a person who wants to decide for themselves, and for the
23
+ * corpus study, which has to measure each model as itself. */
24
+ pinModel?: boolean;
25
+ maxSpendUsd?: number;
26
+ /** Commands the person has approved in advance, matched exactly.
27
+ *
28
+ * The one thing a run with nobody watching cannot otherwise do is verify its
29
+ * own work: every mode asks before running an arbitrary command, correctly,
30
+ * and there is nobody there to answer. Naming the command up front is how
31
+ * that is settled without widening anything, which is why it is a list of
32
+ * exact commands and not a flag that turns the check off. */
33
+ approvedCommands?: string[];
34
+ /** Files already uploaded and waiting, to be read before this question.
35
+ * Ids only: the server holds the text for half an hour and nothing here has
36
+ * ever seen the contents. */
37
+ attachmentIds?: string[];
38
+ /** Nobody is at the keyboard. Approvals cannot be asked for, so the
39
+ * permission mode has to have settled them in advance. */
40
+ headless?: boolean;
41
+ /** Ask the person a yes or no question, using a keyboard somebody else
42
+ * already owns.
43
+ *
44
+ * A session that is reading its own prompt cannot also let this class open a
45
+ * reader: the two divide the typing between them and the second gets
46
+ * nothing, so every approval hung or came back refused. Whoever started the
47
+ * process owns the keyboard and lends it here. Absent, this class opens its
48
+ * own reader as before, which is the standalone command's case. */
49
+ confirm?: (question: string) => Promise<boolean>;
50
+ /** Continue a job that already exists, rather than starting one. */
51
+ resumeJobId?: string;
52
+ /** The job this question follows on from. The server loads that job's
53
+ * conversation from the record, so a terminal with no store of its own still
54
+ * holds a thread. */
55
+ continuesJob?: string;
56
+ /** Go without the code map for this job. See `command.ts`. */
57
+ withoutMap?: boolean;
58
+ }
59
+ export interface JobResult {
60
+ outcome: string;
61
+ detail?: string | null;
62
+ jobId?: string;
63
+ steps: number;
64
+ actions: number;
65
+ totalUsd: number;
66
+ /** What routing saved against the comparison model, or null when there is
67
+ * nothing to compare this job against. Never 0 in place of unknown: a saving
68
+ * of zero and a saving nobody can price are different answers. */
69
+ savedUsd: number | null;
70
+ /** The receipt sentence, exactly as our server wrote it. The terminal
71
+ * formats this and never composes its own: the price list, the comparison
72
+ * model and the arithmetic are all on the server, so a client that wrote its
73
+ * own wording would be a second answer to the same question. */
74
+ receipt: string;
75
+ /** Which models did the work, and each one's share, as the server reported
76
+ * it. Taken as sent, never recalculated here. */
77
+ models: ui.ModelShare[];
78
+ text: string;
79
+ testsPassed?: boolean;
80
+ /** Share of everything the job sent that the provider served from cache. */
81
+ reusedPct?: number | null;
82
+ }
83
+ export declare class TerminalSession {
84
+ private socket;
85
+ private readonly executor;
86
+ private readonly opts;
87
+ private readonly out;
88
+ private lastTodos;
89
+ /** The last thing it said on screen, so the ending does not say it twice.
90
+ * Every step prints what the model said, and the closing frame carries the
91
+ * final text again, so the answer to a one step job appeared twice in a row
92
+ * with nothing between the copies. */
93
+ private lastSaid;
94
+ private canceled;
95
+ constructor(opts: SessionOptions, write?: (s: string) => void);
96
+ /** Where the socket lives, derived from the workspace URL.
97
+ *
98
+ * Derived rather than configured: a second setting for the socket address is
99
+ * a second thing that can point at the wrong server, and the failure would be
100
+ * a job running against somebody else's workspace. */
101
+ /**
102
+ * A pass for this job's stream, from the one service.
103
+ *
104
+ * The job is authorized THERE, where everything else in the product is
105
+ * authorized, and this returns only permission to open the stream. Null when
106
+ * the service refused or could not be reached, and the caller then reports
107
+ * the service's own words rather than guessing at them.
108
+ */
109
+ private pass;
110
+ /** Where the stream lives, derived from the workspace URL.
111
+ *
112
+ * Derived rather than configured: a second setting for the stream address is
113
+ * a second thing that can point at the wrong server, and the failure would be
114
+ * a job running against somebody else's workspace.
115
+ *
116
+ * The credential still travels because the pass is redeemed against the
117
+ * tenant it was issued in, and the pass is what actually authorizes the job.
118
+ * Without a pass this falls back to the old behavior, which is what a
119
+ * workspace running an older build still understands. */
120
+ private socketUrl;
121
+ /** The name the map is held under: the folder's own, which is exactly what
122
+ * the run frame already sends as the workspace. One expression, so the map
123
+ * and the job record can never be about two different projects. */
124
+ private project;
125
+ /** Why the one service would not start this job, in its own words. */
126
+ private refusal;
127
+ /** The workspace does not carry the permission question yet, so fall back. */
128
+ private olderWorkspace;
129
+ /** Set while one is actually running, so two never overlap. */
130
+ private remapping;
131
+ /**
132
+ * The agent just changed something on disk. Bring the map back in step.
133
+ *
134
+ * Only for actions that can change a file. A read or a search leaves the
135
+ * project exactly as it was, and re-reading it would be a round trip bought
136
+ * for nothing on the most common actions there are.
137
+ *
138
+ * The refresh itself is the ordinary one: it compares the files against what
139
+ * the service holds and sends back only what moved, which after one edit is
140
+ * one file. Debounced by a short wait, because an agent that writes four
141
+ * files in one step should pay for one update, not four.
142
+ */
143
+ private noteChanged;
144
+ /**
145
+ * Put the files this job changed back into the map.
146
+ *
147
+ * Runs after the job, without being waited on: the person has their answer
148
+ * and a re-read of three files must not hold up their prompt. A failure here
149
+ * costs nothing, because the next job checks the files anyway and would read
150
+ * them then.
151
+ */
152
+ private remap;
153
+ /** Run one request to its end. Resolves with how it went. */
154
+ run(request: string): Promise<JobResult>;
155
+ /** Stop the running job. Takes effect mid action, not at the end of the run. */
156
+ cancel(): void;
157
+ /** Something typed while it works. Joins the conversation at the next step. */
158
+ steer(text: string): void;
159
+ private announce;
160
+ private narrate;
161
+ private targetOf;
162
+ /** Put a change in front of the person and wait.
163
+ *
164
+ * Three cases, in order. A caller that owns the keyboard lent us one, so the
165
+ * question goes there. Nobody lent one and somebody is watching, so we open
166
+ * our own reader. Nobody is watching, so it is refused.
167
+ *
168
+ * In headless mode there is nobody to ask, so it is refused rather than
169
+ * assumed. Treating silence as consent would let a script approve changes
170
+ * nobody ever saw, which is the opposite of what a permission mode is for:
171
+ * a script that needs to change files says so by choosing a mode that allows
172
+ * it, up front. */
173
+ private ask;
174
+ private report;
175
+ }