agent-nuvira 3.5.0 → 3.5.2

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 (134) hide show
  1. package/README.md +13 -13
  2. package/dist/agent-sdk/src/scaffold.d.ts +1 -1
  3. package/dist/agent-sdk/src/scaffold.js +1 -1
  4. package/dist/cli/chat.d.ts +159 -6
  5. package/dist/cli/chat.d.ts.map +1 -1
  6. package/dist/cli/chat.js +583 -35
  7. package/dist/cli/chat.js.map +1 -1
  8. package/dist/cli/duties.d.ts +11 -0
  9. package/dist/cli/duties.d.ts.map +1 -1
  10. package/dist/cli/duties.js +31 -0
  11. package/dist/cli/duties.js.map +1 -1
  12. package/dist/cli/edit.d.ts.map +1 -1
  13. package/dist/cli/edit.js +3 -2
  14. package/dist/cli/edit.js.map +1 -1
  15. package/dist/cli/execute.d.ts.map +1 -1
  16. package/dist/cli/execute.js +10 -3
  17. package/dist/cli/execute.js.map +1 -1
  18. package/dist/cli/loop-executor.d.ts +16 -0
  19. package/dist/cli/loop-executor.d.ts.map +1 -1
  20. package/dist/cli/loop-executor.js +9 -0
  21. package/dist/cli/loop-executor.js.map +1 -1
  22. package/dist/cli/models.d.ts.map +1 -1
  23. package/dist/cli/models.js +55 -0
  24. package/dist/cli/models.js.map +1 -1
  25. package/dist/cli/plan.d.ts.map +1 -1
  26. package/dist/cli/plan.js +18 -6
  27. package/dist/cli/plan.js.map +1 -1
  28. package/dist/cli/run.d.ts.map +1 -1
  29. package/dist/cli/run.js +23 -8
  30. package/dist/cli/run.js.map +1 -1
  31. package/dist/cli/ui/answer-view.d.ts +38 -0
  32. package/dist/cli/ui/answer-view.d.ts.map +1 -0
  33. package/dist/cli/ui/answer-view.js +72 -0
  34. package/dist/cli/ui/answer-view.js.map +1 -0
  35. package/dist/cli/ui/cards.d.ts +61 -0
  36. package/dist/cli/ui/cards.d.ts.map +1 -0
  37. package/dist/cli/ui/cards.js +141 -0
  38. package/dist/cli/ui/cards.js.map +1 -0
  39. package/dist/cli/ui/highlight.d.ts +32 -0
  40. package/dist/cli/ui/highlight.d.ts.map +1 -0
  41. package/dist/cli/ui/highlight.js +261 -0
  42. package/dist/cli/ui/highlight.js.map +1 -0
  43. package/dist/cli/ui/index.d.ts +21 -0
  44. package/dist/cli/ui/index.d.ts.map +1 -0
  45. package/dist/cli/ui/index.js +14 -0
  46. package/dist/cli/ui/index.js.map +1 -0
  47. package/dist/cli/ui/markdown.d.ts +90 -0
  48. package/dist/cli/ui/markdown.d.ts.map +1 -0
  49. package/dist/cli/ui/markdown.js +292 -0
  50. package/dist/cli/ui/markdown.js.map +1 -0
  51. package/dist/cli/ui/presenter.d.ts +147 -0
  52. package/dist/cli/ui/presenter.d.ts.map +1 -0
  53. package/dist/cli/ui/presenter.js +313 -0
  54. package/dist/cli/ui/presenter.js.map +1 -0
  55. package/dist/cli/ui/render.d.ts +52 -0
  56. package/dist/cli/ui/render.d.ts.map +1 -0
  57. package/dist/cli/ui/render.js +384 -0
  58. package/dist/cli/ui/render.js.map +1 -0
  59. package/dist/cli/ui/theme.d.ts +36 -0
  60. package/dist/cli/ui/theme.d.ts.map +1 -0
  61. package/dist/cli/ui/theme.js +92 -0
  62. package/dist/cli/ui/theme.js.map +1 -0
  63. package/dist/inference/model-entitlement.d.ts +10 -0
  64. package/dist/inference/model-entitlement.d.ts.map +1 -1
  65. package/dist/inference/model-entitlement.js +1 -1
  66. package/dist/inference/model-entitlement.js.map +1 -1
  67. package/dist/learning/auto-router.d.ts +38 -1
  68. package/dist/learning/auto-router.d.ts.map +1 -1
  69. package/dist/learning/auto-router.js +139 -8
  70. package/dist/learning/auto-router.js.map +1 -1
  71. package/dist/learning/duty-lease.d.ts +163 -0
  72. package/dist/learning/duty-lease.d.ts.map +1 -0
  73. package/dist/learning/duty-lease.js +301 -0
  74. package/dist/learning/duty-lease.js.map +1 -0
  75. package/dist/learning/model-first-router.d.ts +14 -0
  76. package/dist/learning/model-first-router.d.ts.map +1 -1
  77. package/dist/learning/model-first-router.js +54 -17
  78. package/dist/learning/model-first-router.js.map +1 -1
  79. package/dist/learning/model-warmup.d.ts +92 -0
  80. package/dist/learning/model-warmup.d.ts.map +1 -1
  81. package/dist/learning/model-warmup.js +137 -2
  82. package/dist/learning/model-warmup.js.map +1 -1
  83. package/dist/learning/reasoning-trace.d.ts +21 -1
  84. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  85. package/dist/learning/reasoning-trace.js.map +1 -1
  86. package/dist/learning/resilient-call.d.ts.map +1 -1
  87. package/dist/learning/resilient-call.js +70 -4
  88. package/dist/learning/resilient-call.js.map +1 -1
  89. package/dist/learning/retry-patience.d.ts +139 -0
  90. package/dist/learning/retry-patience.d.ts.map +1 -0
  91. package/dist/learning/retry-patience.js +169 -0
  92. package/dist/learning/retry-patience.js.map +1 -0
  93. package/dist/tools/process-liveness.d.ts +227 -0
  94. package/dist/tools/process-liveness.d.ts.map +1 -0
  95. package/dist/tools/process-liveness.js +486 -0
  96. package/dist/tools/process-liveness.js.map +1 -0
  97. package/dist/tools/registry.d.ts.map +1 -1
  98. package/dist/tools/registry.js +4 -2
  99. package/dist/tools/registry.js.map +1 -1
  100. package/dist/tools/run-registry.d.ts +282 -0
  101. package/dist/tools/run-registry.d.ts.map +1 -0
  102. package/dist/tools/run-registry.js +544 -0
  103. package/dist/tools/run-registry.js.map +1 -0
  104. package/dist/tools/run-terminal.d.ts +100 -2
  105. package/dist/tools/run-terminal.d.ts.map +1 -1
  106. package/dist/tools/run-terminal.js +841 -67
  107. package/dist/tools/run-terminal.js.map +1 -1
  108. package/dist/tools/tool-loop.d.ts +87 -3
  109. package/dist/tools/tool-loop.d.ts.map +1 -1
  110. package/dist/tools/tool-loop.js +375 -6
  111. package/dist/tools/tool-loop.js.map +1 -1
  112. package/dist/web-dashboard/chat-console.d.ts +25 -0
  113. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  114. package/dist/web-dashboard/chat-console.js +16 -1
  115. package/dist/web-dashboard/chat-console.js.map +1 -1
  116. package/dist/web-dashboard/server.d.ts.map +1 -1
  117. package/dist/web-dashboard/server.js +7 -1
  118. package/dist/web-dashboard/server.js.map +1 -1
  119. package/dist/web-dashboard/src/types.d.ts +32 -1
  120. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  121. package/dist/web-dashboard/task-registry.d.ts +98 -0
  122. package/dist/web-dashboard/task-registry.d.ts.map +1 -0
  123. package/dist/web-dashboard/task-registry.js +159 -0
  124. package/dist/web-dashboard/task-registry.js.map +1 -0
  125. package/dist/web-dashboard/task-runner.d.ts +155 -5
  126. package/dist/web-dashboard/task-runner.d.ts.map +1 -1
  127. package/dist/web-dashboard/task-runner.js +505 -14
  128. package/dist/web-dashboard/task-runner.js.map +1 -1
  129. package/package.json +4 -1
  130. package/src/web-dashboard/public/assets/index-DycGBWwy.js +236 -0
  131. package/src/web-dashboard/public/assets/index-DycGBWwy.js.map +1 -0
  132. package/src/web-dashboard/public/index.html +1 -1
  133. package/src/web-dashboard/public/assets/index-yky0dw5e.js +0 -236
  134. package/src/web-dashboard/public/assets/index-yky0dw5e.js.map +0 -1
@@ -0,0 +1,544 @@
1
+ /**
2
+ * The durable half of the monitored-run registry.
3
+ *
4
+ * WHY THIS EXISTS. Monitoring a long command in memory (see `run-terminal.ts`)
5
+ * fixed the killing, but it made the record as fragile as the process holding it:
6
+ * a CLI exit, a dashboard restart, a crash — all normal — and a command that is
7
+ * still running becomes unpollable. Nothing is left to follow it with, its real
8
+ * exit code is never collected, and the turn that started it can only re-run the
9
+ * work (wrong) or guess at the outcome. Two facts have to outlive the process:
10
+ *
11
+ * what is running — id, command, cwd, pid, start time, and how it ended;
12
+ * what it said — its output, from the FIRST byte as well as the last.
13
+ *
14
+ * The second one is why there is a spool file and not just a record. The
15
+ * in-memory buffer is a bounded TAIL (a 40MB build log must not be held), so the
16
+ * beginning of a long run was simply gone — the model could read the end of a
17
+ * failure and not the line that explained it. The spool keeps a bounded HEAD and
18
+ * a bounded TAIL (rewritten whole, so the file never grows past the cap), which
19
+ * is the part a diagnosis actually uses.
20
+ *
21
+ * Everything here is best-effort and must never break a turn: a missing, unreadable
22
+ * or corrupt registry is an empty registry, never a throw.
23
+ */
24
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
25
+ import { join } from 'node:path';
26
+ import { resolveNuviraDataPath } from '../config/paths.js';
27
+ import { lookupProcess } from './process-liveness.js';
28
+ /** The full-log cap, with an env override read at construction (test-friendly). */
29
+ function envFullBytes() {
30
+ const raw = Number.parseInt(String(process.env.NUVIRA_RUN_LOG_FULL_BYTES ?? '').trim(), 10);
31
+ return Number.isFinite(raw) && raw >= 0 ? raw : SPOOL_FULL_BYTES;
32
+ }
33
+ /** How much of the BEGINNING of a run's output is kept. */
34
+ export const SPOOL_HEAD_BYTES = 32_768;
35
+ /** How much of the END of a run's output is kept. */
36
+ export const SPOOL_TAIL_BYTES = 32_768;
37
+ /**
38
+ * How much of a run's output is kept IN FULL before the window collapses to
39
+ * head+tail.
40
+ *
41
+ * WHY THIS EXISTS. A bounded window throws away the MIDDLE of a long run, and
42
+ * that is exactly where a failure explains itself — so the honest trade is to
43
+ * keep everything while everything is small. Most runs never reach this cap, so
44
+ * the common case stops losing anything at all; past it the file collapses to
45
+ * head+tail (with the dropped count stated), which keeps the guarantee that a
46
+ * chatty command cannot fill the disk. `NUVIRA_RUN_LOG_FULL_BYTES=0` restores
47
+ * "always a bounded window".
48
+ */
49
+ export const SPOOL_FULL_BYTES = 2_000_000;
50
+ /** Pending appends are flushed once they reach this, so memory stays bounded. */
51
+ const SPOOL_PENDING_BYTES = 262_144;
52
+ /** Bounded history: the registry cannot grow with the session. */
53
+ const MAX_PERSISTED_RUNS = 30;
54
+ /** How often an in-flight spool is written out (a crash loses at most this much). */
55
+ export const SPOOL_FLUSH_MS = 2_000;
56
+ /** `<config dir>/run-terminal` — isolated by `$NUVIRA_CONFIG_DIR`, like the rest. */
57
+ export function runsDir() {
58
+ return resolveNuviraDataPath('run-terminal');
59
+ }
60
+ export function spoolPath(id) {
61
+ return join(runsDir(), 'out', `${id}.log`);
62
+ }
63
+ function registryPath() {
64
+ return join(runsDir(), 'runs.json');
65
+ }
66
+ /** Read the registry. A missing/corrupt file is an empty registry. */
67
+ export function loadRuns() {
68
+ try {
69
+ const path = registryPath();
70
+ if (!existsSync(path))
71
+ return [];
72
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'));
73
+ if (!parsed || !Array.isArray(parsed.runs))
74
+ return [];
75
+ return parsed.runs.filter((r) => !!r &&
76
+ typeof r.id === 'string' &&
77
+ typeof r.command === 'string' &&
78
+ typeof r.pid === 'number' &&
79
+ typeof r.startedAt === 'number' &&
80
+ (r.status === 'running' || r.status === 'done'));
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
86
+ function writeRuns(runs) {
87
+ try {
88
+ mkdirSync(runsDir(), { recursive: true });
89
+ const path = registryPath();
90
+ // Atomic-ish: a crash mid-write must not leave a half file that reads as an
91
+ // empty registry (which would silently lose every live run).
92
+ const tmp = `${path}.tmp`;
93
+ writeFileSync(tmp, JSON.stringify({ version: 1, runs }, null, 2), 'utf-8');
94
+ renameSync(tmp, path);
95
+ return true;
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ }
101
+ /**
102
+ * Add or update one run. Finished entries are kept (so a restart can still report
103
+ * the real exit code) and the oldest are dropped past the cap.
104
+ */
105
+ export function saveRun(entry) {
106
+ const runs = loadRuns().filter((r) => r.id !== entry.id);
107
+ runs.push(entry);
108
+ const finished = runs.filter((r) => r.status === 'done');
109
+ while (runs.length > MAX_PERSISTED_RUNS && finished.length > 0) {
110
+ const oldest = finished.shift();
111
+ const at = runs.findIndex((r) => r.id === oldest.id);
112
+ if (at >= 0)
113
+ runs.splice(at, 1);
114
+ }
115
+ // Never drop a LIVE run to satisfy the cap — the record of running work is the
116
+ // whole point of the file.
117
+ while (runs.length > MAX_PERSISTED_RUNS) {
118
+ const oldest = runs.find((r) => r.status === 'done');
119
+ if (!oldest)
120
+ break;
121
+ runs.splice(runs.indexOf(oldest), 1);
122
+ }
123
+ writeRuns(runs);
124
+ }
125
+ /** The spooled output of one run, or null when there is none. */
126
+ export function readSpool(id) {
127
+ try {
128
+ const path = spoolPath(id);
129
+ if (!existsSync(path))
130
+ return null;
131
+ const text = readFileSync(path, 'utf-8');
132
+ return text.length > 0 ? text : null;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ export function spoolBytes(id) {
139
+ try {
140
+ const path = spoolPath(id);
141
+ if (!existsSync(path))
142
+ return 0;
143
+ return readFileSync(path).length;
144
+ }
145
+ catch {
146
+ return 0;
147
+ }
148
+ }
149
+ export function discardSpool(id) {
150
+ try {
151
+ rmSync(spoolPath(id), { force: true });
152
+ }
153
+ catch {
154
+ /* best-effort */
155
+ }
156
+ }
157
+ /**
158
+ * Delete spool files no record refers to.
159
+ *
160
+ * The registry is capped, but a file is not a record: without this, every run
161
+ * that was ever monitored would leave its log behind forever, and "bounded state"
162
+ * would be true of the JSON and false of the disk. Run on hydrate, when the live
163
+ * set is exactly known.
164
+ */
165
+ export function sweepSpools(liveIds) {
166
+ try {
167
+ const keep = new Set(liveIds);
168
+ const dir = join(runsDir(), 'out');
169
+ if (!existsSync(dir))
170
+ return;
171
+ for (const name of readdirSync(dir)) {
172
+ if (!name.endsWith('.log'))
173
+ continue;
174
+ const id = name.slice(0, -4);
175
+ if (keep.has(id))
176
+ continue;
177
+ rmSync(join(dir, name), { force: true });
178
+ }
179
+ }
180
+ catch {
181
+ /* best-effort: a sweep that fails must not stop a turn */
182
+ }
183
+ }
184
+ /**
185
+ * A bounded head+tail window over some text.
186
+ *
187
+ * The head is kept because a run's first lines name what it is doing and how it
188
+ * was invoked; the tail is kept because that is where the failure is. The middle
189
+ * is what gets dropped — bounded, explicit, and never silently a lie about size.
190
+ */
191
+ export function outputWindow(text, maxChars, headChars = SPOOL_HEAD_BYTES, tailChars = SPOOL_TAIL_BYTES) {
192
+ const clean = text.replace(/\r\n/g, '\n');
193
+ if (clean.length <= maxChars)
194
+ return { text: clean, omittedChars: 0 };
195
+ const head = clean.slice(0, Math.min(headChars, Math.floor(maxChars / 2)));
196
+ const tail = clean.slice(-Math.min(tailChars, Math.floor(maxChars / 2)));
197
+ const omittedChars = Math.max(0, clean.length - head.length - tail.length);
198
+ return {
199
+ text: `${head}\n\n… [${omittedChars} characters omitted — the middle of a long run, kept at the ends] …\n\n${tail}`,
200
+ omittedChars,
201
+ };
202
+ }
203
+ /**
204
+ * Write the spool for one run: HEAD (until it is full) + a rolling TAIL.
205
+ *
206
+ * The file is REWRITTEN whole rather than appended to, which is what keeps it
207
+ * bounded: a chatty command can write gigabytes in an hour and the spool still
208
+ * stays at ~64KB. Flushing is throttled (`SPOOL_FLUSH_MS`) so the write cost is
209
+ * independent of the output rate; `close()` does the final write.
210
+ *
211
+ * NOTHING IS WRITTEN UNTIL {@link startPersisting}. Until a run is handed to
212
+ * monitoring it is not durable (there is no registry entry to read it back), so a
213
+ * file on disk would be litter the user never asked for — and litter they do get:
214
+ * MEASURED, the first output chunk of an ordinary `npm test` created
215
+ * `<config>/run-terminal/out/<id>.log` in the developer's REAL profile on every
216
+ * test that spawned a command and finished inside its budget, for a spool that
217
+ * was then discarded. Buffering in memory until hand-off loses nothing (the head
218
+ * is bounded, and the window written at hand-off is the same one) and keeps the
219
+ * suite out of the profile it runs in.
220
+ */
221
+ export class SpoolWriter {
222
+ id;
223
+ headBytes;
224
+ tailBytes;
225
+ dir;
226
+ head = '';
227
+ tail = '';
228
+ /** Everything ever written, so the window can say how much it dropped. */
229
+ total = 0;
230
+ dirty = false;
231
+ lastFlush = 0;
232
+ closed = false;
233
+ discarded = false;
234
+ /** False until the run is durable — see {@link startPersisting}. */
235
+ persisting = false;
236
+ /**
237
+ * `append` — the file is growing with the run, byte for byte, because it is
238
+ * still under the full-log cap. `window` — the cap was passed, so the file is
239
+ * the bounded head+tail window from here on.
240
+ */
241
+ mode = 'append';
242
+ /** Text written since the last flush, in append mode. */
243
+ pending = '';
244
+ fullBytes;
245
+ constructor(id, headBytes = SPOOL_HEAD_BYTES, tailBytes = SPOOL_TAIL_BYTES,
246
+ /**
247
+ * Where the window is written. Defaults to the monitored-run spool dir; the
248
+ * dashboard's tasks keep theirs beside their own records. Parameterised
249
+ * rather than duplicated because the discipline — bounded, rewritten whole,
250
+ * throttled, discarded when nobody will read it — is the part worth sharing.
251
+ */
252
+ dir = join(runsDir(), 'out'), fullBytes = envFullBytes()) {
253
+ this.id = id;
254
+ this.headBytes = headBytes;
255
+ this.tailBytes = tailBytes;
256
+ this.dir = dir;
257
+ this.fullBytes = fullBytes;
258
+ }
259
+ /** The file this window lives in. */
260
+ path() {
261
+ return join(this.dir, `${this.id}.log`);
262
+ }
263
+ /**
264
+ * Begin writing to disk: the run has outlived its wait budget and is now
265
+ * durable (`persistRun`), so its output has to be readable by another process.
266
+ */
267
+ startPersisting() {
268
+ this.persisting = true;
269
+ }
270
+ write(text) {
271
+ if (this.closed || this.discarded || !text)
272
+ return;
273
+ this.total += text.length;
274
+ if (this.head.length < this.headBytes) {
275
+ this.head += text.slice(0, this.headBytes - this.head.length);
276
+ }
277
+ // The tail only needs to remember the last `tailBytes`; a 2x window lets the
278
+ // slice below stay cheap without re-slicing on every chunk.
279
+ this.tail += text;
280
+ if (this.tail.length > this.tailBytes * 2)
281
+ this.tail = this.tail.slice(-this.tailBytes);
282
+ this.dirty = true;
283
+ if (this.persisting && this.mode === 'append') {
284
+ if (this.total > this.fullBytes) {
285
+ // The run outgrew the cap: drop what the window cannot hold, once.
286
+ this.collapse();
287
+ return;
288
+ }
289
+ this.pending += text;
290
+ // A chatty command must not be able to buffer megabytes between flushes.
291
+ if (this.pending.length >= SPOOL_PENDING_BYTES)
292
+ this.flush();
293
+ }
294
+ this.flushIfDue();
295
+ }
296
+ flushIfDue() {
297
+ if (!this.persisting)
298
+ return;
299
+ const now = Date.now();
300
+ if (now - this.lastFlush < SPOOL_FLUSH_MS)
301
+ return;
302
+ this.flush();
303
+ }
304
+ /**
305
+ * Cross the cap: write the bounded window ONCE, and stop growing the file.
306
+ *
307
+ * The bytes already appended are what a run's middle looked like while it still
308
+ * fit; rewriting the file is what keeps a gigabyte of output from becoming a
309
+ * gigabyte of disk.
310
+ */
311
+ collapse() {
312
+ this.mode = 'window';
313
+ this.pending = '';
314
+ this.flush();
315
+ }
316
+ /**
317
+ * Write what has accumulated. Safe to call at any time.
318
+ *
319
+ * The file is the HEAD plus the LAST `tailBytes` of the tail — taken from the
320
+ * END, because the tail is where a failure explains itself. When everything
321
+ * fits in the head, only the head is written: otherwise a short run's output
322
+ * would be stored twice and read back doubled.
323
+ */
324
+ flush() {
325
+ if (!this.persisting || !this.dirty || this.discarded || this.closed)
326
+ return;
327
+ this.dirty = false;
328
+ this.lastFlush = Date.now();
329
+ const tail = this.tail.slice(-this.tailBytes);
330
+ const omitted = Math.max(0, this.total - this.head.length - tail.length);
331
+ const body = omitted > 0
332
+ ? `${this.head}\n… [${omitted} characters omitted — kept at the ends] …\n${tail}`
333
+ : this.head || tail;
334
+ try {
335
+ mkdirSync(this.dir, { recursive: true });
336
+ if (this.mode === 'append' && this.pending) {
337
+ // UNDER THE CAP: append what the run actually printed, so the middle of a
338
+ // long-but-not-huge run is still readable afterwards.
339
+ appendFileSync(this.path(), this.pending, 'utf-8');
340
+ this.pending = '';
341
+ return;
342
+ }
343
+ writeFileSync(this.path(), body, 'utf-8');
344
+ }
345
+ catch {
346
+ /* the spool is a convenience — never break the run for it */
347
+ }
348
+ }
349
+ /**
350
+ * Make sure the bytes still held in memory reach the file.
351
+ *
352
+ * `close()` cannot use `flush()` for this: in append mode a pause in output
353
+ * older than the throttle leaves `pending` unwritten, and those are exactly the
354
+ * last lines of a run — the ones that explain why it failed.
355
+ */
356
+ drainPending() {
357
+ if (!this.persisting || this.discarded || !this.pending)
358
+ return;
359
+ try {
360
+ mkdirSync(this.dir, { recursive: true });
361
+ appendFileSync(this.path(), this.pending, 'utf-8');
362
+ this.pending = '';
363
+ }
364
+ catch {
365
+ /* best-effort */
366
+ }
367
+ }
368
+ /** Final write. The file stays (a finished run's output is still worth reading). */
369
+ close() {
370
+ if (this.closed)
371
+ return;
372
+ if (this.mode === 'append')
373
+ this.drainPending();
374
+ else
375
+ this.flush();
376
+ this.closed = true;
377
+ }
378
+ /** Drop the spool: the run finished inside its wait budget, so nobody will poll it. */
379
+ discard() {
380
+ this.discarded = true;
381
+ this.closed = true;
382
+ try {
383
+ rmSync(this.path(), { force: true });
384
+ }
385
+ catch {
386
+ /* best-effort */
387
+ }
388
+ }
389
+ }
390
+ /**
391
+ * Is `pid` still the process we started, or has the pid been recycled?
392
+ *
393
+ * A run recovered from the registry is identified by a NUMBER that outlived the
394
+ * process table it came from. Between the crash and the re-attach, that pid can
395
+ * belong to something else entirely — and this tool's whole safety story is that
396
+ * it only ever signals commands IT started. So before any recovered run is
397
+ * signalled, it is identified all over again.
398
+ *
399
+ * The process line — `ps` on POSIX, the same `Get-CimInstance Win32_Process`
400
+ * query the liveness sampler already runs on Windows — shows the shell we spawned
401
+ * (`/bin/sh -c <command>`) or, when the shell exec'd a simple command, that
402
+ * command's own line; both contain the command text, which is why the check is a
403
+ * containment test rather than equality.
404
+ */
405
+ export function commandMatchesProcess(pid, command) {
406
+ const row = lookupProcess(pid);
407
+ if (row === null)
408
+ return null; // this platform cannot read the process table
409
+ if (!row.found)
410
+ return false; // the pid is gone — definitely not ours
411
+ return commandVerdict(command, row.command);
412
+ }
413
+ /**
414
+ * The command-line witness alone, as a verdict: `true` ours, `false` not ours,
415
+ * `null` no reading (which is NOT a match, and must never be treated as one).
416
+ */
417
+ function commandVerdict(command, liveLine) {
418
+ const wanted = command.trim();
419
+ if (!wanted)
420
+ return null;
421
+ if (!liveLine)
422
+ return null; // a row whose command line could not be read
423
+ try {
424
+ // Quotes and runs of whitespace are NOT differences of identity: the shell
425
+ // strips the quotes from `node -e "…"` before the process exists, so a naive
426
+ // containment test would refuse to stop a run we really do own.
427
+ const normalize = (s) => s.replace(/["']/g, '').replace(/\s+/g, ' ').trim();
428
+ const actual = normalize(liveLine);
429
+ const wantedNorm = normalize(wanted);
430
+ // The process line CONTAINS what we recorded — the shell carries it
431
+ // (`sh -c npm test`), or the command is what the shell exec'd into.
432
+ if (actual.includes(wantedNorm))
433
+ return true;
434
+ // A process listing can TRUNCATE a very long command line, so a substantial
435
+ // PREFIX of what we recorded is accepted — but only with the same binary,
436
+ // and never a short bare name: a recycled pid showing just `node` must NOT
437
+ // satisfy a recorded `node -e …`, which is how a stranger would be adopted.
438
+ const first = wantedNorm.split(' ')[0];
439
+ if (actual.length >= 16 && wantedNorm.startsWith(actual) && actual.split(' ')[0] === first)
440
+ return true;
441
+ return false;
442
+ }
443
+ catch {
444
+ return null; // unknown — the caller must not treat this as a match
445
+ }
446
+ }
447
+ /**
448
+ * Identity, decided by TWO independent witnesses — pure, so every branch can be
449
+ * asserted instead of hoped for.
450
+ *
451
+ * pid + command line what the run was started as
452
+ * pid + OS start token when the OS says that process began
453
+ *
454
+ * The rule that makes this safe rather than merely clever:
455
+ *
456
+ * a witness may VETO, and only the start token may ACCEPT ALONE.
457
+ *
458
+ * - A different command line is not our process, so `false` wins over any
459
+ * agreement elsewhere — a recycled pid running something else is refused.
460
+ * - When the command lines agree, a start token that DISAGREES refuses too: the
461
+ * same command started as a different process (another `npm test` in another
462
+ * directory, a stranger's `node`) is precisely the case that used to be
463
+ * adopted. This is the new refusal, and it can only ever refuse.
464
+ * - When the command line cannot be read at all (some platforms hide it, and a
465
+ * kernel process has none), a MATCHING start token is accepted: that is a
466
+ * value the kernel does not hand out twice, and it is the difference between
467
+ * a recovered run that can be stopped and one that can only be stopped by
468
+ * hand. Anything short of a match stays `null` — unknown, never ours.
469
+ */
470
+ export function processIdentityVerdict(recorded, live) {
471
+ return processIdentityCheck(recorded, live).verdict;
472
+ }
473
+ /**
474
+ * The verdict AND the evidence behind it.
475
+ *
476
+ * A refusal the operator cannot act on is barely better than a crash: the whole
477
+ * consequence of `false` here is that a real command keeps running and can only
478
+ * be stopped by hand, so the message has to say WHICH witness failed — a
479
+ * different command line and a mismatched start time are different problems, and
480
+ * "could not tell" is a third.
481
+ */
482
+ export function processIdentityCheck(recorded, live) {
483
+ if (live === null) {
484
+ return {
485
+ verdict: null,
486
+ why: 'this platform could not read a process table, so the pid cannot be checked',
487
+ kind: 'unreadable-table',
488
+ };
489
+ }
490
+ // A FACT, as opposed to an inference: the pid is not in the table at all. Only
491
+ // this branch may ever be read as "it is over" — every other refusal says
492
+ // something about IDENTITY, which is not the same claim (see the callers).
493
+ if (!live.found)
494
+ return { verdict: false, why: 'the pid is not running any more', kind: 'gone' };
495
+ const byCommand = commandVerdict(recorded.command, live.command);
496
+ const start = recorded.osStartedAt;
497
+ const byStart = start && live.startedAtText ? start === live.startedAtText : null;
498
+ if (byCommand === false) {
499
+ return {
500
+ verdict: false,
501
+ kind: 'not-ours',
502
+ why: `the pid now runs a different command line (\`${live.command}\`), so it is not the ` +
503
+ `process that was started`,
504
+ };
505
+ }
506
+ if (byCommand === true) {
507
+ if (byStart === false) {
508
+ return {
509
+ verdict: false,
510
+ kind: 'not-ours',
511
+ why: `its command line matches, but the OS start time differs from the one recorded for this run ` +
512
+ `(\`${live.startedAtText}\` now vs \`${start}\` then) — the pid was RECYCLED by a different process ` +
513
+ `that happens to run the same command`,
514
+ };
515
+ }
516
+ return {
517
+ verdict: true,
518
+ kind: 'verified',
519
+ why: byStart === true
520
+ ? 'its command line AND its OS start time match what was recorded when it was started'
521
+ : 'its command line matches what was recorded (no start-time token was recorded to compare)',
522
+ };
523
+ }
524
+ if (byStart === true) {
525
+ return {
526
+ verdict: true,
527
+ kind: 'verified',
528
+ why: 'its OS start time matches the token recorded when it was started, which is a value the OS does not ' +
529
+ 'hand to two different processes (its command line could not be read)',
530
+ };
531
+ }
532
+ return {
533
+ verdict: null,
534
+ kind: 'unknown',
535
+ why: byStart === false
536
+ ? 'its start time does not match, and its command line could not be read'
537
+ : 'neither its command line nor its start time could be read',
538
+ };
539
+ }
540
+ /** `processIdentityVerdict` against the live table: one read, both witnesses. */
541
+ export function processIdentityMatches(pid, recorded) {
542
+ return processIdentityVerdict(recorded, lookupProcess(pid));
543
+ }
544
+ //# sourceMappingURL=run-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-registry.js","sourceRoot":"","sources":["../../src/tools/run-registry.ts"],"names":[],"mappings":"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"}