@astralyn/pi 0.84.6 → 0.84.8

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 (86) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/core/compaction/compaction.d.ts +2 -0
  3. package/dist/core/compaction/compaction.d.ts.map +1 -1
  4. package/dist/core/compaction/compaction.js +7 -7
  5. package/dist/core/compaction/compaction.js.map +1 -1
  6. package/dist/extensions/background/index.d.ts.map +1 -1
  7. package/dist/extensions/background/index.js +48 -72
  8. package/dist/extensions/background/index.js.map +1 -1
  9. package/dist/extensions/background/manager.d.ts.map +1 -1
  10. package/dist/extensions/background/manager.js +5 -3
  11. package/dist/extensions/background/manager.js.map +1 -1
  12. package/dist/extensions/background/registry.d.ts +11 -13
  13. package/dist/extensions/background/registry.d.ts.map +1 -1
  14. package/dist/extensions/background/registry.js +71 -80
  15. package/dist/extensions/background/registry.js.map +1 -1
  16. package/dist/extensions/background/render.d.ts.map +1 -1
  17. package/dist/extensions/background/render.js +17 -4
  18. package/dist/extensions/background/render.js.map +1 -1
  19. package/dist/extensions/deepwiki/constants.d.ts +2 -2
  20. package/dist/extensions/deepwiki/constants.d.ts.map +1 -1
  21. package/dist/extensions/deepwiki/constants.js +5 -5
  22. package/dist/extensions/deepwiki/constants.js.map +1 -1
  23. package/dist/extensions/deepwiki/schema.js +1 -1
  24. package/dist/extensions/deepwiki/schema.js.map +1 -1
  25. package/dist/extensions/question/constants.d.ts +2 -2
  26. package/dist/extensions/question/constants.d.ts.map +1 -1
  27. package/dist/extensions/question/constants.js +5 -2
  28. package/dist/extensions/question/constants.js.map +1 -1
  29. package/dist/extensions/router/dialog-host.d.ts +60 -0
  30. package/dist/extensions/router/dialog-host.d.ts.map +1 -0
  31. package/dist/extensions/router/dialog-host.js +196 -0
  32. package/dist/extensions/router/dialog-host.js.map +1 -0
  33. package/dist/extensions/router/probe.d.ts.map +1 -1
  34. package/dist/extensions/router/probe.js +2 -0
  35. package/dist/extensions/router/probe.js.map +1 -1
  36. package/dist/extensions/router/ui.d.ts.map +1 -1
  37. package/dist/extensions/router/ui.js +117 -67
  38. package/dist/extensions/router/ui.js.map +1 -1
  39. package/dist/extensions/subagent/activity.d.ts +9 -0
  40. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  41. package/dist/extensions/subagent/activity.js +21 -0
  42. package/dist/extensions/subagent/activity.js.map +1 -1
  43. package/dist/extensions/subagent/agents-command.d.ts.map +1 -1
  44. package/dist/extensions/subagent/agents-command.js +355 -71
  45. package/dist/extensions/subagent/agents-command.js.map +1 -1
  46. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  47. package/dist/extensions/subagent/agents.js +7 -3
  48. package/dist/extensions/subagent/agents.js.map +1 -1
  49. package/dist/extensions/subagent/constants.d.ts +1 -0
  50. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  51. package/dist/extensions/subagent/constants.js +4 -0
  52. package/dist/extensions/subagent/constants.js.map +1 -1
  53. package/dist/extensions/subagent/index.js +2 -2
  54. package/dist/extensions/subagent/index.js.map +1 -1
  55. package/dist/extensions/subagent/render.d.ts.map +1 -1
  56. package/dist/extensions/subagent/render.js +1 -7
  57. package/dist/extensions/subagent/render.js.map +1 -1
  58. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  59. package/dist/extensions/subagent/sdk-runner.js +19 -0
  60. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  61. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  62. package/dist/extensions/subagent/settings.js +11 -4
  63. package/dist/extensions/subagent/settings.js.map +1 -1
  64. package/dist/extensions/subagent/state.d.ts +9 -0
  65. package/dist/extensions/subagent/state.d.ts.map +1 -1
  66. package/dist/extensions/subagent/state.js +33 -2
  67. package/dist/extensions/subagent/state.js.map +1 -1
  68. package/dist/extensions/subagent/ui/model-list.d.ts +1 -0
  69. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  70. package/dist/extensions/subagent/ui/model-list.js +5 -0
  71. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  72. package/dist/extensions/todo/constants.d.ts +1 -1
  73. package/dist/extensions/todo/constants.d.ts.map +1 -1
  74. package/dist/extensions/todo/constants.js +10 -27
  75. package/dist/extensions/todo/constants.js.map +1 -1
  76. package/dist/extensions/todo/state.d.ts.map +1 -1
  77. package/dist/extensions/todo/state.js +27 -16
  78. package/dist/extensions/todo/state.js.map +1 -1
  79. package/dist/extensions/todo/view.d.ts.map +1 -1
  80. package/dist/extensions/todo/view.js +16 -5
  81. package/dist/extensions/todo/view.js.map +1 -1
  82. package/docs/bundled/extensions/background.md +3 -3
  83. package/docs/bundled/extensions/todo.md +2 -2
  84. package/docs/compaction.md +2 -0
  85. package/npm-shrinkwrap.json +2 -2
  86. package/package.json +1 -1
@@ -48,41 +48,52 @@ export function looksLikePrompt(tail) {
48
48
  const lastLine = tail.trimEnd().split("\n").pop() ?? "";
49
49
  return STALL_PROMPT_PATTERNS.some((pattern) => pattern.test(lastLine));
50
50
  }
51
+ /**
52
+ * Read `length` bytes at `start`, first nudging the start past any UTF-8
53
+ * continuation bytes so the text begins on a codepoint boundary. The same probe
54
+ * reveals whether the slice starts mid-line. A `start` of 0 is used as-is —
55
+ * there is nothing before it to align against.
56
+ */
57
+ async function readAligned(file, size, start, length) {
58
+ let position = start;
59
+ let remaining = length;
60
+ let startsMidLine = false;
61
+ if (position > 0 && remaining > 0) {
62
+ // Probe from one byte before the slice: probe[0] tells whether the slice
63
+ // starts at a line boundary, the rest lets us skip UTF-8 continuation
64
+ // bytes (0b10xxxxxx) so we never split a codepoint.
65
+ const probeStart = position - 1;
66
+ const probe = Buffer.alloc(Math.min(5, size - probeStart));
67
+ await file.read(probe, 0, probe.length, probeStart);
68
+ let skip = 0;
69
+ while (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {
70
+ skip++;
71
+ }
72
+ position += skip;
73
+ remaining -= skip;
74
+ startsMidLine = probe[skip] !== 0x0a;
75
+ }
76
+ const buffer = Buffer.alloc(Math.max(0, remaining));
77
+ const bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;
78
+ return { text: buffer.subarray(0, bytesRead).toString("utf8"), sliceBytes: bytesRead, startsMidLine, position };
79
+ }
51
80
  /**
52
81
  * Read a bounded slice from the head or tail of a file via positioned reads —
53
- * never the whole file. Tail reads skip UTF-8 continuation bytes so the text
54
- * starts on a codepoint boundary.
82
+ * never the whole file.
55
83
  */
56
84
  export async function readOutputSlice(filePath, options) {
57
85
  const file = await open(filePath, "r");
58
86
  try {
59
87
  const { size } = await file.stat();
60
- let length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));
61
- let position = options.mode === "tail" ? size - length : 0;
62
- let startsMidLine = false;
63
- if (options.mode === "tail" && position > 0 && length > 0) {
64
- // Probe from one byte before the slice: probe[0] tells whether the
65
- // slice starts at a line boundary, the rest lets us skip UTF-8
66
- // continuation bytes (0b10xxxxxx) so we never split a codepoint.
67
- const probeStart = position - 1;
68
- const probe = Buffer.alloc(Math.min(5, size - probeStart));
69
- await file.read(probe, 0, probe.length, probeStart);
70
- let skip = 0;
71
- while (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {
72
- skip++;
73
- }
74
- position += skip;
75
- length -= skip;
76
- startsMidLine = probe[skip] !== 0x0a;
77
- }
78
- const buffer = Buffer.alloc(Math.max(0, length));
79
- const bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;
88
+ const length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));
89
+ const start = options.mode === "tail" ? size - length : 0;
90
+ const slice = await readAligned(file, size, start, length);
80
91
  return {
81
- text: buffer.subarray(0, bytesRead).toString("utf8"),
82
- sliceBytes: bytesRead,
92
+ text: slice.text,
93
+ sliceBytes: slice.sliceBytes,
83
94
  totalBytes: size,
84
- truncated: size > bytesRead,
85
- startsMidLine,
95
+ truncated: size > slice.sliceBytes,
96
+ startsMidLine: slice.startsMidLine,
86
97
  };
87
98
  }
88
99
  finally {
@@ -100,32 +111,19 @@ export async function readOutputSince(filePath, fromByte, maxBytes) {
100
111
  try {
101
112
  const { size } = await file.stat();
102
113
  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;
114
+ const length = Math.min(size - floor, Math.max(0, Math.floor(maxBytes)));
115
+ const start = size - length;
116
+ // Decided before alignment: nudging the start past continuation bytes is
117
+ // not the same as dropping output the caller asked for.
118
+ const truncated = start > floor;
119
+ const slice = await readAligned(file, size, start, length);
122
120
  return {
123
- text: buffer.subarray(0, bytesRead).toString("utf8"),
124
- sliceBytes: bytesRead,
121
+ text: slice.text,
122
+ sliceBytes: slice.sliceBytes,
125
123
  totalBytes: size,
126
- truncated: position > floor,
127
- startsMidLine,
128
- fromByte: position,
124
+ truncated,
125
+ startsMidLine: slice.startsMidLine,
126
+ fromByte: slice.position,
129
127
  };
130
128
  }
131
129
  finally {
@@ -282,8 +280,11 @@ export class BackgroundTaskRegistry {
282
280
  * registers before finalize's synchronous claim check claims delivery; one
283
281
  * that arrives after sees a terminal status and returns it immediately
284
282
  * (the followUp may also arrive — an idempotent repeat, not a lie).
283
+ *
284
+ * An aborted wait (the turn was interrupted) hands the claim back: the
285
+ * caller's result is discarded, so it must not also swallow the followUp.
285
286
  */
286
- async waitForResult(id, timeoutMs) {
287
+ async waitForResult(id, timeoutMs, signal) {
287
288
  const task = this.tasks.get(id);
288
289
  if (!task)
289
290
  throw new Error(`Unknown background task: ${id}`);
@@ -298,7 +299,12 @@ export class BackgroundTaskRegistry {
298
299
  const timedOut = await new Promise((resolve) => {
299
300
  timer = setTimeout(() => resolve(true), timeoutMs);
300
301
  void runtime.done.then(() => resolve(false));
302
+ signal?.addEventListener("abort", () => resolve(true), { once: true });
301
303
  });
304
+ // Throwing from inside `try` matters: `finally` drops the waiter count
305
+ // first, so finalize sees waiters === 0 and still sends the followUp.
306
+ if (signal?.aborted)
307
+ throw new Error("aborted");
302
308
  // done may resolve right after the timer fired (race window); a task
303
309
  // that reached a terminal state during the wait is delivered here.
304
310
  if (!timedOut || task.status !== "running")
@@ -378,11 +384,10 @@ export class BackgroundTaskRegistry {
378
384
  // Re-check inside the async boundary: finalize may have raced the probe.
379
385
  if (runtime.finalized || this.shuttingDown)
380
386
  return;
381
- let tailText = "";
387
+ let slice;
382
388
  let tailError;
383
389
  try {
384
- const slice = await readOutputSlice(task.outputPath, { mode: "tail", maxBytes: this.stallTailBytes });
385
- tailText = slice.text;
390
+ slice = await readOutputSlice(task.outputPath, { mode: "tail", maxBytes: this.stallTailBytes });
386
391
  }
387
392
  catch (error) {
388
393
  tailError = error instanceof Error ? error.message : String(error);
@@ -390,40 +395,26 @@ export class BackgroundTaskRegistry {
390
395
  if (runtime.finalized || this.shuttingDown)
391
396
  return;
392
397
  // 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)) {
398
+ // threshold window out instead of re-probing on every tick. A tail that
399
+ // could not be read is reported, not dismissed as slow.
400
+ if (slice && !looksLikePrompt(slice.text)) {
395
401
  runtime.stallLastGrowthAt = this.now();
396
402
  return;
397
403
  }
398
- task.stalled = true;
399
- this.onChange();
404
+ if (!task.stalled) {
405
+ task.stalled = true;
406
+ this.onChange();
407
+ }
400
408
  if (runtime.stallNotified)
401
409
  return;
402
410
  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
411
  this.onStall?.({
421
412
  task,
422
- tailText: sanitizeBinaryOutput(tailText),
423
- tailBytes,
424
- totalBytes,
425
- tailTruncated,
426
- tailStartsMidLine,
413
+ tailText: sanitizeBinaryOutput(slice?.text ?? ""),
414
+ tailBytes: slice?.sliceBytes ?? 0,
415
+ totalBytes: slice?.totalBytes ?? task.outputBytes,
416
+ tailTruncated: slice?.truncated ?? false,
417
+ tailStartsMidLine: slice?.startsMidLine ?? false,
427
418
  tailError,
428
419
  });
429
420
  }
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAoB,MAAM,SAAS,CAAC;AACnF,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACzD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAG,IAAI,CAAC;AAElD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC,sEAAsE;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,qBAAqB,GAAa;IACvC,WAAW,EAAE,eAAe;IAC5B,WAAW,EAAE,eAAe;IAC5B,cAAc;IACd,gEAAgE,EAAE,qBAAqB;IACvF,wBAAwB;IACxB,aAAa;IACb,cAAc;CACd,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACxD,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxE,CAAC;AA+DD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,QAAgB,EAChB,OAAoD;IAEpD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC;QACJ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,aAAa,GAAG,KAAK,CAAC;QAE1B,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,QAAQ,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3D,mEAAmE;YACnE,+DAA+D;YAC/D,iEAAiE;YACjE,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC;YAC3D,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YACpD,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,OAAO,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,WAAW,EAAE,CAAC;gBAC1F,IAAI,EAAE,CAAC;YACR,CAAC;YACD,QAAQ,IAAI,IAAI,CAAC;YACjB,MAAM,IAAI,IAAI,CAAC;YACf,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;QACtC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1G,OAAO;YACN,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpD,UAAU,EAAE,SAAS;YACrB,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,IAAI,GAAG,SAAS;YAC3B,aAAa;SACb,CAAC;IACH,CAAC;YAAS,CAAC;QACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,QAAgB,EAChB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC;QACJ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAChE,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC;QAC7B,IAAI,aAAa,GAAG,KAAK,CAAC;QAE1B,IAAI,QAAQ,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,sEAAsE;YACtE,sEAAsE;YACtE,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC;YAC3D,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YACpD,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,OAAO,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,WAAW,EAAE,CAAC;gBAC1F,IAAI,EAAE,CAAC;YACR,CAAC;YACD,QAAQ,IAAI,IAAI,CAAC;YACjB,MAAM,IAAI,IAAI,CAAC;YACf,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;QACtC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1G,OAAO;YACN,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpD,UAAU,EAAE,SAAS;YACrB,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,QAAQ,GAAG,KAAK;YAC3B,aAAa;YACb,QAAQ,EAAE,QAAQ;SAClB,CAAC;IACH,CAAC;YAAS,CAAC;QACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACF,CAAC;AA0CD,MAAM,OAAO,sBAAsB;IAkBlC,YAAY,OAAkC;QAJ7B,UAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QAClC,aAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;QACnD,iBAAY,GAAG,KAAK,CAAC;QAG5B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;QACzE,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,KAAK,EAAE,cAAc,IAAI,sBAAsB,CAAC;QACnF,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,KAAK,EAAE,WAAW,IAAI,kBAAkB,CAAC;QACzE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,KAAK,EAAE,SAAS,IAAI,gBAAgB,CAAC;QACnE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,CAAC;IAED,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IAED,SAAS,CAAC,KAMT;QACA,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC7E,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,KAAK,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjG,MAAM,IAAI,KAAK,CACd,4CAA4C,IAAI,CAAC,eAAe,wCAAwC,IAAI,EAAE,CAC9G,CAAC;QACH,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAW;YACpB,EAAE;YACF,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;YAChD,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACd,CAAC;QAEF,IAAI,WAAwB,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC1C,WAAW,GAAG,OAAO,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,0EAA0E;QAC1E,uEAAuE;QACvE,2EAA2E;QAC3E,gFAAgF;QAChF,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAgB;YAC5B,UAAU,EAAE,IAAI,eAAe,EAAE;YACjC,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YAC1D,aAAa,EAAE,KAAK;YACpB,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,SAAS;YACrB,aAAa,EAAE,CAAC;YAChB,iBAAiB,EAAE,IAAI,CAAC,GAAG,EAAE;YAC7B,aAAa,EAAE,KAAK;YACpB,IAAI;YACJ,WAAW;SACX,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACpC,IAAI,OAAO,CAAC,SAAS;gBAAE,OAAO;YAC9B,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC;YACpC,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,CAAC,EAAU;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,8EAA8E;IAC9E,SAAS;QACR,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC9D,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,iFAAiF;IACjF,WAAW,CAAC,QAAgB;QAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;QACvE,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACjD,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,MAAM,EAAE,CAAC,CAC1E,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QACvE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QACvF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;IACvD,CAAC;IAED,QAAQ,CAAC,EAAU;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QACrE,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QACpG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;QAC7B,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,MAAM;QACL,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACpG,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,OAAO;gBAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,WAAW,CAAC,EAAU;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAClC,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,aAAa,CAAC,EAAU,EAAE,SAAiB;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAChE,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAEpE,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,KAAgD,CAAC;QACrD,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;gBACvD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;gBACnD,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;YACH,qEAAqE;YACrE,mEAAmE;YACnE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YACjF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACrC,CAAC;gBAAS,CAAC;YACV,OAAO,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,KAAK,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;IACF,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,IAAI,IAAI,EAAE,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC7B,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC5B,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,KAAgD,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC3C,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACV,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,gBAAgB,CAAC,IAAY,EAAE,OAAoB;QAC1D,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,OAAO,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACjG,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;IAEO,kBAAkB,CAAC,OAAoB;QAC9C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACtC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;QAChC,CAAC;IACF,CAAC;IAEO,UAAU,CAAC,IAAY,EAAE,OAAoB;QACpD,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9B,IAAI,IAAI,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;YAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;YAC7B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,gEAAgE;gBAChE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;gBACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,CAAC;YACD,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,gBAAgB;YAAE,OAAO;QAC3E,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,OAAoB;QAC9D,yEAAyE;QACzE,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACnD,IAAI,QAAQ,GAAG,EAAE,CAAC;QAClB,IAAI,SAA6B,CAAC;QAClC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;YACtG,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACnD,oEAAoE;QACpE,4DAA4D;QAC5D,IAAI,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvC,OAAO;QACR,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,OAAO,CAAC,aAAa;YAAE,OAAO;QAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;QAE7B,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,IAAI,CAAC;gBACJ,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;gBACtG,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC;gBACtB,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;gBAC7B,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;gBAC9B,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC;gBAChC,iBAAiB,GAAG,KAAK,CAAC,aAAa,CAAC;YACzC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACpE,CAAC;QACF,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,IAAI;YACJ,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC;YACxC,SAAS;YACT,UAAU;YACV,aAAa;YACb,iBAAiB;YACjB,SAAS;SACT,CAAC,CAAC;IACJ,CAAC;IAEO,SAAS;QAChB,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,EAAE,CAAC;QACpC,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,OAAoB,EAAE,GAAuB;QAC5E,IAAI,OAAsD,CAAC;QAC3D,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;gBACjE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;gBACtD,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM;gBACjC,OAAO,EAAE,IAAI,CAAC,cAAc;gBAC5B,GAAG;aACH,CAAC,CAAC;YACH,OAAO,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC;QACrB,CAAC;QACD,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAEO,UAAU,CAAC,IAAY,EAAE,OAAoB,EAAE,IAAY;QAClE,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;YAAE,OAAO;QACvF,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5C,IAAI,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,OAAO;QACR,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC;QACtD,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC;QACxC,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC;QACpG,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;QACxB,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,QAAQ,CACrB,IAAY,EACZ,OAAoB,EACpB,OAAsD;QAEtD,IAAI,OAAO,CAAC,SAAS;YAAE,OAAO;QAC9B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,qEAAqE;QACrE,4DAA4D;QAC5D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,OAAO,EAAE,CAAC;gBACV,OAAO;YACR,CAAC;YACD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,uEAAuE;QACvE,0EAA0E;QAC1E,4DAA4D;QAC5D,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;IAEO,eAAe,CACtB,OAAoB,EACpB,OAAsD;QAEtD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO;gBACN,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,uBAAuB,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,8BAA8B;aAC3F,CAAC;QACH,CAAC;QACD,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,sBAAsB,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;QACjF,CAAC;QACD,IAAI,OAAO,CAAC,aAAa;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACvD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;gBAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpG,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACnD,CAAC;QACD,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3E,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAC3D,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,qCAAqC,EAAE,CAAC;QACzG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,4BAA4B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;IACpF,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,IAAY;QAC1C,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC/C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,QAAQ,GAAG,EAAE,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,IAAI,SAA6B,CAAC;QAClC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;YACvG,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;YAC7B,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YAC9B,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC;YAChC,iBAAiB,GAAG,KAAK,CAAC,aAAa,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC;YACJ,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC,CAAC;QACvG,CAAC;QAAC,MAAM,CAAC;YACR,sEAAsE;YACtE,uEAAuE;YACvE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACvB,CAAC;IACF,CAAC;CACD;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAY;IACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACpC,SAAS,CAAC,MAAM,CAAC,CAAC;AACnB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC;IAChG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,EAAU;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACxD,IAAI,YAAY,GAAG,EAAE;QAAE,OAAO,GAAG,YAAY,GAAG,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;IAC3E,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AAChF,CAAC","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
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAoB,MAAM,SAAS,CAAC;AACnF,OAAO,EAAmB,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACzD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAG,IAAI,CAAC;AAElD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC,sEAAsE;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,qBAAqB,GAAa;IACvC,WAAW,EAAE,eAAe;IAC5B,WAAW,EAAE,eAAe;IAC5B,cAAc;IACd,gEAAgE,EAAE,qBAAqB;IACvF,wBAAwB;IACxB,aAAa;IACb,cAAc;CACd,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACxD,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxE,CAAC;AA2DD;;;;;GAKG;AACH,KAAK,UAAU,WAAW,CACzB,IAAgB,EAChB,IAAY,EACZ,KAAa,EACb,MAAc;IAEd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,SAAS,GAAG,MAAM,CAAC;IACvB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,QAAQ,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACnC,yEAAyE;QACzE,sEAAsE;QACtE,oDAAoD;QACpD,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACpD,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,OAAO,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,WAAW,EAAE,CAAC;YAC1F,IAAI,EAAE,CAAC;QACR,CAAC;QACD,QAAQ,IAAI,IAAI,CAAC;QACjB,SAAS,IAAI,IAAI,CAAC;QAClB,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACtC,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1G,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AACjH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,QAAgB,EAChB,OAAoD;IAEpD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC;QACJ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3D,OAAO;YACN,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,IAAI,GAAG,KAAK,CAAC,UAAU;YAClC,aAAa,EAAE,KAAK,CAAC,aAAa;SAClC,CAAC;IACH,CAAC;YAAS,CAAC;QACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,QAAgB,EAChB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC;QACJ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzE,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;QAC5B,yEAAyE;QACzE,wDAAwD;QACxD,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3D,OAAO;YACN,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,UAAU,EAAE,IAAI;YAChB,SAAS;YACT,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACxB,CAAC;IACH,CAAC;YAAS,CAAC;QACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACF,CAAC;AA0CD,MAAM,OAAO,sBAAsB;IAkBlC,YAAY,OAAkC;QAJ7B,UAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QAClC,aAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;QACnD,iBAAY,GAAG,KAAK,CAAC;QAG5B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;QACzE,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,KAAK,EAAE,cAAc,IAAI,sBAAsB,CAAC;QACnF,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,KAAK,EAAE,WAAW,IAAI,kBAAkB,CAAC;QACzE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,KAAK,EAAE,SAAS,IAAI,gBAAgB,CAAC;QACnE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,CAAC;IAED,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IAED,SAAS,CAAC,KAMT;QACA,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC7E,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,KAAK,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjG,MAAM,IAAI,KAAK,CACd,4CAA4C,IAAI,CAAC,eAAe,wCAAwC,IAAI,EAAE,CAC9G,CAAC;QACH,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAW;YACpB,EAAE;YACF,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;YAChD,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACd,CAAC;QAEF,IAAI,WAAwB,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC1C,WAAW,GAAG,OAAO,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,0EAA0E;QAC1E,uEAAuE;QACvE,2EAA2E;QAC3E,gFAAgF;QAChF,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAgB;YAC5B,UAAU,EAAE,IAAI,eAAe,EAAE;YACjC,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YAC1D,aAAa,EAAE,KAAK;YACpB,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,SAAS;YACrB,aAAa,EAAE,CAAC;YAChB,iBAAiB,EAAE,IAAI,CAAC,GAAG,EAAE;YAC7B,aAAa,EAAE,KAAK;YACpB,IAAI;YACJ,WAAW;SACX,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACpC,IAAI,OAAO,CAAC,SAAS;gBAAE,OAAO;YAC9B,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC;YACpC,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,CAAC,EAAU;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,8EAA8E;IAC9E,SAAS;QACR,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC9D,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,iFAAiF;IACjF,WAAW,CAAC,QAAgB;QAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;QACvE,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACjD,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,MAAM,EAAE,CAAC,CAC1E,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QACvE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QACvF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;IACvD,CAAC;IAED,QAAQ,CAAC,EAAU;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QACrE,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QACpG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;QAC7B,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,MAAM;QACL,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACpG,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,OAAO;gBAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,WAAW,CAAC,EAAU;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAClC,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,aAAa,CAAC,EAAU,EAAE,SAAiB,EAAE,MAAoB;QACtE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAChE,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAEpE,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,KAAgD,CAAC;QACrD,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;gBACvD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;gBACnD,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7C,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACxE,CAAC,CAAC,CAAC;YACH,uEAAuE;YACvE,sEAAsE;YACtE,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;YAChD,qEAAqE;YACrE,mEAAmE;YACnE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YACjF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACrC,CAAC;gBAAS,CAAC;YACV,OAAO,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,KAAK,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;IACF,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,IAAI,IAAI,EAAE,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC7B,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC5B,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,KAAgD,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC3C,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACV,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,gBAAgB,CAAC,IAAY,EAAE,OAAoB;QAC1D,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,OAAO,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACjG,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;IAEO,kBAAkB,CAAC,OAAoB;QAC9C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACtC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;QAChC,CAAC;IACF,CAAC;IAEO,UAAU,CAAC,IAAY,EAAE,OAAoB;QACpD,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9B,IAAI,IAAI,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;YAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;YAC7B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,gEAAgE;gBAChE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;gBACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,CAAC;YACD,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,gBAAgB;YAAE,OAAO;QAC3E,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,OAAoB;QAC9D,yEAAyE;QACzE,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACnD,IAAI,KAA8B,CAAC;QACnC,IAAI,SAA6B,CAAC;QAClC,IAAI,CAAC;YACJ,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QACjG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACnD,oEAAoE;QACpE,wEAAwE;QACxE,wDAAwD;QACxD,IAAI,KAAK,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvC,OAAO;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,OAAO,CAAC,aAAa;YAAE,OAAO;QAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,IAAI;YACJ,QAAQ,EAAE,oBAAoB,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC;YACjD,SAAS,EAAE,KAAK,EAAE,UAAU,IAAI,CAAC;YACjC,UAAU,EAAE,KAAK,EAAE,UAAU,IAAI,IAAI,CAAC,WAAW;YACjD,aAAa,EAAE,KAAK,EAAE,SAAS,IAAI,KAAK;YACxC,iBAAiB,EAAE,KAAK,EAAE,aAAa,IAAI,KAAK;YAChD,SAAS;SACT,CAAC,CAAC;IACJ,CAAC;IAEO,SAAS;QAChB,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,EAAE,CAAC;QACpC,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,OAAoB,EAAE,GAAuB;QAC5E,IAAI,OAAsD,CAAC;QAC3D,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;gBACjE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;gBACtD,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM;gBACjC,OAAO,EAAE,IAAI,CAAC,cAAc;gBAC5B,GAAG;aACH,CAAC,CAAC;YACH,OAAO,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC;QACrB,CAAC;QACD,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAEO,UAAU,CAAC,IAAY,EAAE,OAAoB,EAAE,IAAY;QAClE,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;YAAE,OAAO;QACvF,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5C,IAAI,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,OAAO;QACR,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC;QACtD,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC;QACxC,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC;QACpG,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;QACxB,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,QAAQ,CACrB,IAAY,EACZ,OAAoB,EACpB,OAAsD;QAEtD,IAAI,OAAO,CAAC,SAAS;YAAE,OAAO;QAC9B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,qEAAqE;QACrE,4DAA4D;QAC5D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,OAAO,EAAE,CAAC;gBACV,OAAO;YACR,CAAC;YACD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,uEAAuE;QACvE,0EAA0E;QAC1E,4DAA4D;QAC5D,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;IAEO,eAAe,CACtB,OAAoB,EACpB,OAAsD;QAEtD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO;gBACN,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,uBAAuB,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,8BAA8B;aAC3F,CAAC;QACH,CAAC;QACD,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,sBAAsB,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;QACjF,CAAC;QACD,IAAI,OAAO,CAAC,aAAa;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACvD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;gBAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpG,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACnD,CAAC;QACD,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3E,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAC3D,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,qCAAqC,EAAE,CAAC;QACzG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,4BAA4B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;IACpF,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,IAAY;QAC1C,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC/C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,QAAQ,GAAG,EAAE,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,IAAI,SAA6B,CAAC;QAClC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;YACvG,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;YAC7B,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YAC9B,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC;YAChC,iBAAiB,GAAG,KAAK,CAAC,aAAa,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC;YACJ,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC,CAAC;QACvG,CAAC;QAAC,MAAM,CAAC;YACR,sEAAsE;YACtE,uEAAuE;YACvE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACvB,CAAC;IACF,CAAC;CACD;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAY;IACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACpC,SAAS,CAAC,MAAM,CAAC,CAAC;AACnB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC;IAChG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,EAAU;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACxD,IAAI,YAAY,GAAG,EAAE;QAAE,OAAO,GAAG,YAAY,GAAG,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;IAC3E,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AAChF,CAAC","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 { type FileHandle, 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/**\n\t * Process exit code; null when reaped by a signal, undefined while running\n\t * or when the run produced none (spawn failure, kill, timeout).\n\t */\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\t/** Delivery was claimed: onNotify returned without throwing, or a waiter took it over. */\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 type BgStallNotification = BgTaskNotification;\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 `length` bytes at `start`, first nudging the start past any UTF-8\n * continuation bytes so the text begins on a codepoint boundary. The same probe\n * reveals whether the slice starts mid-line. A `start` of 0 is used as-is —\n * there is nothing before it to align against.\n */\nasync function readAligned(\n\tfile: FileHandle,\n\tsize: number,\n\tstart: number,\n\tlength: number,\n): Promise<{ text: string; sliceBytes: number; startsMidLine: boolean; position: number }> {\n\tlet position = start;\n\tlet remaining = length;\n\tlet startsMidLine = false;\n\tif (position > 0 && remaining > 0) {\n\t\t// Probe from one byte before the slice: probe[0] tells whether the slice\n\t\t// starts at a line boundary, the rest lets us skip UTF-8 continuation\n\t\t// bytes (0b10xxxxxx) so we never split a codepoint.\n\t\tconst probeStart = position - 1;\n\t\tconst probe = Buffer.alloc(Math.min(5, size - probeStart));\n\t\tawait file.read(probe, 0, probe.length, probeStart);\n\t\tlet skip = 0;\n\t\twhile (1 + skip < probe.length && ((probe[1 + skip] ?? 0) & 0b1100_0000) === 0b1000_0000) {\n\t\t\tskip++;\n\t\t}\n\t\tposition += skip;\n\t\tremaining -= skip;\n\t\tstartsMidLine = probe[skip] !== 0x0a;\n\t}\n\tconst buffer = Buffer.alloc(Math.max(0, remaining));\n\tconst bytesRead = buffer.length > 0 ? (await file.read(buffer, 0, buffer.length, position)).bytesRead : 0;\n\treturn { text: buffer.subarray(0, bytesRead).toString(\"utf8\"), sliceBytes: bytesRead, startsMidLine, position };\n}\n\n/**\n * Read a bounded slice from the head or tail of a file via positioned reads —\n * never the whole file.\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\tconst length = Math.min(size, Math.max(0, Math.floor(options.maxBytes)));\n\t\tconst start = options.mode === \"tail\" ? size - length : 0;\n\t\tconst slice = await readAligned(file, size, start, length);\n\t\treturn {\n\t\t\ttext: slice.text,\n\t\t\tsliceBytes: slice.sliceBytes,\n\t\t\ttotalBytes: size,\n\t\t\ttruncated: size > slice.sliceBytes,\n\t\t\tstartsMidLine: slice.startsMidLine,\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\tconst length = Math.min(size - floor, Math.max(0, Math.floor(maxBytes)));\n\t\tconst start = size - length;\n\t\t// Decided before alignment: nudging the start past continuation bytes is\n\t\t// not the same as dropping output the caller asked for.\n\t\tconst truncated = start > floor;\n\t\tconst slice = await readAligned(file, size, start, length);\n\t\treturn {\n\t\t\ttext: slice.text,\n\t\t\tsliceBytes: slice.sliceBytes,\n\t\t\ttotalBytes: size,\n\t\t\ttruncated,\n\t\t\tstartsMidLine: slice.startsMidLine,\n\t\t\tfromByte: slice.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\t * An aborted wait (the turn was interrupted) hands the claim back: the\n\t * caller's result is discarded, so it must not also swallow the followUp.\n\t */\n\tasync waitForResult(id: string, timeoutMs: number, signal?: AbortSignal): 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\tsignal?.addEventListener(\"abort\", () => resolve(true), { once: true });\n\t\t\t});\n\t\t\t// Throwing from inside `try` matters: `finally` drops the waiter count\n\t\t\t// first, so finalize sees waiters === 0 and still sends the followUp.\n\t\t\tif (signal?.aborted) throw new Error(\"aborted\");\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 slice: OutputSlice | undefined;\n\t\tlet tailError: string | undefined;\n\t\ttry {\n\t\t\tslice = await readOutputSlice(task.outputPath, { mode: \"tail\", maxBytes: this.stallTailBytes });\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. A tail that\n\t\t// could not be read is reported, not dismissed as slow.\n\t\tif (slice && !looksLikePrompt(slice.text)) {\n\t\t\truntime.stallLastGrowthAt = this.now();\n\t\t\treturn;\n\t\t}\n\t\tif (!task.stalled) {\n\t\t\ttask.stalled = true;\n\t\t\tthis.onChange();\n\t\t}\n\t\tif (runtime.stallNotified) return;\n\t\truntime.stallNotified = true;\n\t\tthis.onStall?.({\n\t\t\ttask,\n\t\t\ttailText: sanitizeBinaryOutput(slice?.text ?? \"\"),\n\t\t\ttailBytes: slice?.sliceBytes ?? 0,\n\t\t\ttotalBytes: slice?.totalBytes ?? task.outputBytes,\n\t\t\ttailTruncated: slice?.truncated ?? false,\n\t\t\ttailStartsMidLine: slice?.startsMidLine ?? false,\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 +1 @@
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"}
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,CAgCX;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAuED,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"}
@@ -94,7 +94,9 @@ export function commandLabel(command, width) {
94
94
  export function renderBgCall(args, theme, context, getTaskLive) {
95
95
  switch (args.action) {
96
96
  case "create":
97
- return renderCreateCall(args, theme, context);
97
+ return typeof args.command === "string"
98
+ ? renderCreateCall(args, theme, context)
99
+ : new Text(theme.fg("toolTitle", theme.bold("bg create")), 0, 0);
98
100
  case "read": {
99
101
  const mode = args.mode ?? "tail";
100
102
  const size = args.bytes !== undefined ? ` ${formatSize(args.bytes)}` : "";
@@ -106,6 +108,12 @@ export function renderBgCall(args, theme, context, getTaskLive) {
106
108
  return new Text(`${theme.fg("toolTitle", theme.bold("bg kill "))}${theme.fg("accent", args.taskId ?? "")}`, 0, 0);
107
109
  case "list":
108
110
  return new Text(theme.fg("toolTitle", theme.bold("bg list")), 0, 0);
111
+ default: {
112
+ // Tool arguments arrive incrementally while the model is still emitting
113
+ // JSON. Keep the call row renderable until `action` becomes valid.
114
+ const action = typeof args.action === "string" ? ` ${theme.fg("dim", args.action)}` : "";
115
+ return new Text(`${theme.fg("toolTitle", theme.bold("bg"))}${action}`, 0, 0);
116
+ }
109
117
  }
110
118
  }
111
119
  /** Mirror the execution-side clamp so the shown window matches the real one. */
@@ -120,9 +128,14 @@ export function clampWaitMs(waitMs) {
120
128
  function renderWaitCall(args, theme, context, getTaskLive) {
121
129
  const taskId = typeof args.taskId === "string" ? args.taskId.trim() : "";
122
130
  const live = getTaskLive !== undefined && taskId ? getTaskLive(taskId) : undefined;
123
- // The live display needs shell-owned state; without it (standalone renders)
124
- // fall back to the static line.
125
- const pending = context.isPartial === true && context.state !== undefined && taskId.length > 0;
131
+ // isPartial alone means "not settled", which also covers argument streaming
132
+ // and replayed history rows that never settle; executionStarted narrows it to
133
+ // a call that is actually running. The live display also needs shell-owned
134
+ // state; without it (standalone renders) fall back to the static line.
135
+ const pending = context.executionStarted === true &&
136
+ context.isPartial === true &&
137
+ context.state !== undefined &&
138
+ taskId.length > 0;
126
139
  scheduleWaitRefresh(context, pending);
127
140
  if (!pending) {
128
141
  const ms = args.waitMs !== undefined ? ` ${formatDuration(clampWaitMs(args.waitMs))}` : "";