mixdog 0.9.2 → 0.9.4

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 (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,108 @@
1
+ import { spawn, spawnSync } from 'child_process';
2
+
3
+ // Process/pid lifecycle helpers for background shell jobs: liveness probing,
4
+ // tree-kill, the module-level live-job pid registry, and the CLI-shutdown exit
5
+ // hook that reaps owned children. Async jobs are intentionally CLI-owned; no
6
+ // restart replay or daemon handoff is attempted.
7
+
8
+ export function isPidAlive(pid) {
9
+ if (!Number.isFinite(pid) || pid <= 0) return false;
10
+ try {
11
+ process.kill(pid, 0);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ export function killProcessTree(pid, signal = 'SIGTERM') {
19
+ if (!Number.isFinite(pid) || pid <= 0) return false;
20
+ try {
21
+ if (process.platform === 'win32') {
22
+ spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
23
+ } else {
24
+ try { process.kill(-pid, signal); }
25
+ catch { process.kill(pid, signal); }
26
+ // SIGKILL escalation: a background child that ignores SIGTERM must
27
+ // not survive (foreground treeKill / persistent _killProcessTree
28
+ // already do this). After a 3s grace, force-kill the group/pid.
29
+ // unref so this backstop never holds the host process open.
30
+ if (signal === 'SIGTERM') {
31
+ const t = setTimeout(() => {
32
+ try { process.kill(-pid, 'SIGKILL'); }
33
+ catch { try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ } }
34
+ }, 3000);
35
+ if (t.unref) t.unref();
36
+ }
37
+ }
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ // Module-level tracking of live background-job pids so CLI shutdown can reap
45
+ // owned children. Async jobs are intentionally CLI-owned; no restart replay or
46
+ // daemon handoff is attempted.
47
+ export const _liveJobPids = new Set();
48
+ export const _liveJobIdsByPid = new Map();
49
+ let _shellJobsExitHookInstalled = false;
50
+ export function _registerLiveJobPid(pid, jobId = null) {
51
+ if (Number.isFinite(pid) && pid > 0) {
52
+ _liveJobPids.add(pid);
53
+ if (jobId) _liveJobIdsByPid.set(pid, jobId);
54
+ }
55
+ }
56
+ export function _unregisterLiveJobPid(pid) {
57
+ if (Number.isFinite(pid) && pid > 0) {
58
+ _liveJobPids.delete(pid);
59
+ _liveJobIdsByPid.delete(pid);
60
+ }
61
+ }
62
+ export function _killLiveJobPid(pid, { sync = false } = {}) {
63
+ try {
64
+ if (process.platform === 'win32') {
65
+ if (sync) {
66
+ spawnSync('taskkill.exe', ['/pid', String(pid), '/t', '/f'], {
67
+ windowsHide: true,
68
+ stdio: 'ignore',
69
+ timeout: 1500,
70
+ });
71
+ } else {
72
+ spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
73
+ }
74
+ } else {
75
+ try { process.kill(-pid, 'SIGKILL'); }
76
+ catch { try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ } }
77
+ }
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+ export function _sweepLiveJobsSync() {
84
+ for (const pid of _liveJobPids) {
85
+ _killLiveJobPid(pid, { sync: true });
86
+ }
87
+ _liveJobPids.clear();
88
+ _liveJobIdsByPid.clear();
89
+ }
90
+ function _ensureProcessListenerHeadroom(events, extra = 1) {
91
+ try {
92
+ if (typeof process.getMaxListeners !== 'function' || typeof process.setMaxListeners !== 'function') return;
93
+ const current = process.getMaxListeners();
94
+ if (current === 0) return;
95
+ let needed = current;
96
+ for (const event of events) needed = Math.max(needed, process.listenerCount(event) + extra);
97
+ if (needed > current) process.setMaxListeners(needed);
98
+ } catch { /* ignore */ }
99
+ }
100
+ export function _installShellJobsExitHook() {
101
+ if (_shellJobsExitHookInstalled) return;
102
+ _shellJobsExitHookInstalled = true;
103
+ _ensureProcessListenerHeadroom(['exit', 'SIGTERM', 'SIGINT', 'SIGHUP'], 1);
104
+ try { process.on('exit', _sweepLiveJobsSync); } catch { /* ignore */ }
105
+ try { process.on('SIGTERM', _sweepLiveJobsSync); } catch { /* ignore */ }
106
+ try { process.on('SIGINT', _sweepLiveJobsSync); } catch { /* ignore */ }
107
+ try { process.on('SIGHUP', _sweepLiveJobsSync); } catch { /* ignore */ }
108
+ }
@@ -1,19 +1,49 @@
1
- import { spawn, spawnSync } from 'child_process';
2
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, watch as fsWatch, writeFileSync } from 'fs';
3
- import * as fsPromises from 'fs/promises';
4
- import { basename, join } from 'path';
5
- import { getPluginData } from '../../config.mjs';
1
+ import { spawn } from 'child_process';
2
+ import { closeSync, existsSync, openSync, readFileSync, readSync, statSync, watch as fsWatch, writeFileSync } from 'fs';
3
+ import { basename } from 'path';
6
4
  import { stripAnsi } from '../shell-command.mjs';
7
5
  import { scrubLoaderVars, scrubProviderSecrets } from '../env-scrub.mjs';
8
6
  import {
9
7
  normalizeToolNotifyContext,
10
8
  notifyToolCompletion,
11
9
  } from '../../../../shared/tool-execution-contract.mjs';
10
+ import {
11
+ renderShellCompletionEnvelope,
12
+ shellCompletionInstruction,
13
+ renderShellPromptStallEnvelope,
14
+ shellPromptStallInstruction,
15
+ } from '../../../../shared/task-notification-envelope.mjs';
12
16
  import {
13
17
  completeBackgroundTask,
14
18
  notifyBackgroundTaskProgress,
19
+ getBackgroundTask,
15
20
  } from '../../../../shared/background-tasks.mjs';
16
21
  import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
22
+ import {
23
+ getShellJobsDir,
24
+ shellJobStdoutPath,
25
+ shellJobStderrPath,
26
+ shellJobExitPath,
27
+ shellJobDonePath,
28
+ shellJobEnforcedPath,
29
+ resolveJobOwnerHostPid,
30
+ writeShellJobDetail,
31
+ readShellJobDetail,
32
+ } from './shell-job-paths.mjs';
33
+ import {
34
+ isPidAlive,
35
+ killProcessTree,
36
+ _unregisterLiveJobPid,
37
+ _installShellJobsExitHook,
38
+ _registerLiveJobPid,
39
+ _liveJobPids,
40
+ _liveJobIdsByPid,
41
+ _killLiveJobPid,
42
+ } from './shell-job-process.mjs';
43
+
44
+ // Facade re-exports: path/detail helpers and the job-not-found message moved
45
+ // to sibling modules; keep existing importers of shell-jobs.mjs resolving.
46
+ export { buildJobNotFoundMessage } from './shell-job-paths.mjs';
17
47
 
18
48
  globalThis.__mixdogShellJobsRuntimeLoaded = true;
19
49
 
@@ -21,269 +51,9 @@ function sleep(ms) {
21
51
  return new Promise((resolve) => setTimeout(resolve, ms));
22
52
  }
23
53
 
24
- // One-shot sweep of stale shell-job artefacts. Each backgrounded `bash`
25
- // emits five files (.json/.done/.exit/.stdout.log/.stderr.log); the .done
26
- // flag is written when the job exits, so a .done file older than
27
- // SHELL_JOB_STALE_MS is the invariant proof its sibling files are also
28
- // safe to remove. Active and recently-completed jobs are kept so
29
- // `task` status/read/wait readers still find them. Runs once per mcp child
30
- // lifetime on first getShellJobsDir() call. Async dirent walk + parallel
31
- // stat/unlink keeps the main event loop free; fire-and-forget so the
32
- // synchronous caller receives `dir` immediately.
33
- const SHELL_JOB_STALE_MS = 24 * 60 * 60 * 1000;
34
- let shellJobsSwept = false;
35
- async function sweepStaleShellJobs(dir) {
36
- if (shellJobsSwept) return;
37
- shellJobsSwept = true;
38
- const cutoff = Date.now() - SHELL_JOB_STALE_MS;
39
- let names;
40
- try { names = await fsPromises.readdir(dir); } catch { return; }
41
- const expired = [];
42
- await Promise.all(names.map(async (name) => {
43
- if (!name.endsWith('.done')) return;
44
- const p = join(dir, name);
45
- try {
46
- const st = await fsPromises.stat(p);
47
- if (st.mtimeMs < cutoff) expired.push(name.slice(0, -5));
48
- } catch {}
49
- }));
50
- // Orphan reclaim: a crashed wrapper leaves <id>.json with no .done —
51
- // forever. Invariant proofs of death (either suffices, both gated on the
52
- // stale cutoff so a young orphan can't race its own spawn):
53
- // a) deadline: the wrapper enforces timeoutMs, so an entry older than
54
- // timeoutMs + grace cannot still be running — pid-reuse-proof. Only
55
- // trusted on runtime proof: detail.timeoutEnforced:true (PS wrapper,
56
- // unconditional) or the <id>.enforced marker the posix wrapper
57
- // touches when its `timeout` branch actually runs.
58
- // b) ESRCH: the recorded pid no longer exists. Alone this misses pids
59
- // recycled by unrelated live processes, hence (a).
60
- const ORPHAN_DEADLINE_GRACE_MS = 30 * 60_000;
61
- const doneSet = new Set(names.filter(n => n.endsWith('.done')).map(n => n.slice(0, -5)));
62
- await Promise.all(names.map(async (name) => {
63
- if (!name.endsWith('.json')) return;
64
- const jobId = name.slice(0, -5);
65
- if (doneSet.has(jobId)) return;
66
- const p = join(dir, name);
67
- try {
68
- const st = await fsPromises.stat(p);
69
- if (st.mtimeMs >= cutoff) return;
70
- const detail = JSON.parse(await fsPromises.readFile(p, 'utf-8'));
71
- const tmo = Number(detail?.timeoutMs);
72
- const enforced = detail?.timeoutEnforced === true
73
- || existsSync(join(dir, `${jobId}.enforced`));
74
- const deadlinePassed = enforced
75
- && Number.isFinite(tmo) && tmo > 0
76
- && (Date.now() - st.mtimeMs) > tmo + ORPHAN_DEADLINE_GRACE_MS;
77
- if (!deadlinePassed) {
78
- const pid = Number(detail?.pid);
79
- if (Number.isFinite(pid) && pid > 0) {
80
- try { process.kill(pid, 0); return; } // alive (or EPERM → treated dead only via ESRCH below)
81
- catch (e) { if (e?.code !== 'ESRCH') return; }
82
- }
83
- }
84
- expired.push(jobId);
85
- } catch {}
86
- }));
87
- // Owner sidecar markers carry a dynamic `.owner-<pid>` suffix, so they
88
- // can't sit in the fixed-extension list — map each expired jobId to the
89
- // marker name(s) actually present in this listing and unlink those too.
90
- const expiredSet = new Set(expired);
91
- const ownerMarkers = names.filter((n) => {
92
- const i = n.lastIndexOf('.owner-');
93
- return i > 0 && expiredSet.has(n.slice(0, i));
94
- });
95
- await Promise.all([
96
- ...expired.flatMap((jobId) =>
97
- ['.json', '.done', '.exit', '.enforced', '.exit.cmd.sh', '.exit.cmd.ps1', '.stdout.log', '.stderr.log'].map((ext) =>
98
- fsPromises.unlink(join(dir, jobId + ext)).catch(() => {}),
99
- ),
100
- ),
101
- ...ownerMarkers.map((n) => fsPromises.unlink(join(dir, n)).catch(() => {})),
102
- ]);
103
- }
104
-
105
- function getShellJobsDir() {
106
- const dir = join(getPluginData(), 'shell-jobs');
107
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
108
- sweepStaleShellJobs(dir);
109
- return dir;
110
- }
111
-
112
- function shellJobDetailPath(jobId) { return join(getShellJobsDir(), `${jobId}.json`); }
113
- function shellJobStdoutPath(jobId) { return join(getShellJobsDir(), `${jobId}.stdout.log`); }
114
- function shellJobStderrPath(jobId) { return join(getShellJobsDir(), `${jobId}.stderr.log`); }
115
- function shellJobExitPath(jobId) { return join(getShellJobsDir(), `${jobId}.exit`); }
116
- function shellJobDonePath(jobId) { return join(getShellJobsDir(), `${jobId}.done`); }
117
- // Runtime proof that the posix wrapper's `timeout` branch actually ran: the
118
- // wrapper touches this marker immediately before exec'ing `timeout`, so its
119
- // existence is never optimistic (no spawn-time probe can guarantee the
120
- // wrapper's own env/cwd resolution). PS jobs don't need it — their wrapper
121
- // enforces unconditionally and records detail.timeoutEnforced:true.
122
- function shellJobEnforcedPath(jobId) { return join(getShellJobsDir(), `${jobId}.enforced`); }
123
- // Owner sidecar marker: a zero-byte file whose NAME encodes the owning CC host
124
- // (claude.exe) pid — `<jobId>.owner-<pid>`. It lets the statusline owner-filter
125
- // jobs from a SINGLE directory listing (no per-job JSON read) so the filter can
126
- // precede its per-tick scan cap — otherwise other sessions' newer jobs evict
127
- // this session's live jobs before filtering. Swept alongside the other
128
- // artefacts.
129
- function shellJobOwnerPath(jobId, pid) { return join(getShellJobsDir(), `${jobId}.owner-${pid}`); }
130
-
131
54
  const JOB_STATUS_PREVIEW_MAX_BYTES = 4096;
132
55
  const JOB_STATUS_PREVIEW_MAX_LINES = 20;
133
56
  const JOB_STATUS_PREVIEW_MAX_CHARS = 1200;
134
-
135
- // Resolve the CLI host pid that owns a freshly-spawned job. Standalone CLI
136
- // execution is process-owned: background tasks are scoped to this CLI lifetime,
137
- // and the pid stamp is only for statusline filtering / legacy sidecars.
138
- function resolveJobOwnerHostPid(clientHostPid) {
139
- const explicit = Number(clientHostPid);
140
- if (Number.isInteger(explicit) && explicit > 0) return explicit;
141
- const envPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
142
- if (Number.isInteger(envPid) && envPid > 0) return envPid;
143
- return null;
144
- }
145
-
146
- function writeShellJobDetail(detail) {
147
- // Session scope stamp: every job record is tagged with the CC host pid
148
- // that owns it (the claude.exe pid). This is the SAME physical pid the
149
- // statusline shim passes as --client-host-pid, so the statusline can count
150
- // only its own session's jobs by exact pid equality (no heuristic). The
151
- // spawn sites set detail.ownerHostPid from the per-request threaded
152
- // clientHostPid (resolveJobOwnerHostPid); this fallback only stamps the
153
- // single-client env value when no spawn-site pid was set, and NEVER
154
- // overwrites an existing stamp — so the field round-trips through disk on
155
- // refresh/kill and a correct per-terminal stamp is preserved across rewrites.
156
- if (detail && detail.ownerHostPid == null) {
157
- const hostPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
158
- if (Number.isInteger(hostPid) && hostPid > 0) detail.ownerHostPid = hostPid;
159
- }
160
- writeFileSync(shellJobDetailPath(detail.jobId), JSON.stringify(detail, null, 2), 'utf-8');
161
- // Owner sidecar: encode the resolved owner pid in the marker filename so the
162
- // statusline can owner-filter from the directory listing alone (before its
163
- // scan cap). Idempotent zero-byte write — safe to repeat on refresh/kill
164
- // rewrites. Skipped when no owner is known (legacy/unattributed jobs).
165
- if (detail && Number.isInteger(detail.ownerHostPid) && detail.ownerHostPid > 0) {
166
- try { writeFileSync(shellJobOwnerPath(detail.jobId, detail.ownerHostPid), '', 'utf-8'); }
167
- catch { /* best-effort marker */ }
168
- }
169
- }
170
-
171
- function readShellJobDetail(jobId) {
172
- try {
173
- const p = shellJobDetailPath(jobId);
174
- if (!existsSync(p)) return null;
175
- return JSON.parse(readFileSync(p, 'utf-8'));
176
- } catch {
177
- return null;
178
- }
179
- }
180
-
181
- export function buildJobNotFoundMessage(jobId) {
182
- return `Error: task not found: ${jobId}`;
183
- }
184
-
185
- function isPidAlive(pid) {
186
- if (!Number.isFinite(pid) || pid <= 0) return false;
187
- try {
188
- process.kill(pid, 0);
189
- return true;
190
- } catch {
191
- return false;
192
- }
193
- }
194
-
195
- function killProcessTree(pid, signal = 'SIGTERM') {
196
- if (!Number.isFinite(pid) || pid <= 0) return false;
197
- try {
198
- if (process.platform === 'win32') {
199
- spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
200
- } else {
201
- try { process.kill(-pid, signal); }
202
- catch { process.kill(pid, signal); }
203
- // SIGKILL escalation: a background child that ignores SIGTERM must
204
- // not survive (foreground treeKill / persistent _killProcessTree
205
- // already do this). After a 3s grace, force-kill the group/pid.
206
- // unref so this backstop never holds the host process open.
207
- if (signal === 'SIGTERM') {
208
- const t = setTimeout(() => {
209
- try { process.kill(-pid, 'SIGKILL'); }
210
- catch { try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ } }
211
- }, 3000);
212
- if (t.unref) t.unref();
213
- }
214
- }
215
- return true;
216
- } catch {
217
- return false;
218
- }
219
- }
220
-
221
- // Module-level tracking of live background-job pids so CLI shutdown can reap
222
- // owned children. Async jobs are intentionally CLI-owned; no restart replay or
223
- // daemon handoff is attempted.
224
- const _liveJobPids = new Set();
225
- const _liveJobIdsByPid = new Map();
226
- let _shellJobsExitHookInstalled = false;
227
- function _registerLiveJobPid(pid, jobId = null) {
228
- if (Number.isFinite(pid) && pid > 0) {
229
- _liveJobPids.add(pid);
230
- if (jobId) _liveJobIdsByPid.set(pid, jobId);
231
- }
232
- }
233
- function _unregisterLiveJobPid(pid) {
234
- if (Number.isFinite(pid) && pid > 0) {
235
- _liveJobPids.delete(pid);
236
- _liveJobIdsByPid.delete(pid);
237
- }
238
- }
239
- function _killLiveJobPid(pid, { sync = false } = {}) {
240
- try {
241
- if (process.platform === 'win32') {
242
- if (sync) {
243
- spawnSync('taskkill.exe', ['/pid', String(pid), '/t', '/f'], {
244
- windowsHide: true,
245
- stdio: 'ignore',
246
- timeout: 1500,
247
- });
248
- } else {
249
- spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
250
- }
251
- } else {
252
- try { process.kill(-pid, 'SIGKILL'); }
253
- catch { try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ } }
254
- }
255
- return true;
256
- } catch {
257
- return false;
258
- }
259
- }
260
- function _sweepLiveJobsSync() {
261
- for (const pid of _liveJobPids) {
262
- _killLiveJobPid(pid, { sync: true });
263
- }
264
- _liveJobPids.clear();
265
- _liveJobIdsByPid.clear();
266
- }
267
- function _ensureProcessListenerHeadroom(events, extra = 1) {
268
- try {
269
- if (typeof process.getMaxListeners !== 'function' || typeof process.setMaxListeners !== 'function') return;
270
- const current = process.getMaxListeners();
271
- if (current === 0) return;
272
- let needed = current;
273
- for (const event of events) needed = Math.max(needed, process.listenerCount(event) + extra);
274
- if (needed > current) process.setMaxListeners(needed);
275
- } catch { /* ignore */ }
276
- }
277
- function _installShellJobsExitHook() {
278
- if (_shellJobsExitHookInstalled) return;
279
- _shellJobsExitHookInstalled = true;
280
- _ensureProcessListenerHeadroom(['exit', 'SIGTERM', 'SIGINT', 'SIGHUP'], 1);
281
- try { process.on('exit', _sweepLiveJobsSync); } catch { /* ignore */ }
282
- try { process.on('SIGTERM', _sweepLiveJobsSync); } catch { /* ignore */ }
283
- try { process.on('SIGINT', _sweepLiveJobsSync); } catch { /* ignore */ }
284
- try { process.on('SIGHUP', _sweepLiveJobsSync); } catch { /* ignore */ }
285
- }
286
-
287
57
  function shellQuoteSingle(s) {
288
58
  return `'${String(s).replace(/'/g, `'\"'\"'`)}'`;
289
59
  }
@@ -729,22 +499,21 @@ export function watchBackgroundShellJob(jobId, notifyCtx) {
729
499
  const elapsedMs = Number.isFinite(startedAtMs) ? Math.max(0, finishedAtMs - startedAtMs) : null;
730
500
  const exitCode = (typeof detail.exitCode === 'number') ? detail.exitCode : null;
731
501
  const status = detail.status || (reason === 'timeout' ? 'running' : 'unknown');
732
- const lines = [
733
- `[task_id: ${jobId}]`,
734
- `[status: ${status}]`,
735
- `[exit: ${exitCode === null ? 'n/a' : exitCode}]`,
736
- elapsedMs !== null ? `[elapsed: ${elapsedMs} ms]` : null,
737
- detail.command ? `[command: ${detail.command}]` : null,
738
- '',
739
- detail.summary ? `Summary: ${detail.summary}` : null,
740
- detail.stdoutPreview ? `\n[stdout preview]\n${detail.stdoutPreview}` : null,
741
- (detail.mergeStderr !== true && detail.stderrPreview) ? `\n[stderr preview]\n${detail.stderrPreview}` : null,
742
- ].filter((l) => l !== null && l !== '');
743
- const body = lines.join('\n');
502
+ const body = renderShellCompletionEnvelope({
503
+ jobId,
504
+ status,
505
+ exitCode,
506
+ elapsedMs,
507
+ command: detail.command,
508
+ summary: detail.summary,
509
+ stdoutPreview: detail.stdoutPreview,
510
+ stderrPreview: detail.stderrPreview,
511
+ mergeStderr: detail.mergeStderr,
512
+ });
744
513
  const taskStatus = status === 'completed'
745
514
  ? 'completed'
746
515
  : (status === 'cancelled' ? 'cancelled' : 'failed');
747
- const instruction = `The background shell task ${jobId} you started earlier has finished (${status}, exit ${exitCode === null ? 'n/a' : exitCode}) - review this result in your next step.`;
516
+ const instruction = shellCompletionInstruction({ jobId, status, exitCode });
748
517
  const completedTask = completeBackgroundTask(jobId, {
749
518
  status: taskStatus,
750
519
  result: shellJobPublicTaskResult(detail),
@@ -780,33 +549,38 @@ export function watchBackgroundShellJob(jobId, notifyCtx) {
780
549
  if (now - lastOutputAtMs < SHELL_JOB_PROMPT_STALL_MS) return;
781
550
  if (!looksLikeInteractivePrompt(tail.text)) return;
782
551
  const elapsedMs = now - (Date.parse(detail.startedAt || '') || now);
783
- const body = [
784
- `[task_id: ${jobId}]`,
785
- '[status: running]',
786
- `[stalled: no output growth for ${now - lastOutputAtMs} ms]`,
787
- elapsedMs >= 0 ? `[elapsed: ${elapsedMs} ms]` : null,
788
- detail.command ? `[command: ${detail.command}]` : null,
789
- '',
790
- 'This background shell task appears to be waiting for interactive input. Background tasks cannot answer prompts automatically; cancel it or rerun with non-interactive flags/input.',
791
- tail.text ? `\n${tail.text}` : null,
792
- ].filter((line) => line !== null && line !== '').join('\n');
793
- const instruction = `The background shell task ${jobId} appears to be waiting for interactive input; inspect the prompt, then cancel or rerun it non-interactively.`;
794
- const sent = notifyBackgroundTaskProgress(jobId, {
795
- text: body,
796
- resultType: 'shell_task_progress',
797
- instruction,
798
- key: 'interactive-prompt-stall',
799
- status: null,
800
- }) || notifyToolCompletion({
801
- surface: 'shell',
802
- id: jobId,
803
- status: null,
804
- text: body,
805
- resultType: 'shell_task_progress',
806
- instruction,
807
- context: ctx,
808
- logPrefix: 'shell-jobs',
552
+ const body = renderShellPromptStallEnvelope({
553
+ jobId,
554
+ stalledMs: now - lastOutputAtMs,
555
+ elapsedMs,
556
+ command: detail.command,
557
+ tailText: tail.text,
809
558
  });
559
+ const instruction = shellPromptStallInstruction({ jobId });
560
+ // Prefer the progress-notify path when a background task row exists.
561
+ // Its once-key ('interactive-prompt-stall') dedupe returns false after
562
+ // a re-arm even though the row is present — do NOT fall back to
563
+ // notifyToolCompletion in that case (it would fire a duplicate). Only
564
+ // use the completion fallback when the task row is absent (progress
565
+ // path unavailable).
566
+ const sent = getBackgroundTask(jobId)
567
+ ? notifyBackgroundTaskProgress(jobId, {
568
+ text: body,
569
+ resultType: 'shell_task_progress',
570
+ instruction,
571
+ key: 'interactive-prompt-stall',
572
+ status: null,
573
+ })
574
+ : notifyToolCompletion({
575
+ surface: 'shell',
576
+ id: jobId,
577
+ status: null,
578
+ text: body,
579
+ resultType: 'shell_task_progress',
580
+ instruction,
581
+ context: ctx,
582
+ logPrefix: 'shell-jobs',
583
+ });
810
584
  if (sent) promptStallNotified = true;
811
585
  };
812
586
  const checkDone = (reason) => {
@@ -35,8 +35,6 @@ import {
35
35
  executeGlobTool,
36
36
  executeGrepTool,
37
37
  } from './builtin/search-tool.mjs';
38
- import { executeDiagnosticsTool } from './builtin/diagnostics-tool.mjs';
39
- import { executeOpenConfigTool } from './builtin/open-config-tool.mjs';
40
38
  import {
41
39
  configureReadRangeIndexTelemetry,
42
40
  flushReadRangeIndexesSync,
@@ -46,6 +44,7 @@ import {
46
44
  extractPdfText,
47
45
  } from './builtin/read-special-files.mjs';
48
46
  import { computeUnifiedDiff } from './builtin/diff-utils.mjs';
47
+ import { tryExecuteExternalToolAdapter, isExternalAdapterTool } from './builtin/external-tool-adapters.mjs';
49
48
  import { formatToolStartProgress } from './progress-message.mjs';
50
49
  import { BUILTIN_TOOLS } from './builtin/builtin-tools.mjs';
51
50
  import { validateBuiltinArgs } from './builtin/arg-guard.mjs';
@@ -490,10 +489,6 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
490
489
  return executeTaskTool(args, options);
491
490
  case 'read':
492
491
  return executeReadTool(args, workDir, readStateScope, executeChildBuiltinTool, options, _readToolHelpers);
493
- case 'diagnostics':
494
- return executeDiagnosticsTool(args, workDir, options);
495
- case 'open_config':
496
- return executeOpenConfigTool();
497
492
  case 'grep':
498
493
  return executeGrepTool(args, workDir, executeChildBuiltinTool, readStateScope, options);
499
494
  case 'glob':
@@ -517,6 +512,10 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
517
512
  case 'hex':
518
513
  return executeHexTool(args, workDir, readStateScope, _readModeHelpers);
519
514
  default:
515
+ {
516
+ const adapted = await tryExecuteExternalToolAdapter(name, args, workDir, options);
517
+ if (adapted !== null) return adapted;
518
+ }
520
519
  return formatUnknownBuiltinToolMessage(name, args);
521
520
  }
522
521
  })();
@@ -531,6 +530,12 @@ export function isBuiltinTool(name) {
531
530
  return BUILTIN_TOOLS.some(t => t.name === toolName);
532
531
  }
533
532
 
533
+ // Re-export so the session loop can route foreign-CLI tool names
534
+ // (StrReplace/Write/bash variants) into executeBuiltinTool — whose default:
535
+ // case runs the native adapter — instead of short-circuiting to the
536
+ // unknown-tool redirect message at the dispatch layer.
537
+ export { isExternalAdapterTool };
538
+
534
539
  // Test-only exports for smart truncation helpers (see
535
540
  // scripts/test-smart-truncation.mjs). Runtime callers inside this module
536
541
  // use the local bindings unchanged; these named exports just make the