@astralyn/pi 0.84.5 → 0.84.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +4 -0
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/extensions/index.d.ts +1 -1
  6. package/dist/core/extensions/index.d.ts.map +1 -1
  7. package/dist/core/extensions/index.js.map +1 -1
  8. package/dist/core/extensions/runner.d.ts +1 -0
  9. package/dist/core/extensions/runner.d.ts.map +1 -1
  10. package/dist/core/extensions/runner.js +6 -0
  11. package/dist/core/extensions/runner.js.map +1 -1
  12. package/dist/core/extensions/types.d.ts +11 -0
  13. package/dist/core/extensions/types.d.ts.map +1 -1
  14. package/dist/core/extensions/types.js.map +1 -1
  15. package/dist/core/tools/bash.d.ts +5 -0
  16. package/dist/core/tools/bash.d.ts.map +1 -1
  17. package/dist/core/tools/bash.js +1 -1
  18. package/dist/core/tools/bash.js.map +1 -1
  19. package/dist/extensions/background/index.d.ts +95 -20
  20. package/dist/extensions/background/index.d.ts.map +1 -1
  21. package/dist/extensions/background/index.js +470 -136
  22. package/dist/extensions/background/index.js.map +1 -1
  23. package/dist/extensions/background/manager.d.ts +14 -0
  24. package/dist/extensions/background/manager.d.ts.map +1 -1
  25. package/dist/extensions/background/manager.js +125 -24
  26. package/dist/extensions/background/manager.js.map +1 -1
  27. package/dist/extensions/background/registry.d.ts +82 -0
  28. package/dist/extensions/background/registry.d.ts.map +1 -1
  29. package/dist/extensions/background/registry.js +237 -6
  30. package/dist/extensions/background/registry.js.map +1 -1
  31. package/dist/extensions/background/render.d.ts +35 -11
  32. package/dist/extensions/background/render.d.ts.map +1 -1
  33. package/dist/extensions/background/render.js +172 -30
  34. package/dist/extensions/background/render.js.map +1 -1
  35. package/dist/extensions/subagent/activity.d.ts +3 -5
  36. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  37. package/dist/extensions/subagent/activity.js +8 -89
  38. package/dist/extensions/subagent/activity.js.map +1 -1
  39. package/dist/extensions/subagent/agents-command.d.ts.map +1 -1
  40. package/dist/extensions/subagent/agents-command.js +40 -94
  41. package/dist/extensions/subagent/agents-command.js.map +1 -1
  42. package/dist/extensions/subagent/agents.d.ts +2 -0
  43. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  44. package/dist/extensions/subagent/agents.js +7 -1
  45. package/dist/extensions/subagent/agents.js.map +1 -1
  46. package/dist/extensions/subagent/budget.d.ts +15 -0
  47. package/dist/extensions/subagent/budget.d.ts.map +1 -0
  48. package/dist/extensions/subagent/budget.js +96 -0
  49. package/dist/extensions/subagent/budget.js.map +1 -0
  50. package/dist/extensions/subagent/cancellation.d.ts +29 -0
  51. package/dist/extensions/subagent/cancellation.d.ts.map +1 -0
  52. package/dist/extensions/subagent/cancellation.js +73 -0
  53. package/dist/extensions/subagent/cancellation.js.map +1 -0
  54. package/dist/extensions/subagent/index.d.ts.map +1 -1
  55. package/dist/extensions/subagent/index.js +5 -22
  56. package/dist/extensions/subagent/index.js.map +1 -1
  57. package/dist/extensions/subagent/model-selection.d.ts +16 -0
  58. package/dist/extensions/subagent/model-selection.d.ts.map +1 -0
  59. package/dist/extensions/subagent/model-selection.js +51 -0
  60. package/dist/extensions/subagent/model-selection.js.map +1 -0
  61. package/dist/extensions/subagent/render.d.ts +6 -1
  62. package/dist/extensions/subagent/render.d.ts.map +1 -1
  63. package/dist/extensions/subagent/render.js +23 -12
  64. package/dist/extensions/subagent/render.js.map +1 -1
  65. package/dist/extensions/subagent/resolve.d.ts.map +1 -1
  66. package/dist/extensions/subagent/resolve.js +2 -21
  67. package/dist/extensions/subagent/resolve.js.map +1 -1
  68. package/dist/extensions/subagent/runner.d.ts +4 -5
  69. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  70. package/dist/extensions/subagent/runner.js +71 -264
  71. package/dist/extensions/subagent/runner.js.map +1 -1
  72. package/dist/extensions/subagent/sdk-runner.d.ts +13 -11
  73. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  74. package/dist/extensions/subagent/sdk-runner.js +167 -148
  75. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  76. package/dist/extensions/subagent/state.d.ts +89 -0
  77. package/dist/extensions/subagent/state.d.ts.map +1 -0
  78. package/dist/extensions/subagent/state.js +294 -0
  79. package/dist/extensions/subagent/state.js.map +1 -0
  80. package/dist/extensions/subagent/text.d.ts +4 -0
  81. package/dist/extensions/subagent/text.d.ts.map +1 -1
  82. package/dist/extensions/subagent/text.js +75 -0
  83. package/dist/extensions/subagent/text.js.map +1 -1
  84. package/dist/extensions/subagent/types.d.ts +3 -8
  85. package/dist/extensions/subagent/types.d.ts.map +1 -1
  86. package/dist/extensions/subagent/types.js.map +1 -1
  87. package/dist/extensions/subagent/ui/choices.d.ts +36 -0
  88. package/dist/extensions/subagent/ui/choices.d.ts.map +1 -0
  89. package/dist/extensions/subagent/ui/choices.js +54 -0
  90. package/dist/extensions/subagent/ui/choices.js.map +1 -0
  91. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  92. package/dist/extensions/subagent/ui/model-list.js +10 -18
  93. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  94. package/dist/index.d.ts +1 -1
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js.map +1 -1
  97. package/docs/bundled/README.md +1 -1
  98. package/docs/bundled/extensions/background.md +55 -20
  99. package/docs/extensions.md +13 -0
  100. package/npm-shrinkwrap.json +2 -2
  101. package/package.json +1 -1
  102. package/dist/extensions/subagent/retry.d.ts +0 -17
  103. package/dist/extensions/subagent/retry.d.ts.map +0 -1
  104. package/dist/extensions/subagent/retry.js +0 -41
  105. package/dist/extensions/subagent/retry.js.map +0 -1
@@ -11,7 +11,7 @@
11
11
  * whole process tree cross-platform.
12
12
  */
13
13
  import { randomBytes } from "node:crypto";
14
- import { createWriteStream, writeFileSync } from "node:fs";
14
+ import { closeSync, createWriteStream, openSync } from "node:fs";
15
15
  import { open } from "node:fs/promises";
16
16
  import { tmpdir } from "node:os";
17
17
  import { join } from "node:path";
@@ -20,7 +20,34 @@ import { sanitizeBinaryOutput } from "../../utils/shell.js";
20
20
  export const DEFAULT_MAX_OUTPUT_BYTES = 20 * 1024 * 1024;
21
21
  export const DEFAULT_MAX_RUNNING_TASKS = 8;
22
22
  export const DEFAULT_NOTIFY_TAIL_BYTES = 4 * 1024;
23
+ /** Wait-window bounds shared by execution and the pending-call renderer. */
24
+ export const BG_WAIT_DEFAULT_MS = 20_000;
25
+ export const BG_WAIT_MIN_MS = 1_000;
26
+ export const BG_WAIT_MAX_MS = 60_000;
23
27
  const SHUTDOWN_GRACE_MS = 2_000;
28
+ /** Stall watchdog defaults, matching Claude Code's CC-1175 tuning. */
29
+ export const STALL_POLL_INTERVAL_MS = 5_000;
30
+ export const STALL_THRESHOLD_MS = 45_000;
31
+ export const STALL_TAIL_BYTES = 1_024;
32
+ /**
33
+ * Last-line patterns that suggest a command is blocked waiting for keyboard
34
+ * input. Used to gate the stall notification: a task that is merely slow
35
+ * (git log -S, long silent builds) stays silent — only a tail that looks like
36
+ * a prompt the model can act on triggers a notification.
37
+ */
38
+ const STALL_PROMPT_PATTERNS = [
39
+ /\(y\/n\)/i, // (Y/n), (y/N)
40
+ /\[y\/n\]/i, // [Y/n], [y/N]
41
+ /\(yes\/no\)/i,
42
+ /\b(?:Do you|Would you|Shall I|Are you sure|Ready to)\b.*\? *$/i, // directed questions
43
+ /Press (any key|Enter)/i,
44
+ /Continue\?/i,
45
+ /Overwrite\?/i,
46
+ ];
47
+ export function looksLikePrompt(tail) {
48
+ const lastLine = tail.trimEnd().split("\n").pop() ?? "";
49
+ return STALL_PROMPT_PATTERNS.some((pattern) => pattern.test(lastLine));
50
+ }
24
51
  /**
25
52
  * Read a bounded slice from the head or tail of a file via positioned reads —
26
53
  * never the whole file. Tail reads skip UTF-8 continuation bytes so the text
@@ -62,6 +89,49 @@ export async function readOutputSlice(filePath, options) {
62
89
  await file.close();
63
90
  }
64
91
  }
92
+ /**
93
+ * Read the last `maxBytes` of a file starting no earlier than `fromByte` — the
94
+ * bounded delta since an offset, tail-aligned so the outcome (at the end of
95
+ * the output) is always included. `fromByte` is clamped into the file; the
96
+ * returned `fromByte` is where the slice actually starts.
97
+ */
98
+ export async function readOutputSince(filePath, fromByte, maxBytes) {
99
+ const file = await open(filePath, "r");
100
+ try {
101
+ const { size } = await file.stat();
102
+ const floor = Math.min(Math.max(0, Math.floor(fromByte)), size);
103
+ let length = Math.min(size - floor, Math.max(0, Math.floor(maxBytes)));
104
+ let position = size - length;
105
+ let startsMidLine = false;
106
+ if (position > 0 && length > 0) {
107
+ // Same boundary probe as tail reads: probe[0] tells whether the slice
108
+ // starts at a line boundary, the rest skips UTF-8 continuation bytes.
109
+ const probeStart = position - 1;
110
+ const probe = Buffer.alloc(Math.min(5, size - probeStart));
111
+ await file.read(probe, 0, probe.length, probeStart);
112
+ let skip = 0;
113
+ while (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {
114
+ skip++;
115
+ }
116
+ position += skip;
117
+ length -= skip;
118
+ startsMidLine = probe[skip] !== 0x0a;
119
+ }
120
+ const buffer = Buffer.alloc(Math.max(0, length));
121
+ const bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;
122
+ return {
123
+ text: buffer.subarray(0, bytesRead).toString("utf8"),
124
+ sliceBytes: bytesRead,
125
+ totalBytes: size,
126
+ truncated: position > floor,
127
+ startsMidLine,
128
+ fromByte: position,
129
+ };
130
+ }
131
+ finally {
132
+ await file.close();
133
+ }
134
+ }
65
135
  export class BackgroundTaskRegistry {
66
136
  constructor(options) {
67
137
  this.tasks = new Map();
@@ -73,6 +143,10 @@ export class BackgroundTaskRegistry {
73
143
  this.maxRunningTasks = options.maxRunningTasks ?? DEFAULT_MAX_RUNNING_TASKS;
74
144
  this.notifyTailBytes = options.notifyTailBytes ?? DEFAULT_NOTIFY_TAIL_BYTES;
75
145
  this.onNotify = options.onNotify;
146
+ this.onStall = options.onStall;
147
+ this.stallPollIntervalMs = options.stall?.pollIntervalMs ?? STALL_POLL_INTERVAL_MS;
148
+ this.stallThresholdMs = options.stall?.thresholdMs ?? STALL_THRESHOLD_MS;
149
+ this.stallTailBytes = options.stall?.tailBytes ?? STALL_TAIL_BYTES;
76
150
  this.onChange = options.onChange;
77
151
  this.now = options.now ?? Date.now;
78
152
  }
@@ -86,12 +160,13 @@ export class BackgroundTaskRegistry {
86
160
  const running = this.listTasks().filter((task) => task.status === "running");
87
161
  if (running.length >= this.maxRunningTasks) {
88
162
  const list = running.map((task) => ` ${task.id} ${firstCommandLine(task.command)}`).join("\n");
89
- throw new Error(`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one with bg_kill first:\n${list}`);
163
+ throw new Error(`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one first (bg action kill):\n${list}`);
90
164
  }
91
165
  const id = this.newTaskId();
92
166
  const task = {
93
167
  id,
94
168
  command: input.command,
169
+ description: input.description,
95
170
  cwd: input.cwd,
96
171
  status: "running",
97
172
  startedAt: this.now(),
@@ -101,20 +176,28 @@ export class BackgroundTaskRegistry {
101
176
  outputTruncated: false,
102
177
  timeoutSeconds: input.timeoutSeconds,
103
178
  notified: false,
179
+ stalled: false,
104
180
  };
105
181
  let resolveDone;
106
182
  const done = new Promise((resolve) => {
107
183
  resolveDone = resolve;
108
184
  });
109
- // Create the file synchronously so bg_logs and the /bg viewer never see
110
- // ENOENT between task start and the stream's async open.
111
- writeFileSync(task.outputPath, "");
185
+ // Create the file synchronously so read/wait and the /bg viewer never see
186
+ // ENOENT between task start and the stream's async open. The exclusive
187
+ // 'wx' flag fails on any existing path — including a symlink — so creation
188
+ // can never truncate a file a stale id (or an attacker-planted link) points at.
189
+ createOutputFileExclusively(task.outputPath);
112
190
  const runtime = {
113
191
  controller: new AbortController(),
114
192
  stream: createWriteStream(task.outputPath, { flags: "a" }),
115
193
  killRequested: false,
116
194
  overflow: false,
117
195
  finalized: false,
196
+ waiters: 0,
197
+ stallTimer: undefined,
198
+ stallLastSize: 0,
199
+ stallLastGrowthAt: this.now(),
200
+ stallNotified: false,
118
201
  done,
119
202
  resolveDone,
120
203
  };
@@ -126,6 +209,7 @@ export class BackgroundTaskRegistry {
126
209
  });
127
210
  this.tasks.set(id, task);
128
211
  this.runtimes.set(id, runtime);
212
+ this.armStallWatchdog(task, runtime);
129
213
  void this.run(task, runtime, input.env);
130
214
  this.onChange();
131
215
  return task;
@@ -170,10 +254,12 @@ export class BackgroundTaskRegistry {
170
254
  return { killed: true };
171
255
  }
172
256
  counts() {
173
- const counts = { running: 0, completed: 0, failed: 0, killed: 0, timeout: 0, total: 0 };
257
+ const counts = { running: 0, completed: 0, failed: 0, killed: 0, timeout: 0, total: 0, stalled: 0 };
174
258
  for (const task of this.tasks.values()) {
175
259
  counts[task.status]++;
176
260
  counts.total++;
261
+ if (task.stalled)
262
+ counts.stalled++;
177
263
  }
178
264
  return counts;
179
265
  }
@@ -185,6 +271,46 @@ export class BackgroundTaskRegistry {
185
271
  await this.runtimes.get(id)?.done;
186
272
  return task;
187
273
  }
274
+ /**
275
+ * Bounded model-facing wait with the claim protocol: a waiter registered
276
+ * when the task finalizes takes over delivery — finalize sees waiters > 0
277
+ * and suppresses the followUp notification, so the completion is delivered
278
+ * exactly once, inline here. If the window expires first, the followUp
279
+ * notification is left untouched and fires as usual.
280
+ *
281
+ * All interleavings are deterministic on JS's single thread: a waiter that
282
+ * registers before finalize's synchronous claim check claims delivery; one
283
+ * that arrives after sees a terminal status and returns it immediately
284
+ * (the followUp may also arrive — an idempotent repeat, not a lie).
285
+ */
286
+ async waitForResult(id, timeoutMs) {
287
+ const task = this.tasks.get(id);
288
+ if (!task)
289
+ throw new Error(`Unknown background task: ${id}`);
290
+ const runtime = this.runtimes.get(id);
291
+ if (!runtime)
292
+ throw new Error(`Unknown background task: ${id}`);
293
+ if (task.status !== "running")
294
+ return { outcome: "terminal", task };
295
+ runtime.waiters++;
296
+ let timer;
297
+ try {
298
+ const timedOut = await new Promise((resolve) => {
299
+ timer = setTimeout(() => resolve(true), timeoutMs);
300
+ void runtime.done.then(() => resolve(false));
301
+ });
302
+ // done may resolve right after the timer fired (race window); a task
303
+ // that reached a terminal state during the wait is delivered here.
304
+ if (!timedOut || task.status !== "running")
305
+ return { outcome: "terminal", task };
306
+ return { outcome: "timeout", task };
307
+ }
308
+ finally {
309
+ runtime.waiters--;
310
+ if (timer !== undefined)
311
+ clearTimeout(timer);
312
+ }
313
+ }
188
314
  /**
189
315
  * Mute notifications, refuse new tasks, abort every running task, and wait
190
316
  * (bounded) for their finalization.
@@ -212,6 +338,95 @@ export class BackgroundTaskRegistry {
212
338
  clearTimeout(timer);
213
339
  }
214
340
  }
341
+ /**
342
+ * Stall watchdog (Claude Code CC-1175): poll the output file; when output
343
+ * stops growing past the threshold AND the tail looks like an interactive
344
+ * prompt, flag the task and fire a one-shot notification. Merely-slow tasks
345
+ * never match; recovery clears the flag (the notification never re-fires).
346
+ */
347
+ armStallWatchdog(task, runtime) {
348
+ if (!this.onStall)
349
+ return;
350
+ runtime.stallTimer = setInterval(() => this.checkStall(task, runtime), this.stallPollIntervalMs);
351
+ runtime.stallTimer.unref?.();
352
+ }
353
+ clearStallWatchdog(runtime) {
354
+ if (runtime.stallTimer !== undefined) {
355
+ clearInterval(runtime.stallTimer);
356
+ runtime.stallTimer = undefined;
357
+ }
358
+ }
359
+ checkStall(task, runtime) {
360
+ if (runtime.finalized || this.shuttingDown)
361
+ return;
362
+ const size = task.outputBytes;
363
+ if (size > runtime.stallLastSize) {
364
+ runtime.stallLastSize = size;
365
+ runtime.stallLastGrowthAt = this.now();
366
+ if (task.stalled) {
367
+ // Output resumed: reflect reality in listings, never re-notify.
368
+ task.stalled = false;
369
+ this.onChange();
370
+ }
371
+ return;
372
+ }
373
+ if (this.now() - runtime.stallLastGrowthAt < this.stallThresholdMs)
374
+ return;
375
+ void this.probeStallTail(task, runtime);
376
+ }
377
+ async probeStallTail(task, runtime) {
378
+ // Re-check inside the async boundary: finalize may have raced the probe.
379
+ if (runtime.finalized || this.shuttingDown)
380
+ return;
381
+ let tailText = "";
382
+ let tailError;
383
+ try {
384
+ const slice = await readOutputSlice(task.outputPath, { mode: "tail", maxBytes: this.stallTailBytes });
385
+ tailText = slice.text;
386
+ }
387
+ catch (error) {
388
+ tailError = error instanceof Error ? error.message : String(error);
389
+ }
390
+ if (runtime.finalized || this.shuttingDown)
391
+ return;
392
+ // Merely slow: not prompt-shaped. Reset so the next check is a full
393
+ // threshold window out instead of re-probing on every tick.
394
+ if (!tailError && !looksLikePrompt(tailText)) {
395
+ runtime.stallLastGrowthAt = this.now();
396
+ return;
397
+ }
398
+ task.stalled = true;
399
+ this.onChange();
400
+ if (runtime.stallNotified)
401
+ return;
402
+ runtime.stallNotified = true;
403
+ let tailBytes = 0;
404
+ let totalBytes = task.outputBytes;
405
+ let tailTruncated = false;
406
+ let tailStartsMidLine = false;
407
+ if (!tailError) {
408
+ try {
409
+ const slice = await readOutputSlice(task.outputPath, { mode: "tail", maxBytes: this.stallTailBytes });
410
+ tailText = slice.text;
411
+ tailBytes = slice.sliceBytes;
412
+ totalBytes = slice.totalBytes;
413
+ tailTruncated = slice.truncated;
414
+ tailStartsMidLine = slice.startsMidLine;
415
+ }
416
+ catch (error) {
417
+ tailError = error instanceof Error ? error.message : String(error);
418
+ }
419
+ }
420
+ this.onStall?.({
421
+ task,
422
+ tailText: sanitizeBinaryOutput(tailText),
423
+ tailBytes,
424
+ totalBytes,
425
+ tailTruncated,
426
+ tailStartsMidLine,
427
+ tailError,
428
+ });
429
+ }
215
430
  newTaskId() {
216
431
  while (true) {
217
432
  const id = `bg-${randomBytes(3).toString("hex")}`;
@@ -258,6 +473,8 @@ export class BackgroundTaskRegistry {
258
473
  if (runtime.finalized)
259
474
  return;
260
475
  runtime.finalized = true;
476
+ this.clearStallWatchdog(runtime);
477
+ task.stalled = false;
261
478
  // Flush before setting terminal state: the output file must hold the
262
479
  // complete truth by the time anyone is told the task ended.
263
480
  await new Promise((resolve) => {
@@ -276,6 +493,12 @@ export class BackgroundTaskRegistry {
276
493
  task.exitCode = outcome.exitCode;
277
494
  task.endedAt = this.now();
278
495
  this.onChange();
496
+ // Claim protocol: at least one waitForResult caller is waiting on this
497
+ // completion, so it delivers the result inline. Mark notified to skip the
498
+ // followUp — the completion must be delivered exactly once.
499
+ if (runtime.waiters > 0) {
500
+ task.notified = true;
501
+ }
279
502
  await this.notifyCompletion(task);
280
503
  runtime.resolveDone();
281
504
  }
@@ -339,6 +562,14 @@ export class BackgroundTaskRegistry {
339
562
  }
340
563
  }
341
564
  }
565
+ /**
566
+ * Create an empty output file exclusively ('wx'): fails on any existing path,
567
+ * including a symlink, so creation can never truncate what the path points at.
568
+ */
569
+ export function createOutputFileExclusively(path) {
570
+ const handle = openSync(path, "wx");
571
+ closeSync(handle);
572
+ }
342
573
  /** First non-empty line of a command, trimmed — for one-line task labels. */
343
574
  export function firstCommandLine(command) {
344
575
  const line = command.split(/\r?\n/).find((candidate) => candidate.trim().length > 0) ?? command;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Background task registry: process lifecycle, output capture, completion\n * notification. Pure logic — no UI imports, dependencies injected for tests.\n *\n * Discipline (mirrors the reference implementation this extension distills):\n * - Output streams straight to a file; memory holds only a byte counter.\n * - Finalization is idempotent and strictly ordered: flush stream, set\n * terminal state, then notify. A notification is only sent once the output\n * file holds the complete truth.\n * - Kill goes through AbortController; the shared bash operations kill the\n * whole process tree cross-platform.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream, writeFileSync } from \"node:fs\";\nimport { open } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { BashOperations } from \"../../core/tools/bash.ts\";\nimport { formatSize } from \"../../core/tools/truncate.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\n\nexport const DEFAULT_MAX_OUTPUT_BYTES = 20 * 1024 * 1024;\nexport const DEFAULT_MAX_RUNNING_TASKS = 8;\nexport const DEFAULT_NOTIFY_TAIL_BYTES = 4 * 1024;\nconst SHUTDOWN_GRACE_MS = 2_000;\n\nexport type BgTaskStatus = \"running\" | \"completed\" | \"failed\" | \"killed\" | \"timeout\";\n\nexport interface BgTask {\n\tid: string;\n\tcommand: string;\n\tcwd: string;\n\tstatus: BgTaskStatus;\n\tstartedAt: number;\n\tendedAt?: number;\n\t/** Process exit code; null when reaped by a signal, undefined while running or when spawn failed. */\n\texitCode: number | null | undefined;\n\toutputPath: string;\n\t/** Bytes accepted into the output file (excludes the overflow notice). */\n\toutputBytes: number;\n\t/** True when output hit the byte limit and the task was killed for it. */\n\toutputTruncated: boolean;\n\terror?: string;\n\ttimeoutSeconds?: number;\n\tnotified: boolean;\n}\n\nexport interface BgTaskNotification {\n\ttask: BgTask;\n\t/** Sanitized tail of the output file; empty when unavailable. */\n\ttailText: string;\n\ttailBytes: number;\n\ttotalBytes: number;\n\ttailTruncated: boolean;\n\ttailStartsMidLine: boolean;\n\t/** Set when the output file could not be read; the notification still fires. */\n\ttailError?: string;\n}\n\nexport interface OutputSlice {\n\ttext: string;\n\tsliceBytes: number;\n\ttotalBytes: number;\n\t/** True when the file holds more bytes than the slice. */\n\ttruncated: boolean;\n\t/** True in tail mode when the slice starts mid-line. */\n\tstartsMidLine: boolean;\n}\n\n/**\n * Read a bounded slice from the head or tail of a file via positioned reads —\n * never the whole file. Tail reads skip UTF-8 continuation bytes so the text\n * starts on a codepoint boundary.\n */\nexport async function readOutputSlice(\n\tfilePath: string,\n\toptions: { mode: \"head\" | \"tail\"; maxBytes: number },\n): Promise<OutputSlice> {\n\tconst file = await open(filePath, \"r\");\n\ttry {\n\t\tconst { size } = await file.stat();\n\t\tlet length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));\n\t\tlet position = options.mode === \"tail\" ? size - length : 0;\n\t\tlet startsMidLine = false;\n\n\t\tif (options.mode === \"tail\" && position > 0 && length > 0) {\n\t\t\t// Probe from one byte before the slice: probe[0] tells whether the\n\t\t\t// slice starts at a line boundary, the rest lets us skip UTF-8\n\t\t\t// continuation bytes (0b10xxxxxx) so we never split a codepoint.\n\t\t\tconst probeStart = position - 1;\n\t\t\tconst probe = Buffer.alloc(Math.min(5, size - probeStart));\n\t\t\tawait file.read(probe, 0, probe.length, probeStart);\n\t\t\tlet skip = 0;\n\t\t\twhile (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {\n\t\t\t\tskip++;\n\t\t\t}\n\t\t\tposition += skip;\n\t\t\tlength -= skip;\n\t\t\tstartsMidLine = probe[skip] !== 0x0a;\n\t\t}\n\n\t\tconst buffer = Buffer.alloc(Math.max(0, length));\n\t\tconst bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;\n\t\treturn {\n\t\t\ttext: buffer.subarray(0, bytesRead).toString(\"utf8\"),\n\t\t\tsliceBytes: bytesRead,\n\t\t\ttotalBytes: size,\n\t\t\ttruncated: size > bytesRead,\n\t\t\tstartsMidLine,\n\t\t};\n\t} finally {\n\t\tawait file.close();\n\t}\n}\n\nexport interface BackgroundRegistryOptions {\n\toperations: BashOperations;\n\t/** Directory for task output files. Defaults to the system temp directory. */\n\toutputDir?: string;\n\tmaxOutputBytes?: number;\n\tmaxRunningTasks?: number;\n\tnotifyTailBytes?: number;\n\t/** Called once per finished task; a synchronous throw rolls back `notified`. */\n\tonNotify: (notification: BgTaskNotification) => void;\n\t/** Called whenever a task starts or reaches a terminal state. */\n\tonChange: () => void;\n\tnow?: () => number;\n}\n\ninterface TaskRuntime {\n\tcontroller: AbortController;\n\tstream: WriteStream;\n\tkillRequested: boolean;\n\toverflow: boolean;\n\tstreamError?: string;\n\tfinalized: boolean;\n\tdone: Promise<void>;\n\tresolveDone: () => void;\n}\n\nexport type ResolveTaskResult =\n\t| { ok: true; task: BgTask }\n\t| { ok: false; reason: \"not-found\" | \"ambiguous\"; candidates: BgTask[] };\n\nexport class BackgroundTaskRegistry {\n\tprivate readonly operations: BashOperations;\n\tprivate readonly outputDir: string;\n\tprivate readonly maxOutputBytes: number;\n\tprivate readonly maxRunningTasks: number;\n\tprivate readonly notifyTailBytes: number;\n\tprivate readonly onNotify: (notification: BgTaskNotification) => void;\n\tprivate readonly onChange: () => void;\n\tprivate readonly now: () => number;\n\n\tprivate readonly tasks = new Map<string, BgTask>();\n\tprivate readonly runtimes = new Map<string, TaskRuntime>();\n\tprivate shuttingDown = false;\n\n\tconstructor(options: BackgroundRegistryOptions) {\n\t\tthis.operations = options.operations;\n\t\tthis.outputDir = options.outputDir ?? tmpdir();\n\t\tthis.maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n\t\tthis.maxRunningTasks = options.maxRunningTasks ?? DEFAULT_MAX_RUNNING_TASKS;\n\t\tthis.notifyTailBytes = options.notifyTailBytes ?? DEFAULT_NOTIFY_TAIL_BYTES;\n\t\tthis.onNotify = options.onNotify;\n\t\tthis.onChange = options.onChange;\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tget isShuttingDown(): boolean {\n\t\treturn this.shuttingDown;\n\t}\n\n\tstartTask(input: { command: string; cwd: string; timeoutSeconds?: number; env?: NodeJS.ProcessEnv }): BgTask {\n\t\tif (this.shuttingDown) {\n\t\t\tthrow new Error(\"Pi is shutting down; background task not started.\");\n\t\t}\n\t\tconst running = this.listTasks().filter((task) => task.status === \"running\");\n\t\tif (running.length >= this.maxRunningTasks) {\n\t\t\tconst list = running.map((task) => ` ${task.id} ${firstCommandLine(task.command)}`).join(\"\\n\");\n\t\t\tthrow new Error(\n\t\t\t\t`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one with bg_kill first:\\n${list}`,\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.newTaskId();\n\t\tconst task: BgTask = {\n\t\t\tid,\n\t\t\tcommand: input.command,\n\t\t\tcwd: input.cwd,\n\t\t\tstatus: \"running\",\n\t\t\tstartedAt: this.now(),\n\t\t\texitCode: undefined,\n\t\t\toutputPath: join(this.outputDir, `pi-${id}.log`),\n\t\t\toutputBytes: 0,\n\t\t\toutputTruncated: false,\n\t\t\ttimeoutSeconds: input.timeoutSeconds,\n\t\t\tnotified: false,\n\t\t};\n\n\t\tlet resolveDone!: () => void;\n\t\tconst done = new Promise<void>((resolve) => {\n\t\t\tresolveDone = resolve;\n\t\t});\n\t\t// Create the file synchronously so bg_logs and the /bg viewer never see\n\t\t// ENOENT between task start and the stream's async open.\n\t\twriteFileSync(task.outputPath, \"\");\n\t\tconst runtime: TaskRuntime = {\n\t\t\tcontroller: new AbortController(),\n\t\t\tstream: createWriteStream(task.outputPath, { flags: \"a\" }),\n\t\t\tkillRequested: false,\n\t\t\toverflow: false,\n\t\t\tfinalized: false,\n\t\t\tdone,\n\t\t\tresolveDone,\n\t\t};\n\t\truntime.stream.on(\"error\", (error) => {\n\t\t\tif (runtime.finalized) return;\n\t\t\truntime.streamError = error.message;\n\t\t\truntime.controller.abort();\n\t\t});\n\n\t\tthis.tasks.set(id, task);\n\t\tthis.runtimes.set(id, runtime);\n\t\tvoid this.run(task, runtime, input.env);\n\t\tthis.onChange();\n\t\treturn task;\n\t}\n\n\tgetTask(id: string): BgTask | undefined {\n\t\treturn this.tasks.get(id);\n\t}\n\n\t/** Running tasks first (oldest first), then finished tasks (newest first). */\n\tlistTasks(): BgTask[] {\n\t\tconst all = [...this.tasks.values()];\n\t\tconst running = all.filter((task) => task.status === \"running\");\n\t\trunning.sort((a, b) => a.startedAt - b.startedAt);\n\t\tconst ended = all.filter((task) => task.status !== \"running\");\n\t\tended.sort((a, b) => (b.endedAt ?? b.startedAt) - (a.endedAt ?? a.startedAt));\n\t\treturn [...running, ...ended];\n\t}\n\n\t/** Resolve a task by id or unique id prefix; the \"bg-\" prefix may be omitted. */\n\tresolveTask(idPrefix: string): ResolveTaskResult {\n\t\tconst needle = idPrefix.trim();\n\t\tif (!needle) return { ok: false, reason: \"not-found\", candidates: [] };\n\t\tconst exact = this.tasks.get(needle) ?? this.tasks.get(`bg-${needle}`);\n\t\tif (exact) return { ok: true, task: exact };\n\t\tconst candidates = [...this.tasks.values()].filter(\n\t\t\t(task) => task.id.startsWith(needle) || task.id.startsWith(`bg-${needle}`),\n\t\t);\n\t\tconst first = candidates[0];\n\t\tif (candidates.length === 1 && first) return { ok: true, task: first };\n\t\tif (candidates.length === 0) return { ok: false, reason: \"not-found\", candidates: [] };\n\t\treturn { ok: false, reason: \"ambiguous\", candidates };\n\t}\n\n\tkillTask(id: string): { killed: true } | { killed: false; reason: \"not-found\" | \"not-running\" } {\n\t\tconst task = this.tasks.get(id);\n\t\tconst runtime = this.runtimes.get(id);\n\t\tif (!task || !runtime) return { killed: false, reason: \"not-found\" };\n\t\tif (task.status !== \"running\" || runtime.finalized) return { killed: false, reason: \"not-running\" };\n\t\truntime.killRequested = true;\n\t\truntime.controller.abort();\n\t\treturn { killed: true };\n\t}\n\n\tcounts(): Record<BgTaskStatus, number> & { total: number } {\n\t\tconst counts = { running: 0, completed: 0, failed: 0, killed: 0, timeout: 0, total: 0 };\n\t\tfor (const task of this.tasks.values()) {\n\t\t\tcounts[task.status]++;\n\t\t\tcounts.total++;\n\t\t}\n\t\treturn counts;\n\t}\n\n\t/** Resolves once the task has finalized, including its notification attempt. */\n\tasync waitForTask(id: string): Promise<BgTask> {\n\t\tconst task = this.tasks.get(id);\n\t\tif (!task) throw new Error(`Unknown background task: ${id}`);\n\t\tawait this.runtimes.get(id)?.done;\n\t\treturn task;\n\t}\n\n\t/**\n\t * Mute notifications, refuse new tasks, abort every running task, and wait\n\t * (bounded) for their finalization.\n\t */\n\tasync shutdown(): Promise<void> {\n\t\tthis.shuttingDown = true;\n\t\tconst pending: Promise<void>[] = [];\n\t\tfor (const [id, runtime] of this.runtimes) {\n\t\t\tconst task = this.tasks.get(id);\n\t\t\tif (task?.status === \"running\" && !runtime.finalized) {\n\t\t\t\truntime.killRequested = true;\n\t\t\t\truntime.controller.abort();\n\t\t\t}\n\t\t\tpending.push(runtime.done);\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst grace = new Promise<void>((resolve) => {\n\t\t\ttimer = setTimeout(resolve, SHUTDOWN_GRACE_MS);\n\t\t});\n\t\ttry {\n\t\t\tawait Promise.race([Promise.allSettled(pending).then(() => undefined), grace]);\n\t\t} finally {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t}\n\t}\n\n\tprivate newTaskId(): string {\n\t\twhile (true) {\n\t\t\tconst id = `bg-${randomBytes(3).toString(\"hex\")}`;\n\t\t\tif (!this.tasks.has(id)) return id;\n\t\t}\n\t}\n\n\tprivate async run(task: BgTask, runtime: TaskRuntime, env?: NodeJS.ProcessEnv): Promise<void> {\n\t\tlet outcome: { exitCode?: number | null; error?: unknown };\n\t\ttry {\n\t\t\tconst result = await this.operations.exec(task.command, task.cwd, {\n\t\t\t\tonData: (data) => this.handleData(task, runtime, data),\n\t\t\t\tsignal: runtime.controller.signal,\n\t\t\t\ttimeout: task.timeoutSeconds,\n\t\t\t\tenv,\n\t\t\t});\n\t\t\toutcome = { exitCode: result.exitCode };\n\t\t} catch (error) {\n\t\t\toutcome = { error };\n\t\t}\n\t\tawait this.finalize(task, runtime, outcome);\n\t}\n\n\tprivate handleData(task: BgTask, runtime: TaskRuntime, data: Buffer): void {\n\t\tif (runtime.finalized || runtime.overflow || runtime.streamError !== undefined) return;\n\t\tconst next = task.outputBytes + data.length;\n\t\tif (next <= this.maxOutputBytes) {\n\t\t\truntime.stream.write(data);\n\t\t\ttask.outputBytes = next;\n\t\t\treturn;\n\t\t}\n\t\tconst budget = this.maxOutputBytes - task.outputBytes;\n\t\tif (budget > 0) {\n\t\t\truntime.stream.write(data.subarray(0, budget));\n\t\t\ttask.outputBytes = this.maxOutputBytes;\n\t\t}\n\t\truntime.stream.write(`\\n[output limit ${formatSize(this.maxOutputBytes)} exceeded; task killed]\\n`);\n\t\ttask.outputTruncated = true;\n\t\truntime.overflow = true;\n\t\truntime.controller.abort();\n\t}\n\n\tprivate async finalize(\n\t\ttask: BgTask,\n\t\truntime: TaskRuntime,\n\t\toutcome: { exitCode?: number | null; error?: unknown },\n\t): Promise<void> {\n\t\tif (runtime.finalized) return;\n\t\truntime.finalized = true;\n\n\t\t// Flush before setting terminal state: the output file must hold the\n\t\t// complete truth by the time anyone is told the task ended.\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tconst stream = runtime.stream;\n\t\t\tif (stream.destroyed) {\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstream.on(\"close\", resolve);\n\t\t\tstream.end(() => resolve());\n\t\t});\n\n\t\tconst verdict = this.classifyOutcome(runtime, outcome);\n\t\ttask.status = verdict.status;\n\t\tif (verdict.error !== undefined) task.error = verdict.error;\n\t\ttask.exitCode = outcome.exitCode;\n\t\ttask.endedAt = this.now();\n\t\tthis.onChange();\n\t\tawait this.notifyCompletion(task);\n\t\truntime.resolveDone();\n\t}\n\n\tprivate classifyOutcome(\n\t\truntime: TaskRuntime,\n\t\toutcome: { exitCode?: number | null; error?: unknown },\n\t): { status: BgTaskStatus; error?: string } {\n\t\tif (runtime.overflow) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: `Output exceeded the ${formatSize(this.maxOutputBytes)} limit; the task was killed.`,\n\t\t\t};\n\t\t}\n\t\tif (runtime.streamError !== undefined) {\n\t\t\treturn { status: \"failed\", error: `Output file error: ${runtime.streamError}` };\n\t\t}\n\t\tif (runtime.killRequested) return { status: \"killed\" };\n\t\tconst error = outcome.error;\n\t\tif (error instanceof Error) {\n\t\t\tif (error.message === \"aborted\") return { status: \"killed\" };\n\t\t\tif (error.message.startsWith(\"timeout:\")) {\n\t\t\t\treturn { status: \"timeout\", error: `Timed out after ${error.message.slice(\"timeout:\".length)}s.` };\n\t\t\t}\n\t\t\treturn { status: \"failed\", error: error.message };\n\t\t}\n\t\tif (error !== undefined) return { status: \"failed\", error: String(error) };\n\t\tif (outcome.exitCode === 0) return { status: \"completed\" };\n\t\tif (outcome.exitCode === null) return { status: \"failed\", error: \"Command was terminated by a signal.\" };\n\t\treturn { status: \"failed\", error: `Command exited with code ${outcome.exitCode}` };\n\t}\n\n\tprivate async notifyCompletion(task: BgTask): Promise<void> {\n\t\tif (this.shuttingDown || task.notified) return;\n\t\ttask.notified = true;\n\n\t\tlet tailText = \"\";\n\t\tlet tailBytes = 0;\n\t\tlet totalBytes = task.outputBytes;\n\t\tlet tailTruncated = false;\n\t\tlet tailStartsMidLine = false;\n\t\tlet tailError: string | undefined;\n\t\ttry {\n\t\t\tconst slice = await readOutputSlice(task.outputPath, { mode: \"tail\", maxBytes: this.notifyTailBytes });\n\t\t\ttailText = sanitizeBinaryOutput(slice.text);\n\t\t\ttailBytes = slice.sliceBytes;\n\t\t\ttotalBytes = slice.totalBytes;\n\t\t\ttailTruncated = slice.truncated;\n\t\t\ttailStartsMidLine = slice.startsMidLine;\n\t\t} catch (error) {\n\t\t\ttailError = error instanceof Error ? error.message : String(error);\n\t\t}\n\n\t\ttry {\n\t\t\tthis.onNotify({ task, tailText, tailBytes, totalBytes, tailTruncated, tailStartsMidLine, tailError });\n\t\t} catch {\n\t\t\t// The send failed synchronously (e.g. stale extension runtime after a\n\t\t\t// session replacement); do not pretend the notification was delivered.\n\t\t\ttask.notified = false;\n\t\t}\n\t}\n}\n\n/** First non-empty line of a command, trimmed — for one-line task labels. */\nexport function firstCommandLine(command: string): string {\n\tconst line = command.split(/\\r?\\n/).find((candidate) => candidate.trim().length > 0) ?? command;\n\treturn line.trim();\n}\n\n/** Compact duration like Pi's own timers: 12s, 3m05s, 1h02m. */\nexport function formatDuration(ms: number): string {\n\tconst totalSeconds = Math.max(0, Math.round(ms / 1000));\n\tif (totalSeconds < 60) return `${totalSeconds}s`;\n\tconst minutes = Math.floor(totalSeconds / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (minutes < 60) return `${minutes}m${String(seconds).padStart(2, \"0\")}s`;\n\treturn `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, \"0\")}m`;\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Background task registry: process lifecycle, output capture, completion\n * notification. Pure logic — no UI imports, dependencies injected for tests.\n *\n * Discipline (mirrors the reference implementation this extension distills):\n * - Output streams straight to a file; memory holds only a byte counter.\n * - Finalization is idempotent and strictly ordered: flush stream, set\n * terminal state, then notify. A notification is only sent once the output\n * file holds the complete truth.\n * - Kill goes through AbortController; the shared bash operations kill the\n * whole process tree cross-platform.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { closeSync, createWriteStream, openSync, type WriteStream } from \"node:fs\";\nimport { open } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { BashOperations } from \"../../core/tools/bash.ts\";\nimport { formatSize } from \"../../core/tools/truncate.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\n\nexport const DEFAULT_MAX_OUTPUT_BYTES = 20 * 1024 * 1024;\nexport const DEFAULT_MAX_RUNNING_TASKS = 8;\nexport const DEFAULT_NOTIFY_TAIL_BYTES = 4 * 1024;\n\n/** Wait-window bounds shared by execution and the pending-call renderer. */\nexport const BG_WAIT_DEFAULT_MS = 20_000;\nexport const BG_WAIT_MIN_MS = 1_000;\nexport const BG_WAIT_MAX_MS = 60_000;\nconst SHUTDOWN_GRACE_MS = 2_000;\n\n/** Stall watchdog defaults, matching Claude Code's CC-1175 tuning. */\nexport const STALL_POLL_INTERVAL_MS = 5_000;\nexport const STALL_THRESHOLD_MS = 45_000;\nexport const STALL_TAIL_BYTES = 1_024;\n\n/**\n * Last-line patterns that suggest a command is blocked waiting for keyboard\n * input. Used to gate the stall notification: a task that is merely slow\n * (git log -S, long silent builds) stays silent — only a tail that looks like\n * a prompt the model can act on triggers a notification.\n */\nconst STALL_PROMPT_PATTERNS: RegExp[] = [\n\t/\\(y\\/n\\)/i, // (Y/n), (y/N)\n\t/\\[y\\/n\\]/i, // [Y/n], [y/N]\n\t/\\(yes\\/no\\)/i,\n\t/\\b(?:Do you|Would you|Shall I|Are you sure|Ready to)\\b.*\\? *$/i, // directed questions\n\t/Press (any key|Enter)/i,\n\t/Continue\\?/i,\n\t/Overwrite\\?/i,\n];\n\nexport function looksLikePrompt(tail: string): boolean {\n\tconst lastLine = tail.trimEnd().split(\"\\n\").pop() ?? \"\";\n\treturn STALL_PROMPT_PATTERNS.some((pattern) => pattern.test(lastLine));\n}\n\nexport type BgTaskStatus = \"running\" | \"completed\" | \"failed\" | \"killed\" | \"timeout\";\n\nexport interface BgTask {\n\tid: string;\n\tcommand: string;\n\t/** Optional model-provided label (e.g. \"dev server\") shown in /bg and notifications. */\n\tdescription?: string;\n\tcwd: string;\n\tstatus: BgTaskStatus;\n\tstartedAt: number;\n\tendedAt?: number;\n\t/** True when the stall watchdog flagged this task as waiting for input. */\n\tstalled: boolean;\n\t/** Process exit code; null when reaped by a signal, undefined while running or when spawn failed. */\n\texitCode: number | null | undefined;\n\toutputPath: string;\n\t/** Bytes accepted into the output file (excludes the overflow notice). */\n\toutputBytes: number;\n\t/** True when output hit the byte limit and the task was killed for it. */\n\toutputTruncated: boolean;\n\terror?: string;\n\ttimeoutSeconds?: number;\n\tnotified: boolean;\n}\n\nexport interface BgTaskNotification {\n\ttask: BgTask;\n\t/** Sanitized tail of the output file; empty when unavailable. */\n\ttailText: string;\n\ttailBytes: number;\n\ttotalBytes: number;\n\ttailTruncated: boolean;\n\ttailStartsMidLine: boolean;\n\t/** Set when the output file could not be read; the notification still fires. */\n\ttailError?: string;\n}\n\n/** One-shot signal that a running task appears blocked on interactive input. */\nexport interface BgStallNotification {\n\ttask: BgTask;\n\ttailText: string;\n\ttailBytes: number;\n\ttotalBytes: number;\n\ttailTruncated: boolean;\n\ttailStartsMidLine: boolean;\n\ttailError?: string;\n}\n\nexport interface OutputSlice {\n\ttext: string;\n\tsliceBytes: number;\n\ttotalBytes: number;\n\t/** True when the file holds more bytes than the slice. */\n\ttruncated: boolean;\n\t/** True in tail mode when the slice starts mid-line. */\n\tstartsMidLine: boolean;\n}\n\n/** Outcome of a bounded wait: terminal delivery, or the wait window expiring. */\nexport type WaitOutcome = { outcome: \"terminal\"; task: BgTask } | { outcome: \"timeout\"; task: BgTask };\n\n/**\n * Read a bounded slice from the head or tail of a file via positioned reads —\n * never the whole file. Tail reads skip UTF-8 continuation bytes so the text\n * starts on a codepoint boundary.\n */\nexport async function readOutputSlice(\n\tfilePath: string,\n\toptions: { mode: \"head\" | \"tail\"; maxBytes: number },\n): Promise<OutputSlice> {\n\tconst file = await open(filePath, \"r\");\n\ttry {\n\t\tconst { size } = await file.stat();\n\t\tlet length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));\n\t\tlet position = options.mode === \"tail\" ? size - length : 0;\n\t\tlet startsMidLine = false;\n\n\t\tif (options.mode === \"tail\" && position > 0 && length > 0) {\n\t\t\t// Probe from one byte before the slice: probe[0] tells whether the\n\t\t\t// slice starts at a line boundary, the rest lets us skip UTF-8\n\t\t\t// continuation bytes (0b10xxxxxx) so we never split a codepoint.\n\t\t\tconst probeStart = position - 1;\n\t\t\tconst probe = Buffer.alloc(Math.min(5, size - probeStart));\n\t\t\tawait file.read(probe, 0, probe.length, probeStart);\n\t\t\tlet skip = 0;\n\t\t\twhile (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {\n\t\t\t\tskip++;\n\t\t\t}\n\t\t\tposition += skip;\n\t\t\tlength -= skip;\n\t\t\tstartsMidLine = probe[skip] !== 0x0a;\n\t\t}\n\n\t\tconst buffer = Buffer.alloc(Math.max(0, length));\n\t\tconst bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;\n\t\treturn {\n\t\t\ttext: buffer.subarray(0, bytesRead).toString(\"utf8\"),\n\t\t\tsliceBytes: bytesRead,\n\t\t\ttotalBytes: size,\n\t\t\ttruncated: size > bytesRead,\n\t\t\tstartsMidLine,\n\t\t};\n\t} finally {\n\t\tawait file.close();\n\t}\n}\n\n/**\n * Read the last `maxBytes` of a file starting no earlier than `fromByte` — the\n * bounded delta since an offset, tail-aligned so the outcome (at the end of\n * the output) is always included. `fromByte` is clamped into the file; the\n * returned `fromByte` is where the slice actually starts.\n */\nexport async function readOutputSince(\n\tfilePath: string,\n\tfromByte: number,\n\tmaxBytes: number,\n): Promise<OutputSlice & { fromByte: number }> {\n\tconst file = await open(filePath, \"r\");\n\ttry {\n\t\tconst { size } = await file.stat();\n\t\tconst floor = Math.min(Math.max(0, Math.floor(fromByte)), size);\n\t\tlet length = Math.min(size - floor, Math.max(0, Math.floor(maxBytes)));\n\t\tlet position = size - length;\n\t\tlet startsMidLine = false;\n\n\t\tif (position > 0 && length > 0) {\n\t\t\t// Same boundary probe as tail reads: probe[0] tells whether the slice\n\t\t\t// starts at a line boundary, the rest skips UTF-8 continuation bytes.\n\t\t\tconst probeStart = position - 1;\n\t\t\tconst probe = Buffer.alloc(Math.min(5, size - probeStart));\n\t\t\tawait file.read(probe, 0, probe.length, probeStart);\n\t\t\tlet skip = 0;\n\t\t\twhile (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {\n\t\t\t\tskip++;\n\t\t\t}\n\t\t\tposition += skip;\n\t\t\tlength -= skip;\n\t\t\tstartsMidLine = probe[skip] !== 0x0a;\n\t\t}\n\n\t\tconst buffer = Buffer.alloc(Math.max(0, length));\n\t\tconst bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;\n\t\treturn {\n\t\t\ttext: buffer.subarray(0, bytesRead).toString(\"utf8\"),\n\t\t\tsliceBytes: bytesRead,\n\t\t\ttotalBytes: size,\n\t\t\ttruncated: position > floor,\n\t\t\tstartsMidLine,\n\t\t\tfromByte: position,\n\t\t};\n\t} finally {\n\t\tawait file.close();\n\t}\n}\n\nexport interface BackgroundRegistryOptions {\n\toperations: BashOperations;\n\t/** Directory for task output files. Defaults to the system temp directory. */\n\toutputDir?: string;\n\tmaxOutputBytes?: number;\n\tmaxRunningTasks?: number;\n\tnotifyTailBytes?: number;\n\t/** Called once per finished task; a synchronous throw rolls back `notified`. */\n\tonNotify: (notification: BgTaskNotification) => void;\n\t/** Called at most once per task when its output stalls on an interactive-looking prompt. */\n\tonStall?: (notification: BgStallNotification) => void;\n\t/** Stall watchdog tuning (tests inject short windows); defaults follow Claude Code's CC-1175. */\n\tstall?: { pollIntervalMs?: number; thresholdMs?: number; tailBytes?: number };\n\t/** Called whenever a task starts or reaches a terminal state. */\n\tonChange: () => void;\n\tnow?: () => number;\n}\n\ninterface TaskRuntime {\n\tcontroller: AbortController;\n\tstream: WriteStream;\n\tkillRequested: boolean;\n\toverflow: boolean;\n\tstreamError?: string;\n\tfinalized: boolean;\n\t/** Registered waitForResult callers; see the claim protocol in finalize(). */\n\twaiters: number;\n\tstallTimer: ReturnType<typeof setInterval> | undefined;\n\tstallLastSize: number;\n\tstallLastGrowthAt: number;\n\t/** One-shot latch: the stall notification fires at most once per task. */\n\tstallNotified: boolean;\n\tdone: Promise<void>;\n\tresolveDone: () => void;\n}\n\nexport type ResolveTaskResult =\n\t| { ok: true; task: BgTask }\n\t| { ok: false; reason: \"not-found\" | \"ambiguous\"; candidates: BgTask[] };\n\nexport class BackgroundTaskRegistry {\n\tprivate readonly operations: BashOperations;\n\tprivate readonly outputDir: string;\n\tprivate readonly maxOutputBytes: number;\n\tprivate readonly maxRunningTasks: number;\n\tprivate readonly notifyTailBytes: number;\n\tprivate readonly onNotify: (notification: BgTaskNotification) => void;\n\tprivate readonly onStall: ((notification: BgStallNotification) => void) | undefined;\n\tprivate readonly stallPollIntervalMs: number;\n\tprivate readonly stallThresholdMs: number;\n\tprivate readonly stallTailBytes: number;\n\tprivate readonly onChange: () => void;\n\tprivate readonly now: () => number;\n\n\tprivate readonly tasks = new Map<string, BgTask>();\n\tprivate readonly runtimes = new Map<string, TaskRuntime>();\n\tprivate shuttingDown = false;\n\n\tconstructor(options: BackgroundRegistryOptions) {\n\t\tthis.operations = options.operations;\n\t\tthis.outputDir = options.outputDir ?? tmpdir();\n\t\tthis.maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n\t\tthis.maxRunningTasks = options.maxRunningTasks ?? DEFAULT_MAX_RUNNING_TASKS;\n\t\tthis.notifyTailBytes = options.notifyTailBytes ?? DEFAULT_NOTIFY_TAIL_BYTES;\n\t\tthis.onNotify = options.onNotify;\n\t\tthis.onStall = options.onStall;\n\t\tthis.stallPollIntervalMs = options.stall?.pollIntervalMs ?? STALL_POLL_INTERVAL_MS;\n\t\tthis.stallThresholdMs = options.stall?.thresholdMs ?? STALL_THRESHOLD_MS;\n\t\tthis.stallTailBytes = options.stall?.tailBytes ?? STALL_TAIL_BYTES;\n\t\tthis.onChange = options.onChange;\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tget isShuttingDown(): boolean {\n\t\treturn this.shuttingDown;\n\t}\n\n\tstartTask(input: {\n\t\tcommand: string;\n\t\tcwd: string;\n\t\tdescription?: string;\n\t\ttimeoutSeconds?: number;\n\t\tenv?: NodeJS.ProcessEnv;\n\t}): BgTask {\n\t\tif (this.shuttingDown) {\n\t\t\tthrow new Error(\"Pi is shutting down; background task not started.\");\n\t\t}\n\t\tconst running = this.listTasks().filter((task) => task.status === \"running\");\n\t\tif (running.length >= this.maxRunningTasks) {\n\t\t\tconst list = running.map((task) => ` ${task.id} ${firstCommandLine(task.command)}`).join(\"\\n\");\n\t\t\tthrow new Error(\n\t\t\t\t`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one first (bg action kill):\\n${list}`,\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.newTaskId();\n\t\tconst task: BgTask = {\n\t\t\tid,\n\t\t\tcommand: input.command,\n\t\t\tdescription: input.description,\n\t\t\tcwd: input.cwd,\n\t\t\tstatus: \"running\",\n\t\t\tstartedAt: this.now(),\n\t\t\texitCode: undefined,\n\t\t\toutputPath: join(this.outputDir, `pi-${id}.log`),\n\t\t\toutputBytes: 0,\n\t\t\toutputTruncated: false,\n\t\t\ttimeoutSeconds: input.timeoutSeconds,\n\t\t\tnotified: false,\n\t\t\tstalled: false,\n\t\t};\n\n\t\tlet resolveDone!: () => void;\n\t\tconst done = new Promise<void>((resolve) => {\n\t\t\tresolveDone = resolve;\n\t\t});\n\t\t// Create the file synchronously so read/wait and the /bg viewer never see\n\t\t// ENOENT between task start and the stream's async open. The exclusive\n\t\t// 'wx' flag fails on any existing path — including a symlink — so creation\n\t\t// can never truncate a file a stale id (or an attacker-planted link) points at.\n\t\tcreateOutputFileExclusively(task.outputPath);\n\t\tconst runtime: TaskRuntime = {\n\t\t\tcontroller: new AbortController(),\n\t\t\tstream: createWriteStream(task.outputPath, { flags: \"a\" }),\n\t\t\tkillRequested: false,\n\t\t\toverflow: false,\n\t\t\tfinalized: false,\n\t\t\twaiters: 0,\n\t\t\tstallTimer: undefined,\n\t\t\tstallLastSize: 0,\n\t\t\tstallLastGrowthAt: this.now(),\n\t\t\tstallNotified: false,\n\t\t\tdone,\n\t\t\tresolveDone,\n\t\t};\n\t\truntime.stream.on(\"error\", (error) => {\n\t\t\tif (runtime.finalized) return;\n\t\t\truntime.streamError = error.message;\n\t\t\truntime.controller.abort();\n\t\t});\n\n\t\tthis.tasks.set(id, task);\n\t\tthis.runtimes.set(id, runtime);\n\t\tthis.armStallWatchdog(task, runtime);\n\t\tvoid this.run(task, runtime, input.env);\n\t\tthis.onChange();\n\t\treturn task;\n\t}\n\n\tgetTask(id: string): BgTask | undefined {\n\t\treturn this.tasks.get(id);\n\t}\n\n\t/** Running tasks first (oldest first), then finished tasks (newest first). */\n\tlistTasks(): BgTask[] {\n\t\tconst all = [...this.tasks.values()];\n\t\tconst running = all.filter((task) => task.status === \"running\");\n\t\trunning.sort((a, b) => a.startedAt - b.startedAt);\n\t\tconst ended = all.filter((task) => task.status !== \"running\");\n\t\tended.sort((a, b) => (b.endedAt ?? b.startedAt) - (a.endedAt ?? a.startedAt));\n\t\treturn [...running, ...ended];\n\t}\n\n\t/** Resolve a task by id or unique id prefix; the \"bg-\" prefix may be omitted. */\n\tresolveTask(idPrefix: string): ResolveTaskResult {\n\t\tconst needle = idPrefix.trim();\n\t\tif (!needle) return { ok: false, reason: \"not-found\", candidates: [] };\n\t\tconst exact = this.tasks.get(needle) ?? this.tasks.get(`bg-${needle}`);\n\t\tif (exact) return { ok: true, task: exact };\n\t\tconst candidates = [...this.tasks.values()].filter(\n\t\t\t(task) => task.id.startsWith(needle) || task.id.startsWith(`bg-${needle}`),\n\t\t);\n\t\tconst first = candidates[0];\n\t\tif (candidates.length === 1 && first) return { ok: true, task: first };\n\t\tif (candidates.length === 0) return { ok: false, reason: \"not-found\", candidates: [] };\n\t\treturn { ok: false, reason: \"ambiguous\", candidates };\n\t}\n\n\tkillTask(id: string): { killed: true } | { killed: false; reason: \"not-found\" | \"not-running\" } {\n\t\tconst task = this.tasks.get(id);\n\t\tconst runtime = this.runtimes.get(id);\n\t\tif (!task || !runtime) return { killed: false, reason: \"not-found\" };\n\t\tif (task.status !== \"running\" || runtime.finalized) return { killed: false, reason: \"not-running\" };\n\t\truntime.killRequested = true;\n\t\truntime.controller.abort();\n\t\treturn { killed: true };\n\t}\n\n\tcounts(): Record<BgTaskStatus, number> & { total: number; stalled: number } {\n\t\tconst counts = { running: 0, completed: 0, failed: 0, killed: 0, timeout: 0, total: 0, stalled: 0 };\n\t\tfor (const task of this.tasks.values()) {\n\t\t\tcounts[task.status]++;\n\t\t\tcounts.total++;\n\t\t\tif (task.stalled) counts.stalled++;\n\t\t}\n\t\treturn counts;\n\t}\n\n\t/** Resolves once the task has finalized, including its notification attempt. */\n\tasync waitForTask(id: string): Promise<BgTask> {\n\t\tconst task = this.tasks.get(id);\n\t\tif (!task) throw new Error(`Unknown background task: ${id}`);\n\t\tawait this.runtimes.get(id)?.done;\n\t\treturn task;\n\t}\n\n\t/**\n\t * Bounded model-facing wait with the claim protocol: a waiter registered\n\t * when the task finalizes takes over delivery — finalize sees waiters > 0\n\t * and suppresses the followUp notification, so the completion is delivered\n\t * exactly once, inline here. If the window expires first, the followUp\n\t * notification is left untouched and fires as usual.\n\t *\n\t * All interleavings are deterministic on JS's single thread: a waiter that\n\t * registers before finalize's synchronous claim check claims delivery; one\n\t * that arrives after sees a terminal status and returns it immediately\n\t * (the followUp may also arrive — an idempotent repeat, not a lie).\n\t */\n\tasync waitForResult(id: string, timeoutMs: number): Promise<WaitOutcome> {\n\t\tconst task = this.tasks.get(id);\n\t\tif (!task) throw new Error(`Unknown background task: ${id}`);\n\t\tconst runtime = this.runtimes.get(id);\n\t\tif (!runtime) throw new Error(`Unknown background task: ${id}`);\n\t\tif (task.status !== \"running\") return { outcome: \"terminal\", task };\n\n\t\truntime.waiters++;\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tconst timedOut = await new Promise<boolean>((resolve) => {\n\t\t\t\ttimer = setTimeout(() => resolve(true), timeoutMs);\n\t\t\t\tvoid runtime.done.then(() => resolve(false));\n\t\t\t});\n\t\t\t// done may resolve right after the timer fired (race window); a task\n\t\t\t// that reached a terminal state during the wait is delivered here.\n\t\t\tif (!timedOut || task.status !== \"running\") return { outcome: \"terminal\", task };\n\t\t\treturn { outcome: \"timeout\", task };\n\t\t} finally {\n\t\t\truntime.waiters--;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t}\n\t}\n\n\t/**\n\t * Mute notifications, refuse new tasks, abort every running task, and wait\n\t * (bounded) for their finalization.\n\t */\n\tasync shutdown(): Promise<void> {\n\t\tthis.shuttingDown = true;\n\t\tconst pending: Promise<void>[] = [];\n\t\tfor (const [id, runtime] of this.runtimes) {\n\t\t\tconst task = this.tasks.get(id);\n\t\t\tif (task?.status === \"running\" && !runtime.finalized) {\n\t\t\t\truntime.killRequested = true;\n\t\t\t\truntime.controller.abort();\n\t\t\t}\n\t\t\tpending.push(runtime.done);\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst grace = new Promise<void>((resolve) => {\n\t\t\ttimer = setTimeout(resolve, SHUTDOWN_GRACE_MS);\n\t\t});\n\t\ttry {\n\t\t\tawait Promise.race([Promise.allSettled(pending).then(() => undefined), grace]);\n\t\t} finally {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t}\n\t}\n\n\t/**\n\t * Stall watchdog (Claude Code CC-1175): poll the output file; when output\n\t * stops growing past the threshold AND the tail looks like an interactive\n\t * prompt, flag the task and fire a one-shot notification. Merely-slow tasks\n\t * never match; recovery clears the flag (the notification never re-fires).\n\t */\n\tprivate armStallWatchdog(task: BgTask, runtime: TaskRuntime): void {\n\t\tif (!this.onStall) return;\n\t\truntime.stallTimer = setInterval(() => this.checkStall(task, runtime), this.stallPollIntervalMs);\n\t\truntime.stallTimer.unref?.();\n\t}\n\n\tprivate clearStallWatchdog(runtime: TaskRuntime): void {\n\t\tif (runtime.stallTimer !== undefined) {\n\t\t\tclearInterval(runtime.stallTimer);\n\t\t\truntime.stallTimer = undefined;\n\t\t}\n\t}\n\n\tprivate checkStall(task: BgTask, runtime: TaskRuntime): void {\n\t\tif (runtime.finalized || this.shuttingDown) return;\n\t\tconst size = task.outputBytes;\n\t\tif (size > runtime.stallLastSize) {\n\t\t\truntime.stallLastSize = size;\n\t\t\truntime.stallLastGrowthAt = this.now();\n\t\t\tif (task.stalled) {\n\t\t\t\t// Output resumed: reflect reality in listings, never re-notify.\n\t\t\t\ttask.stalled = false;\n\t\t\t\tthis.onChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (this.now() - runtime.stallLastGrowthAt < this.stallThresholdMs) return;\n\t\tvoid this.probeStallTail(task, runtime);\n\t}\n\n\tprivate async probeStallTail(task: BgTask, runtime: TaskRuntime): Promise<void> {\n\t\t// Re-check inside the async boundary: finalize may have raced the probe.\n\t\tif (runtime.finalized || this.shuttingDown) return;\n\t\tlet tailText = \"\";\n\t\tlet tailError: string | undefined;\n\t\ttry {\n\t\t\tconst slice = await readOutputSlice(task.outputPath, { mode: \"tail\", maxBytes: this.stallTailBytes });\n\t\t\ttailText = slice.text;\n\t\t} catch (error) {\n\t\t\ttailError = error instanceof Error ? error.message : String(error);\n\t\t}\n\t\tif (runtime.finalized || this.shuttingDown) return;\n\t\t// Merely slow: not prompt-shaped. Reset so the next check is a full\n\t\t// threshold window out instead of re-probing on every tick.\n\t\tif (!tailError && !looksLikePrompt(tailText)) {\n\t\t\truntime.stallLastGrowthAt = this.now();\n\t\t\treturn;\n\t\t}\n\t\ttask.stalled = true;\n\t\tthis.onChange();\n\t\tif (runtime.stallNotified) return;\n\t\truntime.stallNotified = true;\n\n\t\tlet tailBytes = 0;\n\t\tlet totalBytes = task.outputBytes;\n\t\tlet tailTruncated = false;\n\t\tlet tailStartsMidLine = false;\n\t\tif (!tailError) {\n\t\t\ttry {\n\t\t\t\tconst slice = await readOutputSlice(task.outputPath, { mode: \"tail\", maxBytes: this.stallTailBytes });\n\t\t\t\ttailText = slice.text;\n\t\t\t\ttailBytes = slice.sliceBytes;\n\t\t\t\ttotalBytes = slice.totalBytes;\n\t\t\t\ttailTruncated = slice.truncated;\n\t\t\t\ttailStartsMidLine = slice.startsMidLine;\n\t\t\t} catch (error) {\n\t\t\t\ttailError = error instanceof Error ? error.message : String(error);\n\t\t\t}\n\t\t}\n\t\tthis.onStall?.({\n\t\t\ttask,\n\t\t\ttailText: sanitizeBinaryOutput(tailText),\n\t\t\ttailBytes,\n\t\t\ttotalBytes,\n\t\t\ttailTruncated,\n\t\t\ttailStartsMidLine,\n\t\t\ttailError,\n\t\t});\n\t}\n\n\tprivate newTaskId(): string {\n\t\twhile (true) {\n\t\t\tconst id = `bg-${randomBytes(3).toString(\"hex\")}`;\n\t\t\tif (!this.tasks.has(id)) return id;\n\t\t}\n\t}\n\n\tprivate async run(task: BgTask, runtime: TaskRuntime, env?: NodeJS.ProcessEnv): Promise<void> {\n\t\tlet outcome: { exitCode?: number | null; error?: unknown };\n\t\ttry {\n\t\t\tconst result = await this.operations.exec(task.command, task.cwd, {\n\t\t\t\tonData: (data) => this.handleData(task, runtime, data),\n\t\t\t\tsignal: runtime.controller.signal,\n\t\t\t\ttimeout: task.timeoutSeconds,\n\t\t\t\tenv,\n\t\t\t});\n\t\t\toutcome = { exitCode: result.exitCode };\n\t\t} catch (error) {\n\t\t\toutcome = { error };\n\t\t}\n\t\tawait this.finalize(task, runtime, outcome);\n\t}\n\n\tprivate handleData(task: BgTask, runtime: TaskRuntime, data: Buffer): void {\n\t\tif (runtime.finalized || runtime.overflow || runtime.streamError !== undefined) return;\n\t\tconst next = task.outputBytes + data.length;\n\t\tif (next <= this.maxOutputBytes) {\n\t\t\truntime.stream.write(data);\n\t\t\ttask.outputBytes = next;\n\t\t\treturn;\n\t\t}\n\t\tconst budget = this.maxOutputBytes - task.outputBytes;\n\t\tif (budget > 0) {\n\t\t\truntime.stream.write(data.subarray(0, budget));\n\t\t\ttask.outputBytes = this.maxOutputBytes;\n\t\t}\n\t\truntime.stream.write(`\\n[output limit ${formatSize(this.maxOutputBytes)} exceeded; task killed]\\n`);\n\t\ttask.outputTruncated = true;\n\t\truntime.overflow = true;\n\t\truntime.controller.abort();\n\t}\n\n\tprivate async finalize(\n\t\ttask: BgTask,\n\t\truntime: TaskRuntime,\n\t\toutcome: { exitCode?: number | null; error?: unknown },\n\t): Promise<void> {\n\t\tif (runtime.finalized) return;\n\t\truntime.finalized = true;\n\t\tthis.clearStallWatchdog(runtime);\n\t\ttask.stalled = false;\n\n\t\t// Flush before setting terminal state: the output file must hold the\n\t\t// complete truth by the time anyone is told the task ended.\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tconst stream = runtime.stream;\n\t\t\tif (stream.destroyed) {\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstream.on(\"close\", resolve);\n\t\t\tstream.end(() => resolve());\n\t\t});\n\n\t\tconst verdict = this.classifyOutcome(runtime, outcome);\n\t\ttask.status = verdict.status;\n\t\tif (verdict.error !== undefined) task.error = verdict.error;\n\t\ttask.exitCode = outcome.exitCode;\n\t\ttask.endedAt = this.now();\n\t\tthis.onChange();\n\t\t// Claim protocol: at least one waitForResult caller is waiting on this\n\t\t// completion, so it delivers the result inline. Mark notified to skip the\n\t\t// followUp — the completion must be delivered exactly once.\n\t\tif (runtime.waiters > 0) {\n\t\t\ttask.notified = true;\n\t\t}\n\t\tawait this.notifyCompletion(task);\n\t\truntime.resolveDone();\n\t}\n\n\tprivate classifyOutcome(\n\t\truntime: TaskRuntime,\n\t\toutcome: { exitCode?: number | null; error?: unknown },\n\t): { status: BgTaskStatus; error?: string } {\n\t\tif (runtime.overflow) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: `Output exceeded the ${formatSize(this.maxOutputBytes)} limit; the task was killed.`,\n\t\t\t};\n\t\t}\n\t\tif (runtime.streamError !== undefined) {\n\t\t\treturn { status: \"failed\", error: `Output file error: ${runtime.streamError}` };\n\t\t}\n\t\tif (runtime.killRequested) return { status: \"killed\" };\n\t\tconst error = outcome.error;\n\t\tif (error instanceof Error) {\n\t\t\tif (error.message === \"aborted\") return { status: \"killed\" };\n\t\t\tif (error.message.startsWith(\"timeout:\")) {\n\t\t\t\treturn { status: \"timeout\", error: `Timed out after ${error.message.slice(\"timeout:\".length)}s.` };\n\t\t\t}\n\t\t\treturn { status: \"failed\", error: error.message };\n\t\t}\n\t\tif (error !== undefined) return { status: \"failed\", error: String(error) };\n\t\tif (outcome.exitCode === 0) return { status: \"completed\" };\n\t\tif (outcome.exitCode === null) return { status: \"failed\", error: \"Command was terminated by a signal.\" };\n\t\treturn { status: \"failed\", error: `Command exited with code ${outcome.exitCode}` };\n\t}\n\n\tprivate async notifyCompletion(task: BgTask): Promise<void> {\n\t\tif (this.shuttingDown || task.notified) return;\n\t\ttask.notified = true;\n\n\t\tlet tailText = \"\";\n\t\tlet tailBytes = 0;\n\t\tlet totalBytes = task.outputBytes;\n\t\tlet tailTruncated = false;\n\t\tlet tailStartsMidLine = false;\n\t\tlet tailError: string | undefined;\n\t\ttry {\n\t\t\tconst slice = await readOutputSlice(task.outputPath, { mode: \"tail\", maxBytes: this.notifyTailBytes });\n\t\t\ttailText = sanitizeBinaryOutput(slice.text);\n\t\t\ttailBytes = slice.sliceBytes;\n\t\t\ttotalBytes = slice.totalBytes;\n\t\t\ttailTruncated = slice.truncated;\n\t\t\ttailStartsMidLine = slice.startsMidLine;\n\t\t} catch (error) {\n\t\t\ttailError = error instanceof Error ? error.message : String(error);\n\t\t}\n\n\t\ttry {\n\t\t\tthis.onNotify({ task, tailText, tailBytes, totalBytes, tailTruncated, tailStartsMidLine, tailError });\n\t\t} catch {\n\t\t\t// The send failed synchronously (e.g. stale extension runtime after a\n\t\t\t// session replacement); do not pretend the notification was delivered.\n\t\t\ttask.notified = false;\n\t\t}\n\t}\n}\n\n/**\n * Create an empty output file exclusively ('wx'): fails on any existing path,\n * including a symlink, so creation can never truncate what the path points at.\n */\nexport function createOutputFileExclusively(path: string): void {\n\tconst handle = openSync(path, \"wx\");\n\tcloseSync(handle);\n}\n\n/** First non-empty line of a command, trimmed — for one-line task labels. */\nexport function firstCommandLine(command: string): string {\n\tconst line = command.split(/\\r?\\n/).find((candidate) => candidate.trim().length > 0) ?? command;\n\treturn line.trim();\n}\n\n/** Compact duration like Pi's own timers: 12s, 3m05s, 1h02m. */\nexport function formatDuration(ms: number): string {\n\tconst totalSeconds = Math.max(0, Math.round(ms / 1000));\n\tif (totalSeconds < 60) return `${totalSeconds}s`;\n\tconst minutes = Math.floor(totalSeconds / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (minutes < 60) return `${minutes}m${String(seconds).padStart(2, \"0\")}s`;\n\treturn `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, \"0\")}m`;\n}\n"]}
@@ -1,24 +1,48 @@
1
1
  /**
2
- * Transcript rendering for the background extension: tool call/result rows
3
- * and the completion-notification message. Style follows the built-in bash
4
- * presentation: a `$` prompt row with an `&` marker for the background call.
2
+ * Transcript rendering for the background extension: the single bg tool's
3
+ * call/result rows (dispatched on action) and the completion-notification
4
+ * message. Style follows the built-in bash presentation: a `$` prompt row
5
+ * with an `&` marker for the background call.
5
6
  */
6
7
  import { type Component } from "@earendil-works/pi-tui";
7
8
  import type { AgentToolResult, MessageRenderOptions, ToolRenderContext, ToolRenderResultOptions } from "../../core/extensions/types.ts";
8
9
  import type { CustomMessage } from "../../core/messages.ts";
9
10
  import { type Theme } from "../../modes/interactive/theme/theme.ts";
10
- import type { BgBashDetails, BgBashInput, BgKillInput, BgLogsInput, BgNotificationDetails } from "./index.ts";
11
+ import type { BgDetails, BgInput, BgNotificationDetails } from "./index.ts";
11
12
  import { type BgTaskStatus } from "./registry.ts";
12
- export declare function statusGlyph(status: BgTaskStatus): string;
13
- export declare function statusColor(status: BgTaskStatus): "success" | "error" | "warning" | "accent";
13
+ /** Live peek at a waited-on task, fed from the registry by the call renderer. */
14
+ export interface BgTaskLive {
15
+ status: BgTaskStatus;
16
+ outputBytes: number;
17
+ }
18
+ export type WaitLiveProbe = (taskId: string) => BgTaskLive | undefined;
19
+ /** Per-call live-refresh state owned by the shell's render context. */
20
+ export interface BgRenderState {
21
+ refreshTimer?: ReturnType<typeof setTimeout>;
22
+ /** First pending render of a wait call; anchors the elapsed display. */
23
+ waitStartedAt?: number;
24
+ /** outputBytes snapshot at wait start; anchors the "+new output" display. */
25
+ waitBaselineBytes?: number;
26
+ }
27
+ /**
28
+ * Arm a one-shot 1s refresh while a wait call is pending, clear it once
29
+ * settled. The timer fires → context.invalidate() → renderCall re-runs →
30
+ * re-arms, so at most one armed timer exists per tool row. Unref'd: it can
31
+ * never hold the process open.
32
+ */
33
+ export declare function scheduleWaitRefresh(context: ToolRenderContext<BgRenderState>, pending: boolean): void;
34
+ /** Basename of a path, slash-normalized — compact display of output files. */
35
+ export declare function fileNameOf(path: string): string;
36
+ export declare function statusGlyph(status: BgTaskStatus, stalled?: boolean): string;
37
+ export declare function statusColor(status: BgTaskStatus, stalled?: boolean): "success" | "error" | "warning" | "accent";
14
38
  /** First command line truncated to a visible-character budget with an ellipsis. */
15
39
  export declare function commandLabel(command: string, width: number): string;
16
- export declare function renderBgBashCall(args: BgBashInput, theme: Theme, context: ToolRenderContext): Component;
17
- export declare function renderBgBashResult(result: AgentToolResult<BgBashDetails | undefined>, _options: ToolRenderResultOptions, theme: Theme, context: ToolRenderContext): Component;
18
- export declare function renderBgLogsCall(args: BgLogsInput, theme: Theme): Component;
19
- export declare function renderBgKillCall(args: BgKillInput, theme: Theme): Component;
40
+ export declare function renderBgCall(args: BgInput, theme: Theme, context: ToolRenderContext<BgRenderState>, getTaskLive?: WaitLiveProbe): Component;
41
+ /** Mirror the execution-side clamp so the shown window matches the real one. */
42
+ export declare function clampWaitMs(waitMs: number | undefined): number;
43
+ export declare function renderBgResult(result: AgentToolResult<BgDetails | undefined>, options: ToolRenderResultOptions, theme: Theme, context: ToolRenderContext<BgRenderState>): Component;
20
44
  /**
21
- * Collapsed: status glyph, task id, command, and outcome on one line, output
45
+ * Collapsed: status glyph, task id, label, and outcome on one line, output
22
46
  * path below. Expanded adds the embedded output tail. Returns undefined for
23
47
  * malformed details so the default custom-message rendering takes over.
24
48
  */
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/render.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,SAAS,EAAkC,MAAM,wBAAwB,CAAC;AACxF,OAAO,KAAK,EACX,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAiB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC9G,OAAO,EAAE,KAAK,YAAY,EAAoC,MAAM,eAAe,CAAC;AAapF,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAYxD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAY5F;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAcvG;AAED,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,eAAe,CAAC,aAAa,GAAG,SAAS,CAAC,EAClD,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,iBAAiB,GACxB,SAAS,CAYX;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAQ3E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAE3E;AAWD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC7C,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,KAAK,GACV,SAAS,GAAG,SAAS,CAyBvB"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,SAAS,EAAkC,MAAM,wBAAwB,CAAC;AACxF,OAAO,KAAK,EACX,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAiB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,KAAK,EAAiB,SAAS,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC3F,OAAO,EAIN,KAAK,YAAY,EAGjB,MAAM,eAAe,CAAC;AASvB,iFAAiF;AACjF,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;AAEvE,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC7B,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IAC7C,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAmBrG;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAeD,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAa3E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAa/G;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnE;AAID,wBAAgB,YAAY,CAC3B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,EACzC,WAAW,CAAC,EAAE,aAAa,GACzB,SAAS,CAwBX;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAiED,wBAAgB,cAAc,CAC7B,MAAM,EAAE,eAAe,CAAC,SAAS,GAAG,SAAS,CAAC,EAC9C,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,GACvC,SAAS,CAkBX;AAoDD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC7C,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,KAAK,GACV,SAAS,GAAG,SAAS,CAoCvB"}