@letrquan/book 0.2.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 (103) hide show
  1. package/CHANGELOG.md +2504 -0
  2. package/LICENSE +123 -0
  3. package/README.md +1383 -0
  4. package/dist/app-MOXXWEOY.js +18945 -0
  5. package/dist/app-MOXXWEOY.js.map +1 -0
  6. package/dist/chunk-24AXE6SP.js +12 -0
  7. package/dist/chunk-24AXE6SP.js.map +1 -0
  8. package/dist/chunk-3U5IBM24.js +213 -0
  9. package/dist/chunk-3U5IBM24.js.map +1 -0
  10. package/dist/chunk-574C6MQR.js +743 -0
  11. package/dist/chunk-574C6MQR.js.map +1 -0
  12. package/dist/chunk-5GDZ22YP.js +449 -0
  13. package/dist/chunk-5GDZ22YP.js.map +1 -0
  14. package/dist/chunk-5KLVH3PY.js +167 -0
  15. package/dist/chunk-5KLVH3PY.js.map +1 -0
  16. package/dist/chunk-5RDRAO4B.js +92 -0
  17. package/dist/chunk-5RDRAO4B.js.map +1 -0
  18. package/dist/chunk-7N4J557C.js +10 -0
  19. package/dist/chunk-7N4J557C.js.map +1 -0
  20. package/dist/chunk-A7JLBK2Y.js +3506 -0
  21. package/dist/chunk-A7JLBK2Y.js.map +1 -0
  22. package/dist/chunk-ANSJPYGI.js +84 -0
  23. package/dist/chunk-ANSJPYGI.js.map +1 -0
  24. package/dist/chunk-BTZZUCLI.js +264 -0
  25. package/dist/chunk-BTZZUCLI.js.map +1 -0
  26. package/dist/chunk-C7D4NJ2D.js +217 -0
  27. package/dist/chunk-C7D4NJ2D.js.map +1 -0
  28. package/dist/chunk-CAGOB2N4.js +208 -0
  29. package/dist/chunk-CAGOB2N4.js.map +1 -0
  30. package/dist/chunk-DGMQEI6Y.js +98 -0
  31. package/dist/chunk-DGMQEI6Y.js.map +1 -0
  32. package/dist/chunk-E75IPST5.js +289 -0
  33. package/dist/chunk-E75IPST5.js.map +1 -0
  34. package/dist/chunk-EYGD6X2T.js +180 -0
  35. package/dist/chunk-EYGD6X2T.js.map +1 -0
  36. package/dist/chunk-GTVB4HEM.js +765 -0
  37. package/dist/chunk-GTVB4HEM.js.map +1 -0
  38. package/dist/chunk-J7MIZ5CA.js +1907 -0
  39. package/dist/chunk-J7MIZ5CA.js.map +1 -0
  40. package/dist/chunk-KWHQ2XXL.js +626 -0
  41. package/dist/chunk-KWHQ2XXL.js.map +1 -0
  42. package/dist/chunk-L6LK2QIQ.js +657 -0
  43. package/dist/chunk-L6LK2QIQ.js.map +1 -0
  44. package/dist/chunk-MAQXCUR4.js +1007 -0
  45. package/dist/chunk-MAQXCUR4.js.map +1 -0
  46. package/dist/chunk-S4XL7HOM.js +402 -0
  47. package/dist/chunk-S4XL7HOM.js.map +1 -0
  48. package/dist/chunk-URLQAXTR.js +60 -0
  49. package/dist/chunk-URLQAXTR.js.map +1 -0
  50. package/dist/chunk-V4BOYR52.js +3504 -0
  51. package/dist/chunk-V4BOYR52.js.map +1 -0
  52. package/dist/chunk-VZQXX3WW.js +210 -0
  53. package/dist/chunk-VZQXX3WW.js.map +1 -0
  54. package/dist/chunk-WKBO5R4O.js +34 -0
  55. package/dist/chunk-WKBO5R4O.js.map +1 -0
  56. package/dist/chunk-XIFFTATU.js +15753 -0
  57. package/dist/chunk-XIFFTATU.js.map +1 -0
  58. package/dist/command-approvals-6Y57VCWZ.js +28 -0
  59. package/dist/command-approvals-6Y57VCWZ.js.map +1 -0
  60. package/dist/context-ZC5IFEAX.js +25 -0
  61. package/dist/context-ZC5IFEAX.js.map +1 -0
  62. package/dist/hook-approvals-CTUFKEJT.js +22 -0
  63. package/dist/hook-approvals-CTUFKEJT.js.map +1 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +2582 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/interactive-assets-CCAAWVKH.js +31 -0
  68. package/dist/interactive-assets-CCAAWVKH.js.map +1 -0
  69. package/dist/job-runner.d.ts +2 -0
  70. package/dist/job-runner.js +235 -0
  71. package/dist/job-runner.js.map +1 -0
  72. package/dist/loader-4IDS2RIE.js +15 -0
  73. package/dist/loader-4IDS2RIE.js.map +1 -0
  74. package/dist/mcp-approvals-WNPH65BK.js +18 -0
  75. package/dist/mcp-approvals-WNPH65BK.js.map +1 -0
  76. package/dist/mcp-config-YIJIES2C.js +16 -0
  77. package/dist/mcp-config-YIJIES2C.js.map +1 -0
  78. package/dist/model-window-store-YFVNXCIP.js +27 -0
  79. package/dist/model-window-store-YFVNXCIP.js.map +1 -0
  80. package/dist/openai-compatible-RHZUBHV6.js +10 -0
  81. package/dist/openai-compatible-RHZUBHV6.js.map +1 -0
  82. package/dist/permission-approvals-76PVREFD.js +14 -0
  83. package/dist/permission-approvals-76PVREFD.js.map +1 -0
  84. package/dist/permissions-CHCKTFQQ.js +28 -0
  85. package/dist/permissions-CHCKTFQQ.js.map +1 -0
  86. package/dist/sandbox-CNVAQDA7.js +23 -0
  87. package/dist/sandbox-CNVAQDA7.js.map +1 -0
  88. package/dist/scrollback-LUKLIBVI.js +136 -0
  89. package/dist/scrollback-LUKLIBVI.js.map +1 -0
  90. package/dist/sdk.d.ts +4574 -0
  91. package/dist/sdk.js +665 -0
  92. package/dist/sdk.js.map +1 -0
  93. package/dist/settings-loader-M2QP5TX2.js +21 -0
  94. package/dist/settings-loader-M2QP5TX2.js.map +1 -0
  95. package/dist/settings-removed-JWWPT4WA.js +19 -0
  96. package/dist/settings-removed-JWWPT4WA.js.map +1 -0
  97. package/dist/shell-selection-V6QAYXPX.js +17 -0
  98. package/dist/shell-selection-V6QAYXPX.js.map +1 -0
  99. package/package.json +143 -0
  100. package/patches/ink+6.8.0.patch +13 -0
  101. package/scripts/apply-ink-patch.mjs +29 -0
  102. package/scripts/ink-patch.mjs +59 -0
  103. package/scripts/verify-ink-patch.mjs +11 -0
@@ -0,0 +1,235 @@
1
+ import {
2
+ readJsonFile,
3
+ signalProcessGroup,
4
+ system32Executable,
5
+ waitForProcessGroupExit,
6
+ writeJsonAtomic
7
+ } from "./chunk-S4XL7HOM.js";
8
+ import "./chunk-7N4J557C.js";
9
+ import "./chunk-24AXE6SP.js";
10
+
11
+ // src/job-runner.ts
12
+ import { execFile, spawn } from "child_process";
13
+ import {
14
+ appendFileSync,
15
+ existsSync,
16
+ readFileSync,
17
+ rmSync,
18
+ statSync,
19
+ truncateSync,
20
+ writeFileSync
21
+ } from "fs";
22
+ import { createHash } from "crypto";
23
+ var specPath = process.argv[2];
24
+ if (!specPath) {
25
+ process.exitCode = 2;
26
+ throw new Error("Missing persistent shell specification path.");
27
+ }
28
+ var loadedSpec = readJsonFile(specPath);
29
+ if (!loadedSpec || loadedSpec.version !== 1 || createHash("sha256").update(loadedSpec.token).digest("hex") !== loadedSpec.tokenHash) {
30
+ process.exitCode = 2;
31
+ throw new Error("Invalid persistent shell specification.");
32
+ }
33
+ if (loadedSpec.sandboxed && !loadedSpec.exec) {
34
+ process.exitCode = 2;
35
+ throw new Error(
36
+ "Invalid persistent shell specification: sandboxed is set but no sandboxed argv is present."
37
+ );
38
+ }
39
+ var spec = loadedSpec;
40
+ var TERMINATE_GRACE_MS = 1500;
41
+ var child;
42
+ var terminal = false;
43
+ var terminationInFlight = false;
44
+ var heartbeat;
45
+ var controlPoll;
46
+ var deadline;
47
+ var startedAt = Date.now();
48
+ var state = {
49
+ version: 1,
50
+ revision: 1,
51
+ id: spec.id,
52
+ command: spec.command,
53
+ title: spec.title,
54
+ workdir: spec.workdir,
55
+ status: "running",
56
+ notify: spec.notify,
57
+ sandboxed: spec.sandboxed,
58
+ runnerPid: process.pid,
59
+ tokenHash: spec.tokenHash,
60
+ startedAt,
61
+ heartbeatAt: startedAt,
62
+ outputRotationSequence: 0,
63
+ truncatedBytes: 0,
64
+ timeoutMs: spec.timeoutMs,
65
+ deadlineAt: spec.timeoutMs ? startedAt + spec.timeoutMs : void 0,
66
+ parentSessionId: spec.parentSessionId,
67
+ rootRunId: spec.rootRunId,
68
+ parentRunId: spec.parentRunId,
69
+ outputPath: spec.outputPath,
70
+ controlPath: spec.controlPath,
71
+ completionSequence: 0,
72
+ completionDeliveredSequence: 0,
73
+ completionAcknowledgedSequence: 0
74
+ };
75
+ function persist() {
76
+ state = { ...state, revision: state.revision + 1, heartbeatAt: Date.now() };
77
+ writeJsonAtomic(spec.recordPath, state);
78
+ }
79
+ function appendBounded(data) {
80
+ const chunk = Buffer.isBuffer(data) ? data : Buffer.from(String(data));
81
+ if (chunk.length === 0) return;
82
+ appendFileSync(spec.outputPath, chunk);
83
+ try {
84
+ const size = statSync(spec.outputPath).size;
85
+ if (size <= spec.maxLogBytes) return;
86
+ const retained = readFileSync(spec.outputPath).subarray(-spec.maxLogBytes);
87
+ const discarded = size - retained.length;
88
+ truncateSync(spec.outputPath, 0);
89
+ writeFileSync(spec.outputPath, retained);
90
+ state = {
91
+ ...state,
92
+ outputRotationSequence: (state.outputRotationSequence ?? 0) + 1,
93
+ truncatedBytes: (state.truncatedBytes ?? 0) + discarded
94
+ };
95
+ persist();
96
+ } catch {
97
+ }
98
+ }
99
+ function waitForChildClose(timeoutMs) {
100
+ if (!child || child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true);
101
+ const proc = child;
102
+ return new Promise((resolve) => {
103
+ const timer = setTimeout(() => {
104
+ cleanup();
105
+ resolve(proc.exitCode !== null || proc.signalCode !== null);
106
+ }, timeoutMs);
107
+ const onClose = () => {
108
+ cleanup();
109
+ resolve(true);
110
+ };
111
+ const cleanup = () => {
112
+ clearTimeout(timer);
113
+ proc.off("close", onClose);
114
+ };
115
+ proc.once("close", onClose);
116
+ });
117
+ }
118
+ async function terminateTree() {
119
+ if (!child?.pid) return true;
120
+ const pid = child.pid;
121
+ if (process.platform !== "win32") {
122
+ signalProcessGroup(child, pid, "SIGTERM");
123
+ if (await waitForProcessGroupExit(pid, TERMINATE_GRACE_MS)) return true;
124
+ signalProcessGroup(child, pid, "SIGKILL");
125
+ return waitForProcessGroupExit(pid, TERMINATE_GRACE_MS);
126
+ }
127
+ const alreadyExited = child.exitCode !== null || child.signalCode !== null;
128
+ if (alreadyExited) return true;
129
+ const confirmed = await new Promise((resolve) => {
130
+ execFile(
131
+ system32Executable("taskkill"),
132
+ ["/PID", String(pid), "/T", "/F"],
133
+ { windowsHide: true, timeout: TERMINATE_GRACE_MS },
134
+ (error) => resolve(!error)
135
+ );
136
+ });
137
+ if (!confirmed) {
138
+ try {
139
+ child.kill("SIGTERM");
140
+ } catch {
141
+ }
142
+ }
143
+ if (await waitForChildClose(TERMINATE_GRACE_MS)) return confirmed;
144
+ try {
145
+ child.kill("SIGKILL");
146
+ } catch {
147
+ return false;
148
+ }
149
+ return await waitForChildClose(TERMINATE_GRACE_MS) && confirmed;
150
+ }
151
+ function finish(status, code, signal, stopReason) {
152
+ if (terminal) return;
153
+ terminal = true;
154
+ child?.stdout?.off("data", appendBounded);
155
+ child?.stderr?.off("data", appendBounded);
156
+ state = {
157
+ ...state,
158
+ status,
159
+ exitCode: code,
160
+ signal,
161
+ stopReason,
162
+ finishedAt: Date.now(),
163
+ completionSequence: state.completionSequence + 1
164
+ };
165
+ try {
166
+ rmSync(spec.controlPath, { force: true });
167
+ rmSync(specPath, { force: true });
168
+ } catch {
169
+ }
170
+ persist();
171
+ if (heartbeat) clearInterval(heartbeat);
172
+ if (controlPoll) clearInterval(controlPoll);
173
+ if (deadline) clearTimeout(deadline);
174
+ setTimeout(() => process.exit(0), 10).unref();
175
+ }
176
+ writeFileSync(spec.outputPath, "", { encoding: "utf8", mode: 384 });
177
+ try {
178
+ const spawnBase = {
179
+ cwd: spec.workdir,
180
+ env: { ...process.env, ...spec.env },
181
+ detached: process.platform !== "win32",
182
+ stdio: ["ignore", "pipe", "pipe"]
183
+ };
184
+ child = spec.exec ? spawn(spec.exec.file, spec.exec.args, { ...spawnBase, shell: false }) : spawn(spec.effectiveCommand, { ...spawnBase, shell: true });
185
+ state = { ...state, childPid: child.pid };
186
+ persist();
187
+ } catch (error) {
188
+ appendBounded(error instanceof Error ? `${error.message}
189
+ ` : `${String(error)}
190
+ `);
191
+ finish("failed");
192
+ process.exit(1);
193
+ }
194
+ child?.stdout?.on("data", appendBounded);
195
+ child?.stderr?.on("data", appendBounded);
196
+ child?.on("error", (error) => {
197
+ appendBounded(`${error.message}
198
+ `);
199
+ if (state.status !== "stopping") finish("failed");
200
+ });
201
+ child?.on("close", (code, signal) => {
202
+ if (terminal || state.status === "stopping") return;
203
+ finish(code === 0 ? "exited" : "failed", code, signal);
204
+ });
205
+ heartbeat = setInterval(persist, 1e3);
206
+ async function requestTermination(status, reason) {
207
+ if (terminal || terminationInFlight) return;
208
+ terminationInFlight = true;
209
+ state = { ...state, status: "stopping", stopReason: reason };
210
+ persist();
211
+ try {
212
+ if (await terminateTree()) {
213
+ finish(status, child?.exitCode, child?.signalCode, reason);
214
+ } else {
215
+ appendBounded("[termination requested; process is still running; retrying]\n");
216
+ const retry = setTimeout(() => void requestTermination(status, reason), 500);
217
+ retry.unref();
218
+ }
219
+ } finally {
220
+ terminationInFlight = false;
221
+ }
222
+ }
223
+ controlPoll = setInterval(() => {
224
+ if (!existsSync(spec.controlPath) || terminal) return;
225
+ const control = readJsonFile(
226
+ spec.controlPath
227
+ );
228
+ if (control?.token !== spec.token || control.action !== "stop") return;
229
+ void requestTermination("killed", control.reason ?? "requested");
230
+ }, 250);
231
+ deadline = spec.timeoutMs ? setTimeout(() => {
232
+ void requestTermination("timed_out", "timeout");
233
+ }, spec.timeoutMs) : void 0;
234
+ persist();
235
+ //# sourceMappingURL=job-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/job-runner.ts"],"sourcesContent":["import { execFile, spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\r\nimport {\r\n appendFileSync,\r\n existsSync,\r\n readFileSync,\r\n rmSync,\r\n statSync,\r\n truncateSync,\r\n writeFileSync,\r\n} from 'node:fs';\r\nimport { createHash } from 'node:crypto';\r\nimport {\r\n readJsonFile,\r\n writeJsonAtomic,\r\n type PersistentShellSpec,\r\n type PersistentShellState,\r\n} from './jobs/persistent-store.js';\r\nimport { signalProcessGroup, waitForProcessGroupExit } from './jobs/process-tree.js';\r\nimport { system32Executable } from './system32.js';\r\n\r\nconst specPath = process.argv[2];\r\nif (!specPath) {\r\n process.exitCode = 2;\r\n throw new Error('Missing persistent shell specification path.');\r\n}\r\nconst loadedSpec = readJsonFile<PersistentShellSpec>(specPath);\r\nif (\r\n !loadedSpec ||\r\n loadedSpec.version !== 1 ||\r\n createHash('sha256').update(loadedSpec.token).digest('hex') !== loadedSpec.tokenHash\r\n) {\r\n process.exitCode = 2;\r\n throw new Error('Invalid persistent shell specification.');\r\n}\r\n// `effectiveCommand` is the raw user command now that sandboxing rides on\r\n// `exec`, so a spec claiming `sandboxed` without one would run completely\r\n// unconfined while the job panel and the [sandboxed] marker said otherwise.\r\n// Refuse to start rather than silently downgrade. The reverse is legitimate:\r\n// an unsandboxed command also carries an `exec` when the session shell is\r\n// spawned as argv (Git Bash or PowerShell on Windows).\r\nif (loadedSpec.sandboxed && !loadedSpec.exec) {\r\n process.exitCode = 2;\r\n throw new Error(\r\n 'Invalid persistent shell specification: sandboxed is set but no sandboxed argv is present.',\r\n );\r\n}\r\nconst spec: PersistentShellSpec = loadedSpec;\r\nconst TERMINATE_GRACE_MS = 1_500;\r\n\r\nlet child: ChildProcess | undefined;\r\nlet terminal = false;\r\nlet terminationInFlight = false;\r\n// These timers are assigned after the child is wired so startup failures can still call finish().\r\n// eslint-disable-next-line prefer-const\r\nlet heartbeat: NodeJS.Timeout | undefined;\r\n// eslint-disable-next-line prefer-const\r\nlet controlPoll: NodeJS.Timeout | undefined;\r\n// eslint-disable-next-line prefer-const\r\nlet deadline: NodeJS.Timeout | undefined;\r\nconst startedAt = Date.now();\r\nlet state: PersistentShellState = {\r\n version: 1,\r\n revision: 1,\r\n id: spec.id,\r\n command: spec.command,\r\n title: spec.title,\r\n workdir: spec.workdir,\r\n status: 'running',\r\n notify: spec.notify,\r\n sandboxed: spec.sandboxed,\r\n runnerPid: process.pid,\r\n tokenHash: spec.tokenHash,\r\n startedAt,\r\n heartbeatAt: startedAt,\r\n outputRotationSequence: 0,\r\n truncatedBytes: 0,\r\n timeoutMs: spec.timeoutMs,\r\n deadlineAt: spec.timeoutMs ? startedAt + spec.timeoutMs : undefined,\r\n parentSessionId: spec.parentSessionId,\r\n rootRunId: spec.rootRunId,\r\n parentRunId: spec.parentRunId,\r\n outputPath: spec.outputPath,\r\n controlPath: spec.controlPath,\r\n completionSequence: 0,\r\n completionDeliveredSequence: 0,\r\n completionAcknowledgedSequence: 0,\r\n};\r\n\r\nfunction persist(): void {\r\n state = { ...state, revision: state.revision + 1, heartbeatAt: Date.now() };\r\n writeJsonAtomic(spec.recordPath, state);\r\n}\r\n\r\nfunction appendBounded(data: unknown): void {\r\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(String(data));\r\n if (chunk.length === 0) return;\r\n appendFileSync(spec.outputPath, chunk);\r\n try {\r\n const size = statSync(spec.outputPath).size;\r\n if (size <= spec.maxLogBytes) return;\r\n const retained = readFileSync(spec.outputPath).subarray(-spec.maxLogBytes);\r\n const discarded = size - retained.length;\r\n truncateSync(spec.outputPath, 0);\r\n writeFileSync(spec.outputPath, retained);\r\n state = {\r\n ...state,\r\n outputRotationSequence: (state.outputRotationSequence ?? 0) + 1,\r\n truncatedBytes: (state.truncatedBytes ?? 0) + discarded,\r\n };\r\n persist();\r\n } catch {\r\n // Output retention is best effort; lifecycle state remains authoritative.\r\n }\r\n}\r\n\r\nfunction waitForChildClose(timeoutMs: number): Promise<boolean> {\r\n if (!child || child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true);\r\n const proc = child;\r\n return new Promise((resolve) => {\r\n const timer = setTimeout(() => {\r\n cleanup();\r\n resolve(proc.exitCode !== null || proc.signalCode !== null);\r\n }, timeoutMs);\r\n const onClose = () => {\r\n cleanup();\r\n resolve(true);\r\n };\r\n const cleanup = () => {\r\n clearTimeout(timer);\r\n proc.off('close', onClose);\r\n };\r\n proc.once('close', onClose);\r\n });\r\n}\r\n\r\nasync function terminateTree(): Promise<boolean> {\r\n if (!child?.pid) return true;\r\n const pid = child.pid;\r\n if (process.platform !== 'win32') {\r\n // The direct child is the `sh -c` wrapper, which dies from SIGTERM even when the worker it\r\n // forked ignores it. Escalate on whether the group still holds processes, never on the\r\n // wrapper's own exit, or the surviving worker is recorded as killed while it keeps running.\r\n signalProcessGroup(child, pid, 'SIGTERM');\r\n if (await waitForProcessGroupExit(pid, TERMINATE_GRACE_MS)) return true;\r\n signalProcessGroup(child, pid, 'SIGKILL');\r\n return waitForProcessGroupExit(pid, TERMINATE_GRACE_MS);\r\n }\r\n // `taskkill /T /F` covers the tree, so the direct child's close speaks for the tree only when\r\n // taskkill actually ran.\r\n const alreadyExited = child.exitCode !== null || child.signalCode !== null;\r\n if (alreadyExited) return true;\r\n const confirmed = await new Promise<boolean>((resolve) => {\r\n execFile(\r\n system32Executable('taskkill'),\r\n ['/PID', String(pid), '/T', '/F'],\r\n { windowsHide: true, timeout: TERMINATE_GRACE_MS },\r\n (error) => resolve(!error),\r\n );\r\n });\r\n if (!confirmed) {\r\n try {\r\n child.kill('SIGTERM');\r\n } catch {\r\n // The process may have exited between taskkill and the fallback.\r\n }\r\n }\r\n if (await waitForChildClose(TERMINATE_GRACE_MS)) return confirmed;\r\n try {\r\n child.kill('SIGKILL');\r\n } catch {\r\n return false;\r\n }\r\n return (await waitForChildClose(TERMINATE_GRACE_MS)) && confirmed;\r\n}\r\n\r\nfunction finish(\r\n status: 'exited' | 'failed' | 'killed' | 'timed_out',\r\n code?: number | null,\r\n signal?: NodeJS.Signals | string | null,\r\n stopReason?: string,\r\n): void {\r\n if (terminal) return;\r\n terminal = true;\r\n // Stop capturing output at terminality: once the record reads terminal, the\r\n // manager may dismiss the job and delete its files, so a straggling stream\r\n // chunk must not re-create the log after that point.\r\n child?.stdout?.off('data', appendBounded);\r\n child?.stderr?.off('data', appendBounded);\r\n state = {\r\n ...state,\r\n status,\r\n exitCode: code,\r\n signal,\r\n stopReason,\r\n finishedAt: Date.now(),\r\n completionSequence: state.completionSequence + 1,\r\n };\r\n // Remove the runner-owned control/spec files BEFORE persisting the terminal\r\n // record. The manager treats an observable terminal record as \"stop\r\n // complete\" and immediately expects these files to be gone, so the removals\r\n // must happen-before the rename that publishes the terminal state — the old\r\n // order left a window where a preempted runner had published \"killed\" while\r\n // the spec file still existed.\r\n try {\r\n rmSync(spec.controlPath, { force: true });\r\n rmSync(specPath, { force: true });\r\n } catch {\r\n // Best effort: a leaked control/spec file is cleaned up by the manager's\r\n // lost-job reconciliation or dismissal, whereas skipping the terminal\r\n // persist below would strand the job as \"stopping\" forever.\r\n }\r\n persist();\r\n if (heartbeat) clearInterval(heartbeat);\r\n if (controlPoll) clearInterval(controlPoll);\r\n if (deadline) clearTimeout(deadline);\r\n setTimeout(() => process.exit(0), 10).unref();\r\n}\r\n\r\nwriteFileSync(spec.outputPath, '', { encoding: 'utf8', mode: 0o600 });\r\ntry {\r\n const spawnBase: SpawnOptions = {\r\n cwd: spec.workdir,\r\n env: { ...process.env, ...spec.env },\r\n detached: process.platform !== 'win32',\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n };\r\n // Sandboxed specs carry an argv; only unsandboxed ones go through a shell.\r\n child = spec.exec\r\n ? spawn(spec.exec.file, spec.exec.args, { ...spawnBase, shell: false })\r\n : spawn(spec.effectiveCommand, { ...spawnBase, shell: true });\r\n state = { ...state, childPid: child.pid };\r\n persist();\r\n} catch (error) {\r\n appendBounded(error instanceof Error ? `${error.message}\\n` : `${String(error)}\\n`);\r\n finish('failed');\r\n process.exit(1);\r\n}\r\n\r\nchild?.stdout?.on('data', appendBounded);\r\nchild?.stderr?.on('data', appendBounded);\r\nchild?.on('error', (error) => {\r\n appendBounded(`${error.message}\\n`);\r\n if (state.status !== 'stopping') finish('failed');\r\n});\r\nchild?.on('close', (code, signal) => {\r\n if (terminal || state.status === 'stopping') return;\r\n finish(code === 0 ? 'exited' : 'failed', code, signal);\r\n});\r\n\r\nheartbeat = setInterval(persist, 1_000);\r\nasync function requestTermination(status: 'killed' | 'timed_out', reason: string): Promise<void> {\r\n if (terminal || terminationInFlight) return;\r\n terminationInFlight = true;\r\n state = { ...state, status: 'stopping', stopReason: reason };\r\n persist();\r\n try {\r\n if (await terminateTree()) {\r\n finish(status, child?.exitCode, child?.signalCode, reason);\r\n } else {\r\n appendBounded('[termination requested; process is still running; retrying]\\n');\r\n const retry = setTimeout(() => void requestTermination(status, reason), 500);\r\n retry.unref();\r\n }\r\n } finally {\r\n terminationInFlight = false;\r\n }\r\n}\r\n\r\ncontrolPoll = setInterval(() => {\r\n if (!existsSync(spec.controlPath) || terminal) return;\r\n const control = readJsonFile<{ token?: string; action?: string; reason?: string }>(\r\n spec.controlPath,\r\n );\r\n if (control?.token !== spec.token || control.action !== 'stop') return;\r\n void requestTermination('killed', control.reason ?? 'requested');\r\n}, 250);\r\ndeadline = spec.timeoutMs\r\n ? setTimeout(() => {\r\n void requestTermination('timed_out', 'timeout');\r\n }, spec.timeoutMs)\r\n : undefined;\r\n\r\npersist();\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ import {
2
+ discoverCommands,
3
+ generateCommandListing,
4
+ resolveCommandBody
5
+ } from "./chunk-E75IPST5.js";
6
+ import "./chunk-5KLVH3PY.js";
7
+ import "./chunk-DGMQEI6Y.js";
8
+ import "./chunk-574C6MQR.js";
9
+ import "./chunk-24AXE6SP.js";
10
+ export {
11
+ discoverCommands,
12
+ generateCommandListing,
13
+ resolveCommandBody
14
+ };
15
+ //# sourceMappingURL=loader-4IDS2RIE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,18 @@
1
+ import {
2
+ evaluateMcpServerApproval,
3
+ mcpServerFingerprint,
4
+ mcpServersToRecord,
5
+ partitionMcpServersByApproval,
6
+ persistMcpProjectServerChoice
7
+ } from "./chunk-URLQAXTR.js";
8
+ import "./chunk-DGMQEI6Y.js";
9
+ import "./chunk-574C6MQR.js";
10
+ import "./chunk-24AXE6SP.js";
11
+ export {
12
+ evaluateMcpServerApproval,
13
+ mcpServerFingerprint,
14
+ mcpServersToRecord,
15
+ partitionMcpServersByApproval,
16
+ persistMcpProjectServerChoice
17
+ };
18
+ //# sourceMappingURL=mcp-approvals-WNPH65BK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,16 @@
1
+ import {
2
+ consoleMcpDiagnosticSink,
3
+ formatMcpServerCommand,
4
+ isValidMcpServerName,
5
+ loadMcpConfig,
6
+ resolveMcpServerList
7
+ } from "./chunk-3U5IBM24.js";
8
+ import "./chunk-24AXE6SP.js";
9
+ export {
10
+ consoleMcpDiagnosticSink,
11
+ formatMcpServerCommand,
12
+ isValidMcpServerName,
13
+ loadMcpConfig,
14
+ resolveMcpServerList
15
+ };
16
+ //# sourceMappingURL=mcp-config-YIJIES2C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,27 @@
1
+ import {
2
+ FileModelWindowStore,
3
+ LEARNED_WINDOW_SAFETY_MARGIN,
4
+ MIN_LEARNED_CONTEXT_WINDOW,
5
+ MODEL_WINDOW_STORE_VERSION,
6
+ MemoryModelWindowStore,
7
+ createModelWindowStore,
8
+ defaultModelWindowStorePath,
9
+ ratchetModelWindow,
10
+ readModelWindowStore,
11
+ writeModelWindowStore
12
+ } from "./chunk-CAGOB2N4.js";
13
+ import "./chunk-574C6MQR.js";
14
+ import "./chunk-24AXE6SP.js";
15
+ export {
16
+ FileModelWindowStore,
17
+ LEARNED_WINDOW_SAFETY_MARGIN,
18
+ MIN_LEARNED_CONTEXT_WINDOW,
19
+ MODEL_WINDOW_STORE_VERSION,
20
+ MemoryModelWindowStore,
21
+ createModelWindowStore,
22
+ defaultModelWindowStorePath,
23
+ ratchetModelWindow,
24
+ readModelWindowStore,
25
+ writeModelWindowStore
26
+ };
27
+ //# sourceMappingURL=model-window-store-YFVNXCIP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,10 @@
1
+ import {
2
+ chatCompletionStream,
3
+ convertTools
4
+ } from "./chunk-GTVB4HEM.js";
5
+ import "./chunk-7N4J557C.js";
6
+ export {
7
+ chatCompletionStream,
8
+ convertTools
9
+ };
10
+ //# sourceMappingURL=openai-compatible-RHZUBHV6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,14 @@
1
+ import {
2
+ evaluateProjectAllowRule,
3
+ partitionProjectAllowRules,
4
+ persistProjectAllowRuleChoice
5
+ } from "./chunk-WKBO5R4O.js";
6
+ import "./chunk-DGMQEI6Y.js";
7
+ import "./chunk-574C6MQR.js";
8
+ import "./chunk-24AXE6SP.js";
9
+ export {
10
+ evaluateProjectAllowRule,
11
+ partitionProjectAllowRules,
12
+ persistProjectAllowRuleChoice
13
+ };
14
+ //# sourceMappingURL=permission-approvals-76PVREFD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,28 @@
1
+ import {
2
+ evaluatePermission,
3
+ evaluatePermissionDetail,
4
+ hasAdjudicationPolicy,
5
+ parseRule,
6
+ permissionResultOf,
7
+ permissionRuleForToolCall,
8
+ permissionRuleLadder,
9
+ permissionRuleMatchesCall,
10
+ permissionRuleOf,
11
+ primaryArgForRule
12
+ } from "./chunk-MAQXCUR4.js";
13
+ import "./chunk-5GDZ22YP.js";
14
+ import "./chunk-EYGD6X2T.js";
15
+ import "./chunk-24AXE6SP.js";
16
+ export {
17
+ evaluatePermission,
18
+ evaluatePermissionDetail,
19
+ hasAdjudicationPolicy,
20
+ parseRule,
21
+ permissionResultOf,
22
+ permissionRuleForToolCall,
23
+ permissionRuleLadder,
24
+ permissionRuleMatchesCall,
25
+ permissionRuleOf,
26
+ primaryArgForRule
27
+ };
28
+ //# sourceMappingURL=permissions-CHCKTFQQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,23 @@
1
+ import {
2
+ buildSandboxExecution,
3
+ createSandbox,
4
+ hasDomainPolicy,
5
+ matchesExcludedCommand,
6
+ sandboxBackendAvailable,
7
+ sandboxCoverage,
8
+ sandboxPolicySummary,
9
+ unbindablePaths,
10
+ unsandboxedRefusalMessage
11
+ } from "./chunk-EYGD6X2T.js";
12
+ export {
13
+ buildSandboxExecution,
14
+ createSandbox,
15
+ hasDomainPolicy,
16
+ matchesExcludedCommand,
17
+ sandboxBackendAvailable,
18
+ sandboxCoverage,
19
+ sandboxPolicySummary,
20
+ unbindablePaths,
21
+ unsandboxedRefusalMessage
22
+ };
23
+ //# sourceMappingURL=sandbox-CNVAQDA7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,136 @@
1
+ import {
2
+ createDefaultRegistry,
3
+ runAgentLoop
4
+ } from "./chunk-XIFFTATU.js";
5
+ import "./chunk-A7JLBK2Y.js";
6
+ import "./chunk-KWHQ2XXL.js";
7
+ import "./chunk-VZQXX3WW.js";
8
+ import "./chunk-E75IPST5.js";
9
+ import "./chunk-5KLVH3PY.js";
10
+ import "./chunk-3U5IBM24.js";
11
+ import "./chunk-MAQXCUR4.js";
12
+ import {
13
+ getPrimaryArg,
14
+ toolResultErrorMessage,
15
+ toolResultSucceeded
16
+ } from "./chunk-5GDZ22YP.js";
17
+ import "./chunk-EYGD6X2T.js";
18
+ import "./chunk-GTVB4HEM.js";
19
+ import "./chunk-S4XL7HOM.js";
20
+ import "./chunk-7N4J557C.js";
21
+ import "./chunk-BTZZUCLI.js";
22
+ import "./chunk-WKBO5R4O.js";
23
+ import "./chunk-ANSJPYGI.js";
24
+ import "./chunk-DGMQEI6Y.js";
25
+ import "./chunk-5RDRAO4B.js";
26
+ import "./chunk-CAGOB2N4.js";
27
+ import "./chunk-574C6MQR.js";
28
+ import "./chunk-24AXE6SP.js";
29
+
30
+ // src/cli/scrollback.ts
31
+ import { createInterface } from "readline/promises";
32
+ function formatToolCall(call) {
33
+ const primary = getPrimaryArg(call.arguments);
34
+ return `
35
+ [tool] ${call.name}${primary ? ` ${primary}` : ""}
36
+ `;
37
+ }
38
+ function formatToolResult(result, call) {
39
+ const success = toolResultSucceeded(result);
40
+ const label = success ? "[OK]" : "[ERR]";
41
+ const name = call?.name ?? "tool";
42
+ const primary = call ? getPrimaryArg(call.arguments) : "";
43
+ const detail = success ? result.content : toolResultErrorMessage(result) ?? "";
44
+ return `${label} ${name}${primary ? ` ${primary}` : ""}${detail ? `
45
+ ${detail.trimEnd()}` : ""}
46
+ `;
47
+ }
48
+ async function runScrollbackSession(config, options) {
49
+ const output = options.output ?? process.stdout;
50
+ const registry = options.registry ?? createDefaultRegistry({ agents: config.settings.agents.mode !== "off" });
51
+ const runLoop = options.runLoop ?? runAgentLoop;
52
+ const history = [];
53
+ const toolCalls = /* @__PURE__ */ new Map();
54
+ const close = createPromptReader(options);
55
+ output.write("Book scrollback mode. Terminal scrollback owns history. Type /exit to quit.\n\n");
56
+ try {
57
+ while (true) {
58
+ const prompt = await close.read("You> ");
59
+ if (prompt === null) break;
60
+ const trimmed = prompt.trim();
61
+ if (!trimmed) continue;
62
+ if (trimmed === "/exit") break;
63
+ if (trimmed === "/clear") {
64
+ history.length = 0;
65
+ output.write("[cleared]\n");
66
+ continue;
67
+ }
68
+ output.write("\nBook\n");
69
+ const updated = await runLoop(
70
+ config,
71
+ registry,
72
+ prompt,
73
+ history,
74
+ {
75
+ onText: (text) => output.write(text),
76
+ onReasoning: (text) => output.write(`
77
+ [thinking] ${text}
78
+ `),
79
+ onToolCall: (call) => {
80
+ toolCalls.set(call.id, call);
81
+ output.write(formatToolCall(call));
82
+ },
83
+ onToolResult: (result) => output.write(formatToolResult(result, toolCalls.get(result.toolCallId))),
84
+ onError: (error) => output.write(`
85
+ [error] ${error}
86
+ `),
87
+ onTurnStart: (turn) => {
88
+ if (turn > 1) output.write(`
89
+ Book turn ${turn}
90
+ `);
91
+ },
92
+ onDone: () => output.write("\n"),
93
+ onPermissionRequired: (call) => askPermission(call, close.read, output),
94
+ onUsage: () => {
95
+ }
96
+ },
97
+ options.mode
98
+ );
99
+ history.length = 0;
100
+ history.push(...updated);
101
+ output.write("\n");
102
+ }
103
+ } finally {
104
+ close.close();
105
+ }
106
+ return history;
107
+ }
108
+ function createPromptReader(options) {
109
+ if (options.readPrompt)
110
+ return { read: () => options.readPrompt?.() ?? Promise.resolve(null), close: () => {
111
+ } };
112
+ const rl = createInterface({
113
+ input: options.input ?? process.stdin,
114
+ output: process.stdout
115
+ });
116
+ return {
117
+ read: (prompt = "You> ") => rl.question(prompt),
118
+ close: () => rl.close()
119
+ };
120
+ }
121
+ async function askPermission(call, read, output) {
122
+ output.write(
123
+ `[permission] ${call.name}${getPrimaryArg(call.arguments) ? ` ${getPrimaryArg(call.arguments)}` : ""}
124
+ Allow? [y]es/[n]o/[a]lways: `
125
+ );
126
+ const answer = (await read(""))?.trim().toLowerCase();
127
+ if (answer === "a" || answer === "always") return "always";
128
+ if (answer === "y" || answer === "yes") return "allow";
129
+ return "deny";
130
+ }
131
+ export {
132
+ formatToolCall,
133
+ formatToolResult,
134
+ runScrollbackSession
135
+ };
136
+ //# sourceMappingURL=scrollback-LUKLIBVI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/scrollback.ts"],"sourcesContent":["import { createInterface } from 'node:readline/promises';\r\nimport type { AgentConfig, PermissionMode } from '../types/runtime.js';\r\nimport type { AgentLoopCallbacks } from '../types/providers.js';\r\nimport type { Message } from '../types/messages.js';\r\nimport type { ToolCall, ToolResult } from '../types/tools.js';\r\nimport { runAgentLoop } from '../agent/loop.js';\r\nimport { createDefaultRegistry, type ToolRegistry } from '../tools/registry.js';\r\nimport { getPrimaryArg } from '../tools/primary-arg.js';\r\nimport { toolResultErrorMessage, toolResultSucceeded } from '../tools/result.js';\r\n\r\nexport interface ScrollbackOptions {\r\n mode: PermissionMode;\r\n input?: NodeJS.ReadableStream;\r\n output?: { write: (s: string) => boolean | void };\r\n readPrompt?: () => Promise<string | null>;\r\n runLoop?: typeof runAgentLoop;\r\n registry?: ToolRegistry;\r\n}\r\n\r\nexport function formatToolCall(call: ToolCall): string {\r\n const primary = getPrimaryArg(call.arguments);\r\n return `\\n[tool] ${call.name}${primary ? ` ${primary}` : ''}\\n`;\r\n}\r\n\r\nexport function formatToolResult(result: ToolResult, call?: ToolCall): string {\r\n const success = toolResultSucceeded(result);\r\n const label = success ? '[OK]' : '[ERR]';\r\n const name = call?.name ?? 'tool';\r\n const primary = call ? getPrimaryArg(call.arguments) : '';\r\n const detail = success ? result.content : (toolResultErrorMessage(result) ?? '');\r\n return `${label} ${name}${primary ? ` ${primary}` : ''}${detail ? `\\n${detail.trimEnd()}` : ''}\\n`;\r\n}\r\n\r\nexport async function runScrollbackSession(\r\n config: AgentConfig,\r\n options: ScrollbackOptions,\r\n): Promise<Message[]> {\r\n const output = options.output ?? process.stdout;\r\n const registry =\r\n options.registry ?? createDefaultRegistry({ agents: config.settings.agents.mode !== 'off' });\r\n const runLoop = options.runLoop ?? runAgentLoop;\r\n const history: Message[] = [];\r\n const toolCalls = new Map<string, ToolCall>();\r\n const close = createPromptReader(options);\r\n\r\n output.write('Book scrollback mode. Terminal scrollback owns history. Type /exit to quit.\\n\\n');\r\n\r\n try {\r\n while (true) {\r\n const prompt = await close.read('You> ');\r\n if (prompt === null) break;\r\n const trimmed = prompt.trim();\r\n if (!trimmed) continue;\r\n if (trimmed === '/exit') break;\r\n if (trimmed === '/clear') {\r\n history.length = 0;\r\n output.write('[cleared]\\n');\r\n continue;\r\n }\r\n\r\n output.write('\\nBook\\n');\r\n const updated = await runLoop(\r\n config,\r\n registry,\r\n prompt,\r\n history,\r\n {\r\n onText: (text) => output.write(text),\r\n onReasoning: (text) => output.write(`\\n[thinking] ${text}\\n`),\r\n onToolCall: (call) => {\r\n toolCalls.set(call.id, call);\r\n output.write(formatToolCall(call));\r\n },\r\n onToolResult: (result) =>\r\n output.write(formatToolResult(result, toolCalls.get(result.toolCallId))),\r\n onError: (error) => output.write(`\\n[error] ${error}\\n`),\r\n onTurnStart: (turn) => {\r\n if (turn > 1) output.write(`\\nBook turn ${turn}\\n`);\r\n },\r\n onDone: () => output.write('\\n'),\r\n onPermissionRequired: (call) => askPermission(call, close.read, output),\r\n onUsage: () => {},\r\n } satisfies AgentLoopCallbacks,\r\n options.mode,\r\n );\r\n\r\n history.length = 0;\r\n history.push(...updated);\r\n output.write('\\n');\r\n }\r\n } finally {\r\n close.close();\r\n }\r\n\r\n return history;\r\n}\r\n\r\nfunction createPromptReader(options: ScrollbackOptions): {\r\n read: (prompt?: string) => Promise<string | null>;\r\n close: () => void;\r\n} {\r\n if (options.readPrompt)\r\n return { read: () => options.readPrompt?.() ?? Promise.resolve(null), close: () => {} };\r\n\r\n const rl = createInterface({\r\n input: options.input ?? process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return {\r\n read: (prompt = 'You> ') => rl.question(prompt),\r\n close: () => rl.close(),\r\n };\r\n}\r\n\r\nasync function askPermission(\r\n call: ToolCall,\r\n read: (prompt?: string) => Promise<string | null>,\r\n output: { write: (s: string) => boolean | void },\r\n): Promise<'allow' | 'deny' | 'always'> {\r\n output.write(\r\n `[permission] ${call.name}${getPrimaryArg(call.arguments) ? ` ${getPrimaryArg(call.arguments)}` : ''}\\nAllow? [y]es/[n]o/[a]lways: `,\r\n );\r\n const answer = (await read(''))?.trim().toLowerCase();\r\n if (answer === 'a' || answer === 'always') return 'always';\r\n if (answer === 'y' || answer === 'yes') return 'allow';\r\n return 'deny';\r\n}\r\n"],"mappings":"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","names":[]}