@astralyn/pi 0.84.14 → 0.84.16

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 (185) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cli.js +2435 -18
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +12 -6
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/types.d.ts +4 -1
  7. package/dist/core/extensions/types.d.ts.map +1 -1
  8. package/dist/core/extensions/types.js.map +1 -1
  9. package/dist/core/tools/bash.d.ts +1 -0
  10. package/dist/core/tools/bash.d.ts.map +1 -1
  11. package/dist/core/tools/bash.js +4 -1
  12. package/dist/core/tools/bash.js.map +1 -1
  13. package/dist/core/tools/find.d.ts +2 -0
  14. package/dist/core/tools/find.d.ts.map +1 -1
  15. package/dist/core/tools/find.js +18 -15
  16. package/dist/core/tools/find.js.map +1 -1
  17. package/dist/extensions/background/actions.d.ts.map +1 -1
  18. package/dist/extensions/background/actions.js +3 -2
  19. package/dist/extensions/background/actions.js.map +1 -1
  20. package/dist/extensions/background/index.d.ts +5 -4
  21. package/dist/extensions/background/index.d.ts.map +1 -1
  22. package/dist/extensions/background/index.js +9 -5
  23. package/dist/extensions/background/index.js.map +1 -1
  24. package/dist/extensions/background/manager.d.ts.map +1 -1
  25. package/dist/extensions/background/manager.js +8 -9
  26. package/dist/extensions/background/manager.js.map +1 -1
  27. package/dist/extensions/background/registry.d.ts +4 -1
  28. package/dist/extensions/background/registry.d.ts.map +1 -1
  29. package/dist/extensions/background/registry.js +30 -17
  30. package/dist/extensions/background/registry.js.map +1 -1
  31. package/dist/extensions/background/task-view.d.ts +6 -0
  32. package/dist/extensions/background/task-view.d.ts.map +1 -1
  33. package/dist/extensions/background/task-view.js +11 -6
  34. package/dist/extensions/background/task-view.js.map +1 -1
  35. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  36. package/dist/extensions/deepwiki/client.js +3 -0
  37. package/dist/extensions/deepwiki/client.js.map +1 -1
  38. package/dist/extensions/deepwiki/execute.d.ts +2 -1
  39. package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
  40. package/dist/extensions/deepwiki/execute.js +17 -2
  41. package/dist/extensions/deepwiki/execute.js.map +1 -1
  42. package/dist/extensions/deepwiki/http.d.ts +1 -0
  43. package/dist/extensions/deepwiki/http.d.ts.map +1 -1
  44. package/dist/extensions/deepwiki/http.js +16 -2
  45. package/dist/extensions/deepwiki/http.js.map +1 -1
  46. package/dist/extensions/deepwiki/schema.d.ts +1 -1
  47. package/dist/extensions/deepwiki/schema.d.ts.map +1 -1
  48. package/dist/extensions/deepwiki/schema.js +8 -2
  49. package/dist/extensions/deepwiki/schema.js.map +1 -1
  50. package/dist/extensions/question/dialog.d.ts.map +1 -1
  51. package/dist/extensions/question/dialog.js +80 -9
  52. package/dist/extensions/question/dialog.js.map +1 -1
  53. package/dist/extensions/question/index.d.ts.map +1 -1
  54. package/dist/extensions/question/index.js +8 -0
  55. package/dist/extensions/question/index.js.map +1 -1
  56. package/dist/extensions/question/schema.d.ts +2 -2
  57. package/dist/extensions/question/schema.d.ts.map +1 -1
  58. package/dist/extensions/question/schema.js +41 -11
  59. package/dist/extensions/question/schema.js.map +1 -1
  60. package/dist/extensions/question/types.d.ts +1 -1
  61. package/dist/extensions/question/types.d.ts.map +1 -1
  62. package/dist/extensions/question/types.js.map +1 -1
  63. package/dist/extensions/router/probe.d.ts.map +1 -1
  64. package/dist/extensions/router/probe.js +10 -29
  65. package/dist/extensions/router/probe.js.map +1 -1
  66. package/dist/extensions/router/register.d.ts.map +1 -1
  67. package/dist/extensions/router/register.js +16 -6
  68. package/dist/extensions/router/register.js.map +1 -1
  69. package/dist/extensions/router/store.d.ts.map +1 -1
  70. package/dist/extensions/router/store.js +108 -51
  71. package/dist/extensions/router/store.js.map +1 -1
  72. package/dist/extensions/router/ui.d.ts +6 -0
  73. package/dist/extensions/router/ui.d.ts.map +1 -1
  74. package/dist/extensions/router/ui.js +14 -6
  75. package/dist/extensions/router/ui.js.map +1 -1
  76. package/dist/extensions/statusline/index.d.ts.map +1 -1
  77. package/dist/extensions/statusline/index.js +40 -43
  78. package/dist/extensions/statusline/index.js.map +1 -1
  79. package/dist/extensions/subagent/activity.d.ts +7 -1
  80. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  81. package/dist/extensions/subagent/activity.js +69 -6
  82. package/dist/extensions/subagent/activity.js.map +1 -1
  83. package/dist/extensions/subagent/agents-command.js +1 -1
  84. package/dist/extensions/subagent/agents-command.js.map +1 -1
  85. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  86. package/dist/extensions/subagent/agents.js +0 -3
  87. package/dist/extensions/subagent/agents.js.map +1 -1
  88. package/dist/extensions/subagent/budget.d.ts.map +1 -1
  89. package/dist/extensions/subagent/budget.js +29 -2
  90. package/dist/extensions/subagent/budget.js.map +1 -1
  91. package/dist/extensions/subagent/cancellation.d.ts +4 -4
  92. package/dist/extensions/subagent/cancellation.d.ts.map +1 -1
  93. package/dist/extensions/subagent/cancellation.js +31 -13
  94. package/dist/extensions/subagent/cancellation.js.map +1 -1
  95. package/dist/extensions/subagent/constants.d.ts +1 -0
  96. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  97. package/dist/extensions/subagent/constants.js +1 -0
  98. package/dist/extensions/subagent/constants.js.map +1 -1
  99. package/dist/extensions/subagent/index.d.ts.map +1 -1
  100. package/dist/extensions/subagent/index.js +1 -0
  101. package/dist/extensions/subagent/index.js.map +1 -1
  102. package/dist/extensions/subagent/render.d.ts +8 -1
  103. package/dist/extensions/subagent/render.d.ts.map +1 -1
  104. package/dist/extensions/subagent/render.js +155 -178
  105. package/dist/extensions/subagent/render.js.map +1 -1
  106. package/dist/extensions/subagent/runner.d.ts +1 -0
  107. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  108. package/dist/extensions/subagent/runner.js +53 -44
  109. package/dist/extensions/subagent/runner.js.map +1 -1
  110. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  111. package/dist/extensions/subagent/sdk-runner.js +0 -3
  112. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  113. package/dist/extensions/subagent/settings.d.ts +1 -1
  114. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  115. package/dist/extensions/subagent/settings.js +74 -25
  116. package/dist/extensions/subagent/settings.js.map +1 -1
  117. package/dist/extensions/subagent/state.d.ts +1 -0
  118. package/dist/extensions/subagent/state.d.ts.map +1 -1
  119. package/dist/extensions/subagent/state.js +46 -16
  120. package/dist/extensions/subagent/state.js.map +1 -1
  121. package/dist/extensions/subagent/types.d.ts +0 -7
  122. package/dist/extensions/subagent/types.d.ts.map +1 -1
  123. package/dist/extensions/subagent/types.js.map +1 -1
  124. package/dist/extensions/subagent/ui/agents-dialog.js +1 -1
  125. package/dist/extensions/subagent/ui/agents-dialog.js.map +1 -1
  126. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  127. package/dist/extensions/subagent/ui/model-list.js +3 -4
  128. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  129. package/dist/extensions/subagent/ui/model-picker.d.ts.map +1 -1
  130. package/dist/extensions/subagent/ui/model-picker.js +2 -1
  131. package/dist/extensions/subagent/ui/model-picker.js.map +1 -1
  132. package/dist/extensions/web-search/format.d.ts.map +1 -1
  133. package/dist/extensions/web-search/format.js +36 -15
  134. package/dist/extensions/web-search/format.js.map +1 -1
  135. package/dist/extensions/web-search/index.d.ts.map +1 -1
  136. package/dist/extensions/web-search/index.js +11 -8
  137. package/dist/extensions/web-search/index.js.map +1 -1
  138. package/dist/extensions/web-search/providers/deepseek.d.ts.map +1 -1
  139. package/dist/extensions/web-search/providers/deepseek.js +36 -5
  140. package/dist/extensions/web-search/providers/deepseek.js.map +1 -1
  141. package/dist/extensions/web-search/providers/http.d.ts +2 -7
  142. package/dist/extensions/web-search/providers/http.d.ts.map +1 -1
  143. package/dist/extensions/web-search/providers/http.js +12 -57
  144. package/dist/extensions/web-search/providers/http.js.map +1 -1
  145. package/dist/extensions/web-search/providers/minimax.d.ts.map +1 -1
  146. package/dist/extensions/web-search/providers/minimax.js +18 -6
  147. package/dist/extensions/web-search/providers/minimax.js.map +1 -1
  148. package/dist/extensions/web-search/render.d.ts.map +1 -1
  149. package/dist/extensions/web-search/render.js +16 -5
  150. package/dist/extensions/web-search/render.js.map +1 -1
  151. package/dist/extensions/web-search/results.d.ts +10 -3
  152. package/dist/extensions/web-search/results.d.ts.map +1 -1
  153. package/dist/extensions/web-search/results.js +25 -7
  154. package/dist/extensions/web-search/results.js.map +1 -1
  155. package/dist/image-resize-worker.js +2 -0
  156. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  157. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/tool-execution.js +7 -0
  159. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  160. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/tool-group.js +10 -0
  162. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  163. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/dist/modes/interactive/interactive-mode.js +3 -40
  165. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/dist/modes/interactive/theme/theme.d.ts +0 -4
  167. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  168. package/dist/modes/interactive/theme/theme.js +16 -23
  169. package/dist/modes/interactive/theme/theme.js.map +1 -1
  170. package/dist/rpc-entry.js +2435 -10
  171. package/dist/utils/http-response.d.ts +10 -0
  172. package/dist/utils/http-response.d.ts.map +1 -0
  173. package/dist/utils/http-response.js +68 -0
  174. package/dist/utils/http-response.js.map +1 -0
  175. package/docs/bundled/extensions/background.md +4 -3
  176. package/docs/bundled/extensions/deepwiki.md +7 -5
  177. package/docs/bundled/extensions/question.md +6 -4
  178. package/docs/bundled/extensions/router.md +1 -1
  179. package/docs/bundled/extensions/statusline.md +1 -0
  180. package/docs/bundled/extensions/subagent.md +29 -39
  181. package/docs/bundled/extensions/web-search.md +3 -3
  182. package/docs/development.md +8 -3
  183. package/docs/extensions.md +2 -2
  184. package/npm-shrinkwrap.json +110 -109
  185. package/package.json +4 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAEX,YAAY,EAGZ,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,KAAK,cAAc,EAA6B,MAAM,0BAA0B,CAAC;AAqB1F;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAK3G;AAcD,MAAM,WAAW,4BAA4B;IAC5C,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yGAAyG;IACzG,KAAK,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;CACtF;AAED,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,SAAS,CAAC,EAAE,4BAA4B,GAAG,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAgH9G;6BAhHyF,YAAY,KAAK,IAAI;AAoI/G,wBAA2C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAEX,YAAY,EAGZ,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,KAAK,cAAc,EAA6B,MAAM,0BAA0B,CAAC;AAqB1F;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAK3G;AAcD,MAAM,WAAW,4BAA4B;IAC5C,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yGAAyG;IACzG,KAAK,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;CACtF;AAED,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,SAAS,CAAC,EAAE,4BAA4B,GAAG,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAkH9G;6BAlHyF,YAAY,KAAK,IAAI;AAsI/G,wBAA2C"}
@@ -3,10 +3,11 @@
3
3
  *
4
4
  * Tools: a single `bg` tool with five actions. create starts a task and
5
5
  * returns immediately; the result arrives as a <background-task> notification
6
- * (queued behind the current run while streaming, waking the agent when
7
- * idle). read returns a bounded slice of a task's output file; wait blocks
8
- * (bounded) for a task's completion and delivers it inline — the followUp
9
- * notification is suppressed so the completion is delivered exactly once;
6
+ * (steered into the run at the next turn boundary while streaming, waking the
7
+ * agent when idle). read returns a bounded slice of a task's output file;
8
+ * wait blocks (bounded) for a task's completion and delivers it inline — the
9
+ * completion notification is suppressed so the completion is delivered exactly
10
+ * once;
10
11
  * kill stops a single task; list enumerates known tasks. /bg opens the
11
12
  * interactive task manager. Running counts surface in the footer via
12
13
  * ctx.ui.setStatus.
@@ -88,7 +89,10 @@ export function createBackgroundExtension(overrides) {
88
89
  content: buildNotificationContent(notification),
89
90
  display: true,
90
91
  details: toNotificationDetails(notification),
91
- }, { deliverAs: "followUp", triggerTurn: true }),
92
+ },
93
+ // steer, not followUp: a task that finishes mid-run must reach the
94
+ // model at the next turn boundary, not after the whole run ends.
95
+ { deliverAs: "steer", triggerTurn: true }),
92
96
  onChange: updateStatus,
93
97
  });
94
98
  updateStatus();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/background/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * background — run bash commands in the background.\n *\n * Tools: a single `bg` tool with five actions. create starts a task and\n * returns immediately; the result arrives as a <background-task> notification\n * (queued behind the current run while streaming, waking the agent when\n * idle). read returns a bounded slice of a task's output file; wait blocks\n * (bounded) for a task's completion and delivers it inline — the followUp\n * notification is suppressed so the completion is delivered exactly once;\n * kill stops a single task; list enumerates known tasks. /bg opens the\n * interactive task manager. Running counts surface in the footer via\n * ctx.ui.setStatus.\n *\n * This file is wiring only: the actions live in actions.ts, the notification\n * in notify.ts, the transcript rows in render.ts, and the menu in manager.ts.\n */\n\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport { SettingsManager } from \"../../core/settings-manager.ts\";\nimport { type BashOperations, createLocalBashOperations } from \"../../core/tools/bash.ts\";\nimport { describeTaskLine, runCreate, runKill, runList, runRead, runWait } from \"./actions.ts\";\nimport { BG_NOTIFICATION_TYPE } from \"./constants.ts\";\nimport { BackgroundTasksMenu } from \"./manager.ts\";\nimport { buildNotificationContent, toNotificationDetails } from \"./notify.ts\";\nimport { readOutputSlice } from \"./output-file.ts\";\nimport { BackgroundTaskRegistry } from \"./registry.ts\";\nimport {\n\ttype BgRenderState,\n\trenderBackgroundNotification,\n\trenderBgCall,\n\trenderBgResult,\n\ttype WaitLiveProbe,\n} from \"./render.ts\";\nimport { BG_PROMPT_GUIDELINES, BG_PROMPT_SNIPPET, BG_TOOL_DESCRIPTION, bgSchema } from \"./schema.ts\";\nimport { formatStatusline } from \"./task-view.ts\";\nimport type { BgDetails, BgNotificationDetails } from \"./types.ts\";\n\n/** Tasks shown by the non-TUI `/bg` fallback summary. */\nconst BG_COMMAND_SUMMARY_SHOWN = 10;\n\n/**\n * Wrap operations so every executed command carries the prefix, mirroring the\n * built-in bash tool. The prefix never reaches task labels or notifications:\n * `BgTask.command` stays the user's original input.\n */\nexport function prependCommandPrefix(operations: BashOperations, prefix: string | undefined): BashOperations {\n\tif (!prefix) return operations;\n\treturn {\n\t\texec: (command, cwd, options) => operations.exec(`${prefix}\\n${command}`, cwd, options),\n\t};\n}\n\n/** Hosts without getShellSettings (older SDK hosts): read what the session was configured with. */\nfunction readDiskShellSettings(ctx: ExtensionContext): { shellPath?: string; commandPrefix?: string } {\n\tconst settings = SettingsManager.create(ctx.cwd, getAgentDir(), { projectTrusted: ctx.isProjectTrusted() });\n\treturn { shellPath: settings.getShellPath(), commandPrefix: settings.getShellCommandPrefix() };\n}\n\n/** Local shell operations honoring the session's shell settings (shellPath, commandPrefix). */\nfunction createSessionBashOperations(ctx: ExtensionContext): BashOperations {\n\tconst shell = ctx.getShellSettings?.() ?? readDiskShellSettings(ctx);\n\treturn prependCommandPrefix(createLocalBashOperations({ shellPath: shell.shellPath }), shell.commandPrefix);\n}\n\nexport interface BackgroundExtensionOverrides {\n\toperations?: BashOperations;\n\toutputDir?: string;\n\tmaxOutputBytes?: number;\n\t/** Stall watchdog tuning, or false to disable it; defaults follow Claude Code's CC-1175 (5s/45s/1KB). */\n\tstall?: { pollIntervalMs?: number; thresholdMs?: number; tailBytes?: number } | false;\n}\n\n/** Factory with injectable seams for tests; the default export uses production wiring. */\nexport function createBackgroundExtension(overrides?: BackgroundExtensionOverrides): (pi: ExtensionAPI) => void {\n\treturn (pi: ExtensionAPI) => {\n\t\tlet registry: BackgroundTaskRegistry | undefined;\n\t\tlet sessionCtx: ExtensionContext | undefined;\n\n\t\tconst requireRegistry = (): BackgroundTaskRegistry => {\n\t\t\tif (!registry) throw new Error(\"Background tasks are not available yet (no active session).\");\n\t\t\treturn registry;\n\t\t};\n\n\t\t/** Read-only live peek for the pending wait renderer; never throws. */\n\t\tconst waitLiveProbe: WaitLiveProbe = (taskId) => {\n\t\t\tif (!registry) return undefined;\n\t\t\tconst resolved = registry.resolveTask(taskId);\n\t\t\treturn resolved.ok ? { status: resolved.task.status, outputBytes: resolved.task.outputBytes } : undefined;\n\t\t};\n\n\t\tconst updateStatus = () => {\n\t\t\tif (!registry || registry.isShuttingDown) return;\n\t\t\tsessionCtx?.ui.setStatus(\"background\", formatStatusline(registry.counts()));\n\t\t};\n\n\t\tpi.on(\"session_start\", (_event, ctx) => {\n\t\t\tsessionCtx = ctx;\n\t\t\t// Defensive: a leftover registry (session_shutdown not seen) must not leak processes.\n\t\t\tif (registry) void registry.shutdown();\n\t\t\tregistry = new BackgroundTaskRegistry({\n\t\t\t\toperations: overrides?.operations ?? createSessionBashOperations(ctx),\n\t\t\t\toutputDir: overrides?.outputDir,\n\t\t\t\tmaxOutputBytes: overrides?.maxOutputBytes,\n\t\t\t\tstall: overrides?.stall,\n\t\t\t\t// Completion and stall reach the model the same way; that choice lives here only.\n\t\t\t\tonNotify: (notification) =>\n\t\t\t\t\tpi.sendMessage(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcustomType: BG_NOTIFICATION_TYPE,\n\t\t\t\t\t\t\tcontent: buildNotificationContent(notification),\n\t\t\t\t\t\t\tdisplay: true,\n\t\t\t\t\t\t\tdetails: toNotificationDetails(notification),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ deliverAs: \"followUp\", triggerTurn: true },\n\t\t\t\t\t),\n\t\t\t\tonChange: updateStatus,\n\t\t\t});\n\t\t\tupdateStatus();\n\t\t});\n\n\t\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\t\tctx.ui.setStatus(\"background\", undefined);\n\t\t\tsessionCtx = undefined;\n\t\t\tconst active = registry;\n\t\t\tregistry = undefined;\n\t\t\tawait active?.shutdown();\n\t\t});\n\n\t\tpi.registerTool<typeof bgSchema, BgDetails, BgRenderState>({\n\t\t\tname: \"bg\",\n\t\t\tlabel: \"bg\",\n\t\t\tdescription: BG_TOOL_DESCRIPTION,\n\t\t\tpromptSnippet: BG_PROMPT_SNIPPET,\n\t\t\tpromptGuidelines: BG_PROMPT_GUIDELINES,\n\t\t\tparameters: bgSchema,\n\t\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx): Promise<AgentToolResult<BgDetails>> {\n\t\t\t\tconst active = requireRegistry();\n\t\t\t\tswitch (params.action) {\n\t\t\t\t\tcase \"create\":\n\t\t\t\t\t\treturn runCreate(active, params, ctx);\n\t\t\t\t\tcase \"read\":\n\t\t\t\t\t\treturn runRead(active, params);\n\t\t\t\t\tcase \"wait\":\n\t\t\t\t\t\treturn runWait(active, params, signal);\n\t\t\t\t\tcase \"kill\":\n\t\t\t\t\t\treturn runKill(active, params);\n\t\t\t\t\tcase \"list\":\n\t\t\t\t\t\treturn runList(active);\n\t\t\t\t}\n\t\t\t},\n\t\t\trenderCall(args, theme, context) {\n\t\t\t\treturn renderBgCall(args, theme, context, waitLiveProbe);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderBgResult(result, options, theme, context);\n\t\t\t},\n\t\t});\n\n\t\tpi.registerMessageRenderer<BgNotificationDetails>(BG_NOTIFICATION_TYPE, renderBackgroundNotification);\n\n\t\tpi.registerCommand(\"bg\", {\n\t\t\tdescription: \"View and manage background tasks\",\n\t\t\thandler: async (_args, ctx: ExtensionCommandContext) => {\n\t\t\t\tconst active = registry;\n\t\t\t\tif (!active || active.counts().total === 0) {\n\t\t\t\t\tctx.ui.notify(\"No background tasks.\", \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\t// RPC/print: ctx.ui.custom is unavailable — show a bounded summary.\n\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\tconst lines = active\n\t\t\t\t\t\t.listTasks()\n\t\t\t\t\t\t.slice(0, BG_COMMAND_SUMMARY_SHOWN)\n\t\t\t\t\t\t.map((task) => describeTaskLine(task, now));\n\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t`Background tasks:\\n${lines.join(\"\\n\")}\\nUse the bg tool (action kill) to stop a task.`,\n\t\t\t\t\t\t\"info\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait showBackgroundManager(ctx, active);\n\t\t\t},\n\t\t});\n\t};\n}\n\nasync function showBackgroundManager(ctx: ExtensionCommandContext, registry: BackgroundTaskRegistry): Promise<void> {\n\t// No overlay options: the component mounts inline in the editor slot, like /model.\n\tawait ctx.ui.custom(\n\t\t(tui, theme, keybindings, done) =>\n\t\t\tnew BackgroundTasksMenu({\n\t\t\t\ttui,\n\t\t\t\ttheme,\n\t\t\t\tkeybindings,\n\t\t\t\thost: {\n\t\t\t\t\tlistTasks: () => registry.listTasks(),\n\t\t\t\t\tkillTask: (id) => registry.killTask(id),\n\t\t\t\t\treadSlice: (filePath, options) => readOutputSlice(filePath, options),\n\t\t\t\t},\n\t\t\t\tonClose: () => done(undefined),\n\t\t\t}),\n\t);\n}\n\nexport default createBackgroundExtension();\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/background/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * background — run bash commands in the background.\n *\n * Tools: a single `bg` tool with five actions. create starts a task and\n * returns immediately; the result arrives as a <background-task> notification\n * (steered into the run at the next turn boundary while streaming, waking the\n * agent when idle). read returns a bounded slice of a task's output file;\n * wait blocks (bounded) for a task's completion and delivers it inline — the\n * completion notification is suppressed so the completion is delivered exactly\n * once;\n * kill stops a single task; list enumerates known tasks. /bg opens the\n * interactive task manager. Running counts surface in the footer via\n * ctx.ui.setStatus.\n *\n * This file is wiring only: the actions live in actions.ts, the notification\n * in notify.ts, the transcript rows in render.ts, and the menu in manager.ts.\n */\n\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport { SettingsManager } from \"../../core/settings-manager.ts\";\nimport { type BashOperations, createLocalBashOperations } from \"../../core/tools/bash.ts\";\nimport { describeTaskLine, runCreate, runKill, runList, runRead, runWait } from \"./actions.ts\";\nimport { BG_NOTIFICATION_TYPE } from \"./constants.ts\";\nimport { BackgroundTasksMenu } from \"./manager.ts\";\nimport { buildNotificationContent, toNotificationDetails } from \"./notify.ts\";\nimport { readOutputSlice } from \"./output-file.ts\";\nimport { BackgroundTaskRegistry } from \"./registry.ts\";\nimport {\n\ttype BgRenderState,\n\trenderBackgroundNotification,\n\trenderBgCall,\n\trenderBgResult,\n\ttype WaitLiveProbe,\n} from \"./render.ts\";\nimport { BG_PROMPT_GUIDELINES, BG_PROMPT_SNIPPET, BG_TOOL_DESCRIPTION, bgSchema } from \"./schema.ts\";\nimport { formatStatusline } from \"./task-view.ts\";\nimport type { BgDetails, BgNotificationDetails } from \"./types.ts\";\n\n/** Tasks shown by the non-TUI `/bg` fallback summary. */\nconst BG_COMMAND_SUMMARY_SHOWN = 10;\n\n/**\n * Wrap operations so every executed command carries the prefix, mirroring the\n * built-in bash tool. The prefix never reaches task labels or notifications:\n * `BgTask.command` stays the user's original input.\n */\nexport function prependCommandPrefix(operations: BashOperations, prefix: string | undefined): BashOperations {\n\tif (!prefix) return operations;\n\treturn {\n\t\texec: (command, cwd, options) => operations.exec(`${prefix}\\n${command}`, cwd, options),\n\t};\n}\n\n/** Hosts without getShellSettings (older SDK hosts): read what the session was configured with. */\nfunction readDiskShellSettings(ctx: ExtensionContext): { shellPath?: string; commandPrefix?: string } {\n\tconst settings = SettingsManager.create(ctx.cwd, getAgentDir(), { projectTrusted: ctx.isProjectTrusted() });\n\treturn { shellPath: settings.getShellPath(), commandPrefix: settings.getShellCommandPrefix() };\n}\n\n/** Local shell operations honoring the session's shell settings (shellPath, commandPrefix). */\nfunction createSessionBashOperations(ctx: ExtensionContext): BashOperations {\n\tconst shell = ctx.getShellSettings?.() ?? readDiskShellSettings(ctx);\n\treturn prependCommandPrefix(createLocalBashOperations({ shellPath: shell.shellPath }), shell.commandPrefix);\n}\n\nexport interface BackgroundExtensionOverrides {\n\toperations?: BashOperations;\n\toutputDir?: string;\n\tmaxOutputBytes?: number;\n\t/** Stall watchdog tuning, or false to disable it; defaults follow Claude Code's CC-1175 (5s/45s/1KB). */\n\tstall?: { pollIntervalMs?: number; thresholdMs?: number; tailBytes?: number } | false;\n}\n\n/** Factory with injectable seams for tests; the default export uses production wiring. */\nexport function createBackgroundExtension(overrides?: BackgroundExtensionOverrides): (pi: ExtensionAPI) => void {\n\treturn (pi: ExtensionAPI) => {\n\t\tlet registry: BackgroundTaskRegistry | undefined;\n\t\tlet sessionCtx: ExtensionContext | undefined;\n\n\t\tconst requireRegistry = (): BackgroundTaskRegistry => {\n\t\t\tif (!registry) throw new Error(\"Background tasks are not available yet (no active session).\");\n\t\t\treturn registry;\n\t\t};\n\n\t\t/** Read-only live peek for the pending wait renderer; never throws. */\n\t\tconst waitLiveProbe: WaitLiveProbe = (taskId) => {\n\t\t\tif (!registry) return undefined;\n\t\t\tconst resolved = registry.resolveTask(taskId);\n\t\t\treturn resolved.ok ? { status: resolved.task.status, outputBytes: resolved.task.outputBytes } : undefined;\n\t\t};\n\n\t\tconst updateStatus = () => {\n\t\t\tif (!registry || registry.isShuttingDown) return;\n\t\t\tsessionCtx?.ui.setStatus(\"background\", formatStatusline(registry.counts()));\n\t\t};\n\n\t\tpi.on(\"session_start\", (_event, ctx) => {\n\t\t\tsessionCtx = ctx;\n\t\t\t// Defensive: a leftover registry (session_shutdown not seen) must not leak processes.\n\t\t\tif (registry) void registry.shutdown();\n\t\t\tregistry = new BackgroundTaskRegistry({\n\t\t\t\toperations: overrides?.operations ?? createSessionBashOperations(ctx),\n\t\t\t\toutputDir: overrides?.outputDir,\n\t\t\t\tmaxOutputBytes: overrides?.maxOutputBytes,\n\t\t\t\tstall: overrides?.stall,\n\t\t\t\t// Completion and stall reach the model the same way; that choice lives here only.\n\t\t\t\tonNotify: (notification) =>\n\t\t\t\t\tpi.sendMessage(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcustomType: BG_NOTIFICATION_TYPE,\n\t\t\t\t\t\t\tcontent: buildNotificationContent(notification),\n\t\t\t\t\t\t\tdisplay: true,\n\t\t\t\t\t\t\tdetails: toNotificationDetails(notification),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t// steer, not followUp: a task that finishes mid-run must reach the\n\t\t\t\t\t\t// model at the next turn boundary, not after the whole run ends.\n\t\t\t\t\t\t{ deliverAs: \"steer\", triggerTurn: true },\n\t\t\t\t\t),\n\t\t\t\tonChange: updateStatus,\n\t\t\t});\n\t\t\tupdateStatus();\n\t\t});\n\n\t\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\t\tctx.ui.setStatus(\"background\", undefined);\n\t\t\tsessionCtx = undefined;\n\t\t\tconst active = registry;\n\t\t\tregistry = undefined;\n\t\t\tawait active?.shutdown();\n\t\t});\n\n\t\tpi.registerTool<typeof bgSchema, BgDetails, BgRenderState>({\n\t\t\tname: \"bg\",\n\t\t\tlabel: \"bg\",\n\t\t\tdescription: BG_TOOL_DESCRIPTION,\n\t\t\tpromptSnippet: BG_PROMPT_SNIPPET,\n\t\t\tpromptGuidelines: BG_PROMPT_GUIDELINES,\n\t\t\tparameters: bgSchema,\n\t\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx): Promise<AgentToolResult<BgDetails>> {\n\t\t\t\tconst active = requireRegistry();\n\t\t\t\tswitch (params.action) {\n\t\t\t\t\tcase \"create\":\n\t\t\t\t\t\treturn runCreate(active, params, ctx);\n\t\t\t\t\tcase \"read\":\n\t\t\t\t\t\treturn runRead(active, params);\n\t\t\t\t\tcase \"wait\":\n\t\t\t\t\t\treturn runWait(active, params, signal);\n\t\t\t\t\tcase \"kill\":\n\t\t\t\t\t\treturn runKill(active, params);\n\t\t\t\t\tcase \"list\":\n\t\t\t\t\t\treturn runList(active);\n\t\t\t\t}\n\t\t\t},\n\t\t\trenderCall(args, theme, context) {\n\t\t\t\treturn renderBgCall(args, theme, context, waitLiveProbe);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderBgResult(result, options, theme, context);\n\t\t\t},\n\t\t});\n\n\t\tpi.registerMessageRenderer<BgNotificationDetails>(BG_NOTIFICATION_TYPE, renderBackgroundNotification);\n\n\t\tpi.registerCommand(\"bg\", {\n\t\t\tdescription: \"View and manage background tasks\",\n\t\t\thandler: async (_args, ctx: ExtensionCommandContext) => {\n\t\t\t\tconst active = registry;\n\t\t\t\tif (!active || active.counts().total === 0) {\n\t\t\t\t\tctx.ui.notify(\"No background tasks.\", \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\t// RPC/print: ctx.ui.custom is unavailable — show a bounded summary.\n\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\tconst lines = active\n\t\t\t\t\t\t.listTasks()\n\t\t\t\t\t\t.slice(0, BG_COMMAND_SUMMARY_SHOWN)\n\t\t\t\t\t\t.map((task) => describeTaskLine(task, now));\n\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t`Background tasks:\\n${lines.join(\"\\n\")}\\nUse the bg tool (action kill) to stop a task.`,\n\t\t\t\t\t\t\"info\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait showBackgroundManager(ctx, active);\n\t\t\t},\n\t\t});\n\t};\n}\n\nasync function showBackgroundManager(ctx: ExtensionCommandContext, registry: BackgroundTaskRegistry): Promise<void> {\n\t// No overlay options: the component mounts inline in the editor slot, like /model.\n\tawait ctx.ui.custom(\n\t\t(tui, theme, keybindings, done) =>\n\t\t\tnew BackgroundTasksMenu({\n\t\t\t\ttui,\n\t\t\t\ttheme,\n\t\t\t\tkeybindings,\n\t\t\t\thost: {\n\t\t\t\t\tlistTasks: () => registry.listTasks(),\n\t\t\t\t\tkillTask: (id) => registry.killTask(id),\n\t\t\t\t\treadSlice: (filePath, options) => readOutputSlice(filePath, options),\n\t\t\t\t},\n\t\t\t\tonClose: () => done(undefined),\n\t\t\t}),\n\t);\n}\n\nexport default createBackgroundExtension();\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/manager.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ import { stripTerminalSequences, truncateToWidth } from "@earendil-works/pi-tui"
8
8
  import { formatSize } from "../../core/tools/truncate.js";
9
9
  import { keyLabel } from "../../modes/interactive/components/keybinding-hints.js";
10
10
  import { sanitizeBinaryOutput } from "../../utils/shell.js";
11
- import { exitSuffix, runtimeLabel, statusColor, statusGlyph, taskLabel } from "./task-view.js";
11
+ import { exitSuffix, formatTaskCounts, runtimeLabel, statusColor, statusGlyph, taskLabel } from "./task-view.js";
12
12
  import { fileNameOf } from "./text.js";
13
13
  const POLL_INTERVAL_MS = 1000;
14
14
  const VIEW_TAIL_BYTES = 128 * 1024;
@@ -43,6 +43,7 @@ export class BackgroundTasksMenu {
43
43
  this.selectedTaskId = this.tasks[0]?.id;
44
44
  void this.tick();
45
45
  this.pollTimer = setInterval(() => void this.tick(), this.pollIntervalMs);
46
+ this.pollTimer.unref?.();
46
47
  }
47
48
  get focused() {
48
49
  return this._focused;
@@ -195,16 +196,14 @@ export class BackgroundTasksMenu {
195
196
  }
196
197
  countsLabel() {
197
198
  let running = 0;
198
- for (const task of this.tasks)
199
+ let stalled = 0;
200
+ for (const task of this.tasks) {
199
201
  if (task.status === "running")
200
202
  running++;
201
- const ended = this.tasks.length - running;
202
- const parts = [];
203
- if (running > 0)
204
- parts.push(`${running} running`);
205
- if (ended > 0)
206
- parts.push(`${ended} done`);
207
- return parts.join(" · ");
203
+ if (task.stalled)
204
+ stalled++;
205
+ }
206
+ return formatTaskCounts({ running, stalled, total: this.tasks.length }) ?? "";
208
207
  }
209
208
  // ── detail view ────────────────────────────────────────────────────────
210
209
  renderDetail(width) {
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/extensions/background/manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /bg task manager — an inline, /model-style menu that mounts in the editor\n * slot. Two views in one component: a task list, and a live output viewport\n * for the selected task (Enter to open, Esc to go back). The host dependency\n * is narrowed to three callbacks so the component stays testable.\n */\n\nimport { type Component, type Focusable, stripTerminalSequences, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { KeybindingsManager } from \"../../core/keybindings.ts\";\nimport { formatSize } from \"../../core/tools/truncate.ts\";\nimport { keyLabel } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { OutputSlice } from \"./output-file.ts\";\nimport type { BgTask } from \"./registry.ts\";\nimport { exitSuffix, runtimeLabel, statusColor, statusGlyph, taskLabel } from \"./task-view.ts\";\nimport { fileNameOf } from \"./text.ts\";\n\nconst POLL_INTERVAL_MS = 1000;\nconst VIEW_TAIL_BYTES = 128 * 1024;\nconst MAX_VIEW_LINES = 2000;\nconst LIST_MAX_VISIBLE = 10;\nconst LIST_MIN_VISIBLE = 3;\n/** Rows outside the task list itself (borders, title, counter, footer, padding). */\nconst LIST_RESERVED_ROWS = 14;\n\nexport interface BackgroundManagerHost {\n\tlistTasks(): BgTask[];\n\tkillTask(id: string): { killed: boolean };\n\treadSlice(filePath: string, options: { mode: \"tail\"; maxBytes: number }): Promise<OutputSlice>;\n}\n\ninterface MenuTui {\n\trequestRender(): void;\n\tterminal: { rows: number; columns: number };\n}\n\nexport interface BackgroundTasksMenuOptions {\n\ttui: MenuTui;\n\ttheme: Theme;\n\tkeybindings: Pick<KeybindingsManager, \"matches\" | \"getKeys\">;\n\thost: BackgroundManagerHost;\n\tonClose: () => void;\n\tpollIntervalMs?: number;\n}\n\ninterface TailCache {\n\ttaskId: string;\n\tlines: string[];\n\ttotalBytes: number;\n\t/** True once a terminal task's final output has been read; skips re-reads. */\n\tsettledRead?: boolean;\n\terror?: string;\n}\n\n/** Truncate to an exact visible width, padding short lines with spaces. */\nfunction padLine(line: string, width: number): string {\n\treturn truncateToWidth(line, width, \"…\", true);\n}\n\nexport class BackgroundTasksMenu implements Component, Focusable {\n\tprivate readonly tui: MenuTui;\n\tprivate readonly theme: Theme;\n\tprivate readonly keybindings: Pick<KeybindingsManager, \"matches\" | \"getKeys\">;\n\tprivate readonly host: BackgroundManagerHost;\n\tprivate readonly onClose: () => void;\n\tprivate readonly pollIntervalMs: number;\n\n\tprivate view: \"list\" | \"detail\" = \"list\";\n\tprivate tasks: BgTask[] = [];\n\tprivate selectedTaskId: string | undefined;\n\tprivate listScrollTop = 0;\n\tprivate follow = true;\n\tprivate tailOffsetLines = 0;\n\tprivate tailCache: TailCache | undefined;\n\tprivate killFeedback: string | undefined;\n\t/** Task whose settle expires killFeedback; undefined = persists until the next key. */\n\tprivate killFeedbackTaskId: string | undefined;\n\t/** Last tick's render signature; unchanged means nothing visible moved. */\n\tprivate lastSignature = \"\";\n\tprivate pollTimer: ReturnType<typeof setInterval> | undefined;\n\tprivate readBusy = false;\n\tprivate disposed = false;\n\tprivate _focused = false;\n\n\tconstructor(options: BackgroundTasksMenuOptions) {\n\t\tthis.tui = options.tui;\n\t\tthis.theme = options.theme;\n\t\tthis.keybindings = options.keybindings;\n\t\tthis.host = options.host;\n\t\tthis.onClose = options.onClose;\n\t\tthis.pollIntervalMs = options.pollIntervalMs ?? POLL_INTERVAL_MS;\n\n\t\tthis.tasks = this.host.listTasks();\n\t\tthis.selectedTaskId = this.tasks[0]?.id;\n\t\tvoid this.tick();\n\t\tthis.pollTimer = setInterval(() => void this.tick(), this.pollIntervalMs);\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached render state; the poller drives refresh.\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.pollTimer !== undefined) {\n\t\t\tclearInterval(this.pollTimer);\n\t\t\tthis.pollTimer = undefined;\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.killFeedback !== undefined) {\n\t\t\tthis.killFeedback = undefined;\n\t\t\tthis.killFeedbackTaskId = undefined;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tif (this.view === \"detail\") {\n\t\t\t\tthis.view = \"list\";\n\t\t\t\tthis.tui.requestRender();\n\t\t\t} else {\n\t\t\t\tthis.onClose();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"app.backgroundTasks.kill\")) {\n\t\t\tthis.killSelected();\n\t\t\treturn;\n\t\t}\n\t\tif (this.view === \"list\") this.handleListInput(data);\n\t\telse this.handleDetailInput(data);\n\t}\n\n\tprivate handleListInput(data: string): void {\n\t\tif (this.keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.moveSelection(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tthis.moveSelection(1);\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.confirm\")) {\n\t\t\tif (!this.selectedTask()) return;\n\t\t\tthis.view = \"detail\";\n\t\t\tthis.resetFollow();\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\tprivate handleDetailInput(data: string): void {\n\t\tif (this.keybindings.matches(data, \"tui.select.pageUp\")) {\n\t\t\tthis.scrollDetail(Math.max(1, this.detailBodyHeight()));\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.pageDown\")) {\n\t\t\tif (!this.follow) this.scrollDetail(-Math.max(1, this.detailBodyHeight()));\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.scrollDetail(1);\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tif (!this.follow) this.scrollDetail(-1);\n\t\t}\n\t}\n\n\t/** Scroll the detail viewport by `lines` (positive = back through history). */\n\tprivate scrollDetail(lines: number): void {\n\t\tif (lines > 0) {\n\t\t\tconst max = this.maxTailOffset();\n\t\t\tif (max === 0) return; // Nothing above the viewport to scroll back to.\n\t\t\tthis.follow = false;\n\t\t\tthis.tailOffsetLines = Math.min(max, this.tailOffsetLines + lines);\n\t\t} else {\n\t\t\tif (this.follow) return;\n\t\t\tthis.tailOffsetLines = Math.max(0, this.tailOffsetLines + lines);\n\t\t\tif (this.tailOffsetLines === 0) {\n\t\t\t\tthis.follow = true;\n\t\t\t\tvoid this.refreshViewport();\n\t\t\t}\n\t\t}\n\t\tthis.tui.requestRender();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst body = this.view === \"list\" ? this.renderList(width) : this.renderDetail(width);\n\t\tconst border = this.theme.fg(\"border\", \"─\".repeat(Math.max(1, width)));\n\t\treturn [border, ...body, padLine(this.footerLine(), width), border];\n\t}\n\n\t// ── list view ──────────────────────────────────────────────────────────\n\n\t/** Task rows the terminal can spare; short windows trade rows for the transcript. */\n\tprivate listMaxVisible(): number {\n\t\treturn Math.min(LIST_MAX_VISIBLE, Math.max(LIST_MIN_VISIBLE, this.tui.terminal.rows - LIST_RESERVED_ROWS));\n\t}\n\n\tprivate renderList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst counts = this.countsLabel();\n\t\tconst title = counts ? `Background tasks — ${counts}` : \"Background tasks\";\n\t\tlines.push(padLine(this.theme.fg(\"accent\", this.theme.bold(title)), width));\n\n\t\tif (this.tasks.length === 0) {\n\t\t\tlines.push(padLine(this.theme.fg(\"muted\", \" (no tasks)\"), width));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst selected = this.selectedTask();\n\t\tconst maxVisible = this.listMaxVisible();\n\t\tconst visible = this.tasks.slice(this.listScrollTop, this.listScrollTop + maxVisible);\n\t\tlet seenRunning = false;\n\t\tfor (const task of visible) {\n\t\t\t// listTasks() orders running first, finished after: one separator at the boundary.\n\t\t\tif (seenRunning && task.status !== \"running\") {\n\t\t\t\tlines.push(padLine(this.theme.fg(\"muted\", \" ── finished ──\"), width));\n\t\t\t\tseenRunning = false;\n\t\t\t}\n\t\t\tif (task.status === \"running\") seenRunning = true;\n\t\t\tlines.push(this.renderTaskRow(task, task === selected, width));\n\t\t}\n\t\tif (this.tasks.length > maxVisible) {\n\t\t\tconst index = this.tasks.findIndex((task) => task.id === this.selectedTaskId);\n\t\t\tlines.push(padLine(this.theme.fg(\"muted\", ` (${index + 1}/${this.tasks.length})`), width));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate renderTaskRow(task: BgTask, isSelected: boolean, width: number): string {\n\t\tconst duration = runtimeLabel(task);\n\t\tconst label = taskLabel(task);\n\t\tif (isSelected) {\n\t\t\t// Pad to full width before styling so the selection background spans the row.\n\t\t\tconst plain = padLine(`→ ${statusGlyph(task.status, task.stalled)} ${task.id} ${duration} ${label}`, width);\n\t\t\treturn this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", plain));\n\t\t}\n\t\tconst glyph = this.theme.fg(statusColor(task.status, task.stalled), statusGlyph(task.status, task.stalled));\n\t\tconst id = this.theme.fg(\"accent\", task.id);\n\t\tconst rest = this.theme.fg(\"muted\", `${duration} ${label}`);\n\t\treturn padLine(` ${glyph} ${id} ${rest}`, width);\n\t}\n\n\tprivate countsLabel(): string {\n\t\tlet running = 0;\n\t\tfor (const task of this.tasks) if (task.status === \"running\") running++;\n\t\tconst ended = this.tasks.length - running;\n\t\tconst parts: string[] = [];\n\t\tif (running > 0) parts.push(`${running} running`);\n\t\tif (ended > 0) parts.push(`${ended} done`);\n\t\treturn parts.join(\" · \");\n\t}\n\n\t// ── detail view ────────────────────────────────────────────────────────\n\n\tprivate renderDetail(width: number): string[] {\n\t\tconst task = this.selectedTask();\n\t\tconst lines: string[] = [padLine(this.detailHeader(task), width)];\n\t\tconst height = this.detailBodyHeight();\n\t\tconst cache = task && this.tailCache?.taskId === task.id ? this.tailCache : undefined;\n\t\tif (cache?.error) {\n\t\t\tlines.push(padLine(this.theme.fg(\"error\", `Cannot read output: ${cache.error}`), width));\n\t\t\tfor (let row = 1; row < height; row++) lines.push(padLine(\"\", width));\n\t\t\treturn lines;\n\t\t}\n\t\tconst content = cache?.lines ?? [];\n\t\tconst fromEnd = this.follow ? 0 : this.tailOffsetLines;\n\t\tconst start = content.length - height - fromEnd;\n\t\tfor (let row = 0; row < height; row++) {\n\t\t\tconst line = content[start + row];\n\t\t\tlines.push(line === undefined ? padLine(\"\", width) : padLine(this.theme.fg(\"toolOutput\", line), width));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate detailHeader(task: BgTask | undefined): string {\n\t\tif (!task) return this.theme.fg(\"muted\", \"(no task selected)\");\n\t\tconst duration = runtimeLabel(task);\n\t\tconst exit = exitSuffix(task.exitCode, \" \");\n\t\tconst resumeLabel = keyLabel(\"tui.select.pageDown\", { keybindings: this.keybindings });\n\t\tconst paused = this.follow ? \"\" : resumeLabel ? ` — paused (${resumeLabel} to follow)` : \" — paused\";\n\t\tconst fileName = fileNameOf(task.outputPath);\n\t\tconst glyph = this.theme.fg(statusColor(task.status, task.stalled), statusGlyph(task.status, task.stalled));\n\t\tconst statusLabel = task.stalled ? \"running, waiting for input\" : task.status;\n\t\tconst header = `${task.id} · ${statusLabel}${exit} · ${duration} · ${formatSize(task.outputBytes)} · ${fileName}${paused}`;\n\t\treturn `${glyph} ${this.theme.fg(\"muted\", header)}`;\n\t}\n\n\tprivate detailBodyHeight(): number {\n\t\t// Inline component: keep the chat transcript visible above the menu.\n\t\treturn Math.min(20, Math.max(4, this.tui.terminal.rows - 8));\n\t}\n\n\t// ── shared behavior ────────────────────────────────────────────────────\n\n\tprivate footerLine(): string {\n\t\tif (this.killFeedback) return this.theme.fg(\"warning\", this.killFeedback);\n\t\tconst opts = { keybindings: this.keybindings };\n\t\tconst parts =\n\t\t\tthis.view === \"list\"\n\t\t\t\t? [\n\t\t\t\t\t\t`${keyLabel(\"tui.select.up\", opts)}/${keyLabel(\"tui.select.down\", opts)} select`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.confirm\", opts)} output`,\n\t\t\t\t\t\t`${keyLabel(\"app.backgroundTasks.kill\", opts)} kill`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.cancel\", opts)} close`,\n\t\t\t\t\t]\n\t\t\t\t: [\n\t\t\t\t\t\t`${keyLabel(\"tui.select.up\", opts)}/${keyLabel(\"tui.select.down\", opts)} scroll`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.pageUp\", opts)}/${keyLabel(\"tui.select.pageDown\", opts)} page`,\n\t\t\t\t\t\t`${keyLabel(\"app.backgroundTasks.kill\", opts)} kill`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.cancel\", opts)} back`,\n\t\t\t\t\t];\n\t\treturn this.theme.fg(\"dim\", parts.join(\" · \"));\n\t}\n\n\tprivate selectedTask(): BgTask | undefined {\n\t\treturn this.tasks.find((task) => task.id === this.selectedTaskId);\n\t}\n\n\tprivate moveSelection(offset: -1 | 1): void {\n\t\tif (this.tasks.length === 0) return;\n\t\tconst current = this.tasks.findIndex((task) => task.id === this.selectedTaskId);\n\t\tconst next = (current + offset + this.tasks.length) % this.tasks.length;\n\t\tconst task = this.tasks[next];\n\t\tif (!task) return;\n\t\tthis.selectedTaskId = task.id;\n\t\tif (next < this.listScrollTop) this.listScrollTop = next;\n\t\tif (next >= this.listScrollTop + this.listMaxVisible()) {\n\t\t\tthis.listScrollTop = next - this.listMaxVisible() + 1;\n\t\t}\n\t\tthis.resetFollow();\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate resetFollow(): void {\n\t\tthis.follow = true;\n\t\tthis.tailOffsetLines = 0;\n\t\tthis.tailCache = undefined;\n\t\tvoid this.refreshViewport();\n\t}\n\n\tprivate currentLines(): string[] {\n\t\tconst task = this.selectedTask();\n\t\tconst cache = this.tailCache;\n\t\treturn task && cache && cache.taskId === task.id ? cache.lines : [];\n\t}\n\n\tprivate maxTailOffset(): number {\n\t\treturn Math.max(0, this.currentLines().length - this.detailBodyHeight());\n\t}\n\n\tprivate killSelected(): void {\n\t\tconst task = this.selectedTask();\n\t\tif (!task) return;\n\t\tif (task.status !== \"running\") {\n\t\t\tthis.killFeedback = `${task.id} is not running`;\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tthis.host.killTask(task.id);\n\t\t// Terminal state lands on the next poll; the feedback expires with it (tick).\n\t\tthis.killFeedback = `stopping ${task.id}…`;\n\t\tthis.killFeedbackTaskId = task.id;\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate async tick(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.tasks = this.host.listTasks();\n\t\tif (!this.selectedTask() && this.tasks[0]) this.selectedTaskId = this.tasks[0].id;\n\t\tthis.expireKillFeedback();\n\t\t// A pending re-read counts as a change: refreshViewport may skip this tick\n\t\t// (readBusy), and the signature alone would let that retry be dropped.\n\t\tconst needsRead = this.view === \"detail\" && this.follow && this.detailOutputChanged();\n\t\tif (!needsRead && this.renderSignature() === this.lastSignature) return; // Nothing visible changed.\n\t\tif (needsRead) await this.refreshViewport();\n\t\tthis.lastSignature = this.renderSignature();\n\t\tthis.tui.requestRender();\n\t}\n\n\t/** Drop \"stopping\" feedback once its task has settled. */\n\tprivate expireKillFeedback(): void {\n\t\tif (this.killFeedbackTaskId === undefined) return;\n\t\tconst task = this.tasks.find((candidate) => candidate.id === this.killFeedbackTaskId);\n\t\tif (!task || task.status !== \"running\") {\n\t\t\tthis.killFeedback = undefined;\n\t\t\tthis.killFeedbackTaskId = undefined;\n\t\t}\n\t}\n\n\t/** Everything the rendered output depends on; equal signatures need no redraw. */\n\tprivate renderSignature(): string {\n\t\tconst parts = [\n\t\t\tthis.view,\n\t\t\tthis.selectedTaskId ?? \"\",\n\t\t\tString(this.listScrollTop),\n\t\t\tString(this.follow),\n\t\t\tString(this.tailOffsetLines),\n\t\t\tthis.killFeedback ?? \"\",\n\t\t\tthis.tailCache\n\t\t\t\t? `${this.tailCache.taskId}:${this.tailCache.lines.length}:${this.tailCache.totalBytes}:${this.tailCache.error ?? \"\"}:${String(this.tailCache.settledRead ?? false)}`\n\t\t\t\t: \"\",\n\t\t];\n\t\tfor (const task of this.tasks) {\n\t\t\tconst duration = runtimeLabel(task);\n\t\t\tparts.push(\n\t\t\t\t`${task.id}:${task.status}:${String(task.stalled)}:${task.outputBytes}:${task.exitCode ?? \"\"}:${duration}`,\n\t\t\t);\n\t\t}\n\t\treturn parts.join(\"|\");\n\t}\n\n\t/** True when the detail viewport must re-read the output file this tick. */\n\tprivate detailOutputChanged(): boolean {\n\t\tconst task = this.selectedTask();\n\t\tif (!task) return false;\n\t\tconst cache = this.tailCache;\n\t\tif (!cache || cache.taskId !== task.id) return true;\n\t\tif (cache.error !== undefined) return true; // Keep retrying; failed reads are cheap.\n\t\tif (task.status !== \"running\") {\n\t\t\t// Terminal task: the file is final — read it once, then never again.\n\t\t\treturn cache.settledRead !== true;\n\t\t}\n\t\treturn cache.totalBytes !== task.outputBytes;\n\t}\n\n\tprivate async refreshViewport(): Promise<void> {\n\t\tif (this.readBusy) return;\n\t\tconst task = this.selectedTask();\n\t\tif (!task) return;\n\t\tthis.readBusy = true;\n\t\ttry {\n\t\t\tconst slice = await this.host.readSlice(task.outputPath, { mode: \"tail\", maxBytes: VIEW_TAIL_BYTES });\n\t\t\tif (this.disposed || this.selectedTaskId !== task.id || !this.follow) return;\n\t\t\tthis.tailCache = {\n\t\t\t\ttaskId: task.id,\n\t\t\t\tlines: toViewLines(slice.text),\n\t\t\t\ttotalBytes: slice.totalBytes,\n\t\t\t\tsettledRead: task.status !== \"running\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (this.disposed || this.selectedTaskId !== task.id) return;\n\t\t\tthis.tailCache = {\n\t\t\t\ttaskId: task.id,\n\t\t\t\tlines: [],\n\t\t\t\ttotalBytes: task.outputBytes,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t} finally {\n\t\t\tthis.readBusy = false;\n\t\t}\n\t}\n}\n\nfunction toViewLines(text: string): string[] {\n\t// Strip ANSI first: sanitize would remove the escape bytes alone, leaving\n\t// orphaned \"[31m\" fragments that strip can no longer see.\n\tconst lines = sanitizeBinaryOutput(stripTerminalSequences(text)).split(\"\\n\");\n\t// Drop the trailing empty line produced by a final newline, then bound the\n\t// window: a 128KB slice of one-byte lines must never become 128k rows.\n\twhile (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n\treturn lines.slice(-MAX_VIEW_LINES);\n}\n"]}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/extensions/background/manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /bg task manager — an inline, /model-style menu that mounts in the editor\n * slot. Two views in one component: a task list, and a live output viewport\n * for the selected task (Enter to open, Esc to go back). The host dependency\n * is narrowed to three callbacks so the component stays testable.\n */\n\nimport { type Component, type Focusable, stripTerminalSequences, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { KeybindingsManager } from \"../../core/keybindings.ts\";\nimport { formatSize } from \"../../core/tools/truncate.ts\";\nimport { keyLabel } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { OutputSlice } from \"./output-file.ts\";\nimport type { BgTask } from \"./registry.ts\";\nimport { exitSuffix, formatTaskCounts, runtimeLabel, statusColor, statusGlyph, taskLabel } from \"./task-view.ts\";\nimport { fileNameOf } from \"./text.ts\";\n\nconst POLL_INTERVAL_MS = 1000;\nconst VIEW_TAIL_BYTES = 128 * 1024;\nconst MAX_VIEW_LINES = 2000;\nconst LIST_MAX_VISIBLE = 10;\nconst LIST_MIN_VISIBLE = 3;\n/** Rows outside the task list itself (borders, title, counter, footer, padding). */\nconst LIST_RESERVED_ROWS = 14;\n\nexport interface BackgroundManagerHost {\n\tlistTasks(): BgTask[];\n\tkillTask(id: string): { killed: boolean };\n\treadSlice(filePath: string, options: { mode: \"tail\"; maxBytes: number }): Promise<OutputSlice>;\n}\n\ninterface MenuTui {\n\trequestRender(): void;\n\tterminal: { rows: number; columns: number };\n}\n\nexport interface BackgroundTasksMenuOptions {\n\ttui: MenuTui;\n\ttheme: Theme;\n\tkeybindings: Pick<KeybindingsManager, \"matches\" | \"getKeys\">;\n\thost: BackgroundManagerHost;\n\tonClose: () => void;\n\tpollIntervalMs?: number;\n}\n\ninterface TailCache {\n\ttaskId: string;\n\tlines: string[];\n\ttotalBytes: number;\n\t/** True once a terminal task's final output has been read; skips re-reads. */\n\tsettledRead?: boolean;\n\terror?: string;\n}\n\n/** Truncate to an exact visible width, padding short lines with spaces. */\nfunction padLine(line: string, width: number): string {\n\treturn truncateToWidth(line, width, \"…\", true);\n}\n\nexport class BackgroundTasksMenu implements Component, Focusable {\n\tprivate readonly tui: MenuTui;\n\tprivate readonly theme: Theme;\n\tprivate readonly keybindings: Pick<KeybindingsManager, \"matches\" | \"getKeys\">;\n\tprivate readonly host: BackgroundManagerHost;\n\tprivate readonly onClose: () => void;\n\tprivate readonly pollIntervalMs: number;\n\n\tprivate view: \"list\" | \"detail\" = \"list\";\n\tprivate tasks: BgTask[] = [];\n\tprivate selectedTaskId: string | undefined;\n\tprivate listScrollTop = 0;\n\tprivate follow = true;\n\tprivate tailOffsetLines = 0;\n\tprivate tailCache: TailCache | undefined;\n\tprivate killFeedback: string | undefined;\n\t/** Task whose settle expires killFeedback; undefined = persists until the next key. */\n\tprivate killFeedbackTaskId: string | undefined;\n\t/** Last tick's render signature; unchanged means nothing visible moved. */\n\tprivate lastSignature = \"\";\n\tprivate pollTimer: ReturnType<typeof setInterval> | undefined;\n\tprivate readBusy = false;\n\tprivate disposed = false;\n\tprivate _focused = false;\n\n\tconstructor(options: BackgroundTasksMenuOptions) {\n\t\tthis.tui = options.tui;\n\t\tthis.theme = options.theme;\n\t\tthis.keybindings = options.keybindings;\n\t\tthis.host = options.host;\n\t\tthis.onClose = options.onClose;\n\t\tthis.pollIntervalMs = options.pollIntervalMs ?? POLL_INTERVAL_MS;\n\n\t\tthis.tasks = this.host.listTasks();\n\t\tthis.selectedTaskId = this.tasks[0]?.id;\n\t\tvoid this.tick();\n\t\tthis.pollTimer = setInterval(() => void this.tick(), this.pollIntervalMs);\n\t\tthis.pollTimer.unref?.();\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached render state; the poller drives refresh.\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.pollTimer !== undefined) {\n\t\t\tclearInterval(this.pollTimer);\n\t\t\tthis.pollTimer = undefined;\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.killFeedback !== undefined) {\n\t\t\tthis.killFeedback = undefined;\n\t\t\tthis.killFeedbackTaskId = undefined;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tif (this.view === \"detail\") {\n\t\t\t\tthis.view = \"list\";\n\t\t\t\tthis.tui.requestRender();\n\t\t\t} else {\n\t\t\t\tthis.onClose();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"app.backgroundTasks.kill\")) {\n\t\t\tthis.killSelected();\n\t\t\treturn;\n\t\t}\n\t\tif (this.view === \"list\") this.handleListInput(data);\n\t\telse this.handleDetailInput(data);\n\t}\n\n\tprivate handleListInput(data: string): void {\n\t\tif (this.keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.moveSelection(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tthis.moveSelection(1);\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.confirm\")) {\n\t\t\tif (!this.selectedTask()) return;\n\t\t\tthis.view = \"detail\";\n\t\t\tthis.resetFollow();\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\tprivate handleDetailInput(data: string): void {\n\t\tif (this.keybindings.matches(data, \"tui.select.pageUp\")) {\n\t\t\tthis.scrollDetail(Math.max(1, this.detailBodyHeight()));\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.pageDown\")) {\n\t\t\tif (!this.follow) this.scrollDetail(-Math.max(1, this.detailBodyHeight()));\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.scrollDetail(1);\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tif (!this.follow) this.scrollDetail(-1);\n\t\t}\n\t}\n\n\t/** Scroll the detail viewport by `lines` (positive = back through history). */\n\tprivate scrollDetail(lines: number): void {\n\t\tif (lines > 0) {\n\t\t\tconst max = this.maxTailOffset();\n\t\t\tif (max === 0) return; // Nothing above the viewport to scroll back to.\n\t\t\tthis.follow = false;\n\t\t\tthis.tailOffsetLines = Math.min(max, this.tailOffsetLines + lines);\n\t\t} else {\n\t\t\tif (this.follow) return;\n\t\t\tthis.tailOffsetLines = Math.max(0, this.tailOffsetLines + lines);\n\t\t\tif (this.tailOffsetLines === 0) {\n\t\t\t\tthis.follow = true;\n\t\t\t\tvoid this.refreshViewport();\n\t\t\t}\n\t\t}\n\t\tthis.tui.requestRender();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst body = this.view === \"list\" ? this.renderList(width) : this.renderDetail(width);\n\t\tconst border = this.theme.fg(\"border\", \"─\".repeat(Math.max(1, width)));\n\t\treturn [border, ...body, padLine(this.footerLine(), width), border];\n\t}\n\n\t// ── list view ──────────────────────────────────────────────────────────\n\n\t/** Task rows the terminal can spare; short windows trade rows for the transcript. */\n\tprivate listMaxVisible(): number {\n\t\treturn Math.min(LIST_MAX_VISIBLE, Math.max(LIST_MIN_VISIBLE, this.tui.terminal.rows - LIST_RESERVED_ROWS));\n\t}\n\n\tprivate renderList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst counts = this.countsLabel();\n\t\tconst title = counts ? `Background tasks — ${counts}` : \"Background tasks\";\n\t\tlines.push(padLine(this.theme.fg(\"accent\", this.theme.bold(title)), width));\n\n\t\tif (this.tasks.length === 0) {\n\t\t\tlines.push(padLine(this.theme.fg(\"muted\", \" (no tasks)\"), width));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst selected = this.selectedTask();\n\t\tconst maxVisible = this.listMaxVisible();\n\t\tconst visible = this.tasks.slice(this.listScrollTop, this.listScrollTop + maxVisible);\n\t\tlet seenRunning = false;\n\t\tfor (const task of visible) {\n\t\t\t// listTasks() orders running first, finished after: one separator at the boundary.\n\t\t\tif (seenRunning && task.status !== \"running\") {\n\t\t\t\tlines.push(padLine(this.theme.fg(\"muted\", \" ── finished ──\"), width));\n\t\t\t\tseenRunning = false;\n\t\t\t}\n\t\t\tif (task.status === \"running\") seenRunning = true;\n\t\t\tlines.push(this.renderTaskRow(task, task === selected, width));\n\t\t}\n\t\tif (this.tasks.length > maxVisible) {\n\t\t\tconst index = this.tasks.findIndex((task) => task.id === this.selectedTaskId);\n\t\t\tlines.push(padLine(this.theme.fg(\"muted\", ` (${index + 1}/${this.tasks.length})`), width));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate renderTaskRow(task: BgTask, isSelected: boolean, width: number): string {\n\t\tconst duration = runtimeLabel(task);\n\t\tconst label = taskLabel(task);\n\t\tif (isSelected) {\n\t\t\t// Pad to full width before styling so the selection background spans the row.\n\t\t\tconst plain = padLine(`→ ${statusGlyph(task.status, task.stalled)} ${task.id} ${duration} ${label}`, width);\n\t\t\treturn this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", plain));\n\t\t}\n\t\tconst glyph = this.theme.fg(statusColor(task.status, task.stalled), statusGlyph(task.status, task.stalled));\n\t\tconst id = this.theme.fg(\"accent\", task.id);\n\t\tconst rest = this.theme.fg(\"muted\", `${duration} ${label}`);\n\t\treturn padLine(` ${glyph} ${id} ${rest}`, width);\n\t}\n\n\tprivate countsLabel(): string {\n\t\tlet running = 0;\n\t\tlet stalled = 0;\n\t\tfor (const task of this.tasks) {\n\t\t\tif (task.status === \"running\") running++;\n\t\t\tif (task.stalled) stalled++;\n\t\t}\n\t\treturn formatTaskCounts({ running, stalled, total: this.tasks.length }) ?? \"\";\n\t}\n\n\t// ── detail view ────────────────────────────────────────────────────────\n\n\tprivate renderDetail(width: number): string[] {\n\t\tconst task = this.selectedTask();\n\t\tconst lines: string[] = [padLine(this.detailHeader(task), width)];\n\t\tconst height = this.detailBodyHeight();\n\t\tconst cache = task && this.tailCache?.taskId === task.id ? this.tailCache : undefined;\n\t\tif (cache?.error) {\n\t\t\tlines.push(padLine(this.theme.fg(\"error\", `Cannot read output: ${cache.error}`), width));\n\t\t\tfor (let row = 1; row < height; row++) lines.push(padLine(\"\", width));\n\t\t\treturn lines;\n\t\t}\n\t\tconst content = cache?.lines ?? [];\n\t\tconst fromEnd = this.follow ? 0 : this.tailOffsetLines;\n\t\tconst start = content.length - height - fromEnd;\n\t\tfor (let row = 0; row < height; row++) {\n\t\t\tconst line = content[start + row];\n\t\t\tlines.push(line === undefined ? padLine(\"\", width) : padLine(this.theme.fg(\"toolOutput\", line), width));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate detailHeader(task: BgTask | undefined): string {\n\t\tif (!task) return this.theme.fg(\"muted\", \"(no task selected)\");\n\t\tconst duration = runtimeLabel(task);\n\t\tconst exit = exitSuffix(task.exitCode, \" \");\n\t\tconst resumeLabel = keyLabel(\"tui.select.pageDown\", { keybindings: this.keybindings });\n\t\tconst paused = this.follow ? \"\" : resumeLabel ? ` — paused (${resumeLabel} to follow)` : \" — paused\";\n\t\tconst fileName = fileNameOf(task.outputPath);\n\t\tconst glyph = this.theme.fg(statusColor(task.status, task.stalled), statusGlyph(task.status, task.stalled));\n\t\tconst statusLabel = task.stalled ? \"running, waiting for input\" : task.status;\n\t\tconst header = `${task.id} · ${statusLabel}${exit} · ${duration} · ${formatSize(task.outputBytes)} · ${fileName}${paused}`;\n\t\treturn `${glyph} ${this.theme.fg(\"muted\", header)}`;\n\t}\n\n\tprivate detailBodyHeight(): number {\n\t\t// Inline component: keep the chat transcript visible above the menu.\n\t\treturn Math.min(20, Math.max(4, this.tui.terminal.rows - 8));\n\t}\n\n\t// ── shared behavior ────────────────────────────────────────────────────\n\n\tprivate footerLine(): string {\n\t\tif (this.killFeedback) return this.theme.fg(\"warning\", this.killFeedback);\n\t\tconst opts = { keybindings: this.keybindings };\n\t\tconst parts =\n\t\t\tthis.view === \"list\"\n\t\t\t\t? [\n\t\t\t\t\t\t`${keyLabel(\"tui.select.up\", opts)}/${keyLabel(\"tui.select.down\", opts)} select`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.confirm\", opts)} output`,\n\t\t\t\t\t\t`${keyLabel(\"app.backgroundTasks.kill\", opts)} kill`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.cancel\", opts)} close`,\n\t\t\t\t\t]\n\t\t\t\t: [\n\t\t\t\t\t\t`${keyLabel(\"tui.select.up\", opts)}/${keyLabel(\"tui.select.down\", opts)} scroll`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.pageUp\", opts)}/${keyLabel(\"tui.select.pageDown\", opts)} page`,\n\t\t\t\t\t\t`${keyLabel(\"app.backgroundTasks.kill\", opts)} kill`,\n\t\t\t\t\t\t`${keyLabel(\"tui.select.cancel\", opts)} back`,\n\t\t\t\t\t];\n\t\treturn this.theme.fg(\"dim\", parts.join(\" · \"));\n\t}\n\n\tprivate selectedTask(): BgTask | undefined {\n\t\treturn this.tasks.find((task) => task.id === this.selectedTaskId);\n\t}\n\n\tprivate moveSelection(offset: -1 | 1): void {\n\t\tif (this.tasks.length === 0) return;\n\t\tconst current = this.tasks.findIndex((task) => task.id === this.selectedTaskId);\n\t\tconst next = (current + offset + this.tasks.length) % this.tasks.length;\n\t\tconst task = this.tasks[next];\n\t\tif (!task) return;\n\t\tthis.selectedTaskId = task.id;\n\t\tif (next < this.listScrollTop) this.listScrollTop = next;\n\t\tif (next >= this.listScrollTop + this.listMaxVisible()) {\n\t\t\tthis.listScrollTop = next - this.listMaxVisible() + 1;\n\t\t}\n\t\tthis.resetFollow();\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate resetFollow(): void {\n\t\tthis.follow = true;\n\t\tthis.tailOffsetLines = 0;\n\t\tthis.tailCache = undefined;\n\t\tvoid this.refreshViewport();\n\t}\n\n\tprivate currentLines(): string[] {\n\t\tconst task = this.selectedTask();\n\t\tconst cache = this.tailCache;\n\t\treturn task && cache && cache.taskId === task.id ? cache.lines : [];\n\t}\n\n\tprivate maxTailOffset(): number {\n\t\treturn Math.max(0, this.currentLines().length - this.detailBodyHeight());\n\t}\n\n\tprivate killSelected(): void {\n\t\tconst task = this.selectedTask();\n\t\tif (!task) return;\n\t\tif (task.status !== \"running\") {\n\t\t\tthis.killFeedback = `${task.id} is not running`;\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tthis.host.killTask(task.id);\n\t\t// Terminal state lands on the next poll; the feedback expires with it (tick).\n\t\tthis.killFeedback = `stopping ${task.id}…`;\n\t\tthis.killFeedbackTaskId = task.id;\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate async tick(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.tasks = this.host.listTasks();\n\t\tif (!this.selectedTask() && this.tasks[0]) this.selectedTaskId = this.tasks[0].id;\n\t\tthis.expireKillFeedback();\n\t\t// A pending re-read counts as a change: refreshViewport may skip this tick\n\t\t// (readBusy), and the signature alone would let that retry be dropped.\n\t\tconst needsRead = this.view === \"detail\" && this.follow && this.detailOutputChanged();\n\t\tif (!needsRead && this.renderSignature() === this.lastSignature) return; // Nothing visible changed.\n\t\tif (needsRead) await this.refreshViewport();\n\t\tthis.lastSignature = this.renderSignature();\n\t\tthis.tui.requestRender();\n\t}\n\n\t/** Drop \"stopping\" feedback once its task has settled. */\n\tprivate expireKillFeedback(): void {\n\t\tif (this.killFeedbackTaskId === undefined) return;\n\t\tconst task = this.tasks.find((candidate) => candidate.id === this.killFeedbackTaskId);\n\t\tif (!task || task.status !== \"running\") {\n\t\t\tthis.killFeedback = undefined;\n\t\t\tthis.killFeedbackTaskId = undefined;\n\t\t}\n\t}\n\n\t/** Everything the rendered output depends on; equal signatures need no redraw. */\n\tprivate renderSignature(): string {\n\t\tconst parts = [\n\t\t\tthis.view,\n\t\t\tthis.selectedTaskId ?? \"\",\n\t\t\tString(this.listScrollTop),\n\t\t\tString(this.follow),\n\t\t\tString(this.tailOffsetLines),\n\t\t\tthis.killFeedback ?? \"\",\n\t\t\tthis.tailCache\n\t\t\t\t? `${this.tailCache.taskId}:${this.tailCache.lines.length}:${this.tailCache.totalBytes}:${this.tailCache.error ?? \"\"}:${String(this.tailCache.settledRead ?? false)}`\n\t\t\t\t: \"\",\n\t\t];\n\t\tfor (const task of this.tasks) {\n\t\t\tconst duration = runtimeLabel(task);\n\t\t\tparts.push(\n\t\t\t\t`${task.id}:${task.status}:${String(task.stalled)}:${task.outputBytes}:${task.exitCode ?? \"\"}:${duration}`,\n\t\t\t);\n\t\t}\n\t\treturn parts.join(\"|\");\n\t}\n\n\t/** True when the detail viewport must re-read the output file this tick. */\n\tprivate detailOutputChanged(): boolean {\n\t\tconst task = this.selectedTask();\n\t\tif (!task) return false;\n\t\tconst cache = this.tailCache;\n\t\tif (!cache || cache.taskId !== task.id) return true;\n\t\tif (cache.error !== undefined) return true; // Keep retrying; failed reads are cheap.\n\t\tif (task.status !== \"running\") {\n\t\t\t// Terminal task: the file is final — read it once, then never again.\n\t\t\treturn cache.settledRead !== true;\n\t\t}\n\t\treturn cache.totalBytes !== task.outputBytes;\n\t}\n\n\tprivate async refreshViewport(): Promise<void> {\n\t\tif (this.readBusy) return;\n\t\tconst task = this.selectedTask();\n\t\tif (!task) return;\n\t\tthis.readBusy = true;\n\t\ttry {\n\t\t\tconst slice = await this.host.readSlice(task.outputPath, { mode: \"tail\", maxBytes: VIEW_TAIL_BYTES });\n\t\t\tif (this.disposed || this.selectedTaskId !== task.id || !this.follow) return;\n\t\t\tthis.tailCache = {\n\t\t\t\ttaskId: task.id,\n\t\t\t\tlines: toViewLines(slice.text),\n\t\t\t\ttotalBytes: slice.totalBytes,\n\t\t\t\tsettledRead: task.status !== \"running\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (this.disposed || this.selectedTaskId !== task.id) return;\n\t\t\tthis.tailCache = {\n\t\t\t\ttaskId: task.id,\n\t\t\t\tlines: [],\n\t\t\t\ttotalBytes: task.outputBytes,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t} finally {\n\t\t\tthis.readBusy = false;\n\t\t}\n\t}\n}\n\nfunction toViewLines(text: string): string[] {\n\t// Strip ANSI first: sanitize would remove the escape bytes alone, leaving\n\t// orphaned \"[31m\" fragments that strip can no longer see.\n\tconst lines = sanitizeBinaryOutput(stripTerminalSequences(text)).split(\"\\n\");\n\t// Drop the trailing empty line produced by a final newline, then bound the\n\t// window: a 128KB slice of one-byte lines must never become 128k rows.\n\twhile (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n\treturn lines.slice(-MAX_VIEW_LINES);\n}\n"]}
@@ -92,6 +92,8 @@ export interface BackgroundRegistryOptions {
92
92
  /** Called whenever a task starts or reaches a terminal state. */
93
93
  onChange: () => void;
94
94
  now?: () => number;
95
+ /** Test hook for deterministic collision coverage. */
96
+ createTaskId?: () => string;
95
97
  }
96
98
  export type ResolveTaskResult = {
97
99
  ok: true;
@@ -114,6 +116,7 @@ export declare class BackgroundTaskRegistry {
114
116
  private readonly stallTailBytes;
115
117
  private readonly onChange;
116
118
  private readonly now;
119
+ private readonly createTaskId;
117
120
  private readonly tasks;
118
121
  private readonly runtimes;
119
122
  private shuttingDown;
@@ -175,7 +178,7 @@ export declare class BackgroundTaskRegistry {
175
178
  private probeStallTail;
176
179
  /** The one place a notification payload is shaped, for either kind. */
177
180
  private emit;
178
- private newTaskId;
181
+ private allocateTaskOutput;
179
182
  private run;
180
183
  private handleData;
181
184
  private finalize;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/extensions/background/registry.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@ export const DEFAULT_MAX_OUTPUT_BYTES = 20 * 1024 * 1024;
22
22
  export const DEFAULT_MAX_RUNNING_TASKS = 8;
23
23
  export const DEFAULT_NOTIFY_TAIL_BYTES = 4 * 1024;
24
24
  const SHUTDOWN_GRACE_MS = 2_000;
25
+ const TASK_ID_ALLOCATION_ATTEMPTS = 100;
25
26
  /** Stall watchdog defaults, matching Claude Code's CC-1175 tuning. */
26
27
  export const STALL_POLL_INTERVAL_MS = 5_000;
27
28
  export const STALL_THRESHOLD_MS = 45_000;
@@ -63,6 +64,7 @@ export class BackgroundTaskRegistry {
63
64
  this.stallTailBytes = stall?.tailBytes ?? STALL_TAIL_BYTES;
64
65
  this.onChange = options.onChange;
65
66
  this.now = options.now ?? Date.now;
67
+ this.createTaskId = options.createTaskId ?? (() => `bg-${randomBytes(3).toString("hex")}`);
66
68
  }
67
69
  get isShuttingDown() {
68
70
  return this.shuttingDown;
@@ -76,7 +78,7 @@ export class BackgroundTaskRegistry {
76
78
  const list = running.map((task) => ` ${task.id} ${firstCommandLine(task.command)}`).join("\n");
77
79
  throw new Error(`Too many running background tasks (limit ${this.maxRunningTasks}). Kill one first (bg action kill):\n${list}`);
78
80
  }
79
- const id = this.newTaskId();
81
+ const { id, outputPath } = this.allocateTaskOutput();
80
82
  const task = {
81
83
  id,
82
84
  command: input.command,
@@ -85,7 +87,7 @@ export class BackgroundTaskRegistry {
85
87
  status: "running",
86
88
  startedAt: this.now(),
87
89
  exitCode: undefined,
88
- outputPath: join(this.outputDir, `pi-${id}.log`),
90
+ outputPath,
89
91
  outputBytes: 0,
90
92
  outputTruncated: false,
91
93
  timeoutSeconds: input.timeoutSeconds,
@@ -96,11 +98,6 @@ export class BackgroundTaskRegistry {
96
98
  const done = new Promise((resolve) => {
97
99
  resolveDone = resolve;
98
100
  });
99
- // Create the file synchronously so read/wait and the /bg viewer never see
100
- // ENOENT between task start and the stream's async open. The exclusive
101
- // 'wx' flag fails on any existing path — including a symlink — so creation
102
- // can never truncate a file a stale id (or an attacker-planted link) points at.
103
- createOutputFileExclusively(task.outputPath);
104
101
  const runtime = {
105
102
  controller: new AbortController(),
106
103
  stream: createWriteStream(task.outputPath, { flags: "a" }),
@@ -318,11 +315,14 @@ export class BackgroundTaskRegistry {
318
315
  const { slice, error } = await readTail(task.outputPath, this.stallTailBytes);
319
316
  if (runtime.finalized || this.shuttingDown)
320
317
  return;
321
- // Merely slow: not prompt-shaped. Reset so the next check is a full
322
- // threshold window out instead of re-probing on every tick. A tail that
323
- // could not be read is reported, not dismissed as slow. Matched on the
324
- // raw text — sanitizing is for delivery, not for detection.
325
- if (slice && !looksLikePrompt(slice.text)) {
318
+ // A read failure is not evidence of an interactive prompt. Retry after a
319
+ // full threshold window without changing task state or notifying. Detection
320
+ // uses raw text; sanitizing is only for delivery.
321
+ if (error) {
322
+ runtime.stallLastGrowthAt = this.now();
323
+ return;
324
+ }
325
+ if (!slice || !looksLikePrompt(slice.text)) {
326
326
  runtime.stallLastGrowthAt = this.now();
327
327
  return;
328
328
  }
@@ -350,12 +350,25 @@ export class BackgroundTaskRegistry {
350
350
  // there is no retry path, so the delivery record stands.
351
351
  }
352
352
  }
353
- newTaskId() {
354
- while (true) {
355
- const id = `bg-${randomBytes(3).toString("hex")}`;
356
- if (!this.tasks.has(id))
357
- return id;
353
+ allocateTaskOutput() {
354
+ for (let attempt = 0; attempt < TASK_ID_ALLOCATION_ATTEMPTS; attempt++) {
355
+ const id = this.createTaskId();
356
+ if (this.tasks.has(id))
357
+ continue;
358
+ const outputPath = join(this.outputDir, `pi-${id}.log`);
359
+ try {
360
+ // Create synchronously before returning the task so read/wait and /bg
361
+ // never see ENOENT. `wx` also protects stale logs and symlinks.
362
+ createOutputFileExclusively(outputPath);
363
+ return { id, outputPath };
364
+ }
365
+ catch (error) {
366
+ if (error.code === "EEXIST")
367
+ continue;
368
+ throw error;
369
+ }
358
370
  }
371
+ throw new Error(`Could not allocate a unique background task id after ${TASK_ID_ALLOCATION_ATTEMPTS} attempts.`);
359
372
  }
360
373
  async run(task, runtime, env) {
361
374
  let outcome;