mixdog 0.8.0 → 0.9.0

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 (285) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +344 -0
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1223 -95
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2325 -450
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +118 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
  114. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
  115. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  116. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  117. package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
  118. package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
  119. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  120. package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
  121. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  123. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  124. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  125. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  128. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  129. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  130. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  132. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  139. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  140. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  141. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  143. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  144. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
  145. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  146. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  147. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  148. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  149. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  150. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  151. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  152. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  153. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  155. package/src/runtime/channels/index.mjs +152 -24
  156. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  157. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  158. package/src/runtime/channels/lib/tool-format.mjs +0 -2
  159. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  160. package/src/runtime/channels/lib/webhook.mjs +1 -1
  161. package/src/runtime/channels/tool-defs.mjs +29 -29
  162. package/src/runtime/memory/index.mjs +635 -107
  163. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  164. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  165. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  166. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  167. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  168. package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
  169. package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
  170. package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
  171. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  172. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  173. package/src/runtime/memory/lib/memory.mjs +88 -9
  174. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  175. package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  177. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  178. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  179. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  180. package/src/runtime/memory/tool-defs.mjs +46 -37
  181. package/src/runtime/search/index.mjs +102 -466
  182. package/src/runtime/search/lib/web-tools.mjs +45 -25
  183. package/src/runtime/search/tool-defs.mjs +16 -23
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/atomic-file.mjs +4 -3
  186. package/src/runtime/shared/background-tasks.mjs +122 -11
  187. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  188. package/src/runtime/shared/config.mjs +7 -4
  189. package/src/runtime/shared/err-text.mjs +131 -4
  190. package/src/runtime/shared/llm/cost.mjs +2 -2
  191. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  192. package/src/runtime/shared/llm/index.mjs +34 -11
  193. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  194. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  195. package/src/runtime/shared/singleton-owner.mjs +104 -0
  196. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  197. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  198. package/src/runtime/shared/tool-surface.mjs +624 -98
  199. package/src/runtime/shared/user-data-guard.mjs +0 -2
  200. package/src/standalone/agent-task-status.mjs +203 -0
  201. package/src/standalone/agent-task-status.test.mjs +76 -0
  202. package/src/standalone/agent-tool.mjs +1913 -0
  203. package/src/standalone/channel-worker.mjs +370 -14
  204. package/src/standalone/explore-tool.mjs +165 -70
  205. package/src/standalone/folder-dialog.mjs +314 -0
  206. package/src/standalone/hook-bus.mjs +898 -22
  207. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  208. package/src/standalone/projects.mjs +226 -0
  209. package/src/standalone/provider-admin.mjs +41 -24
  210. package/src/standalone/seeds.mjs +2 -69
  211. package/src/standalone/usage-dashboard.mjs +96 -8
  212. package/src/tui/App.jsx +4798 -2153
  213. package/src/tui/components/AnsiText.jsx +39 -28
  214. package/src/tui/components/ContextPanel.jsx +87 -29
  215. package/src/tui/components/Markdown.jsx +43 -77
  216. package/src/tui/components/MarkdownTable.jsx +9 -184
  217. package/src/tui/components/Message.jsx +28 -11
  218. package/src/tui/components/Picker.jsx +95 -56
  219. package/src/tui/components/PromptInput.jsx +367 -239
  220. package/src/tui/components/QueuedCommands.jsx +1 -1
  221. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  222. package/src/tui/components/Spinner.jsx +67 -38
  223. package/src/tui/components/StatusLine.jsx +606 -38
  224. package/src/tui/components/TextEntryPanel.jsx +128 -9
  225. package/src/tui/components/ToolExecution.jsx +617 -368
  226. package/src/tui/components/TurnDone.jsx +3 -3
  227. package/src/tui/components/UsagePanel.jsx +3 -5
  228. package/src/tui/components/tool-output-format.mjs +365 -0
  229. package/src/tui/components/tool-output-format.test.mjs +220 -0
  230. package/src/tui/dist/index.mjs +8915 -2418
  231. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  232. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  233. package/src/tui/engine.mjs +1455 -279
  234. package/src/tui/figures.mjs +21 -40
  235. package/src/tui/index.jsx +75 -31
  236. package/src/tui/input-editing.mjs +25 -0
  237. package/src/tui/markdown/format-token.mjs +511 -68
  238. package/src/tui/markdown/format-token.test.mjs +216 -0
  239. package/src/tui/markdown/render-ansi.mjs +94 -0
  240. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  241. package/src/tui/markdown/stream-fence.mjs +34 -0
  242. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  243. package/src/tui/markdown/table-layout.mjs +250 -0
  244. package/src/tui/paste-attachments.mjs +0 -7
  245. package/src/tui/spinner-verbs.mjs +1 -2
  246. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  247. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  248. package/src/tui/theme.mjs +746 -24
  249. package/src/tui/time-format.mjs +1 -1
  250. package/src/tui/transcript-tool-failures.mjs +67 -0
  251. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  252. package/src/ui/ansi.mjs +1 -2
  253. package/src/ui/markdown.mjs +85 -26
  254. package/src/ui/markdown.test.mjs +70 -0
  255. package/src/ui/model-display.mjs +121 -0
  256. package/src/ui/session-stats.mjs +44 -0
  257. package/src/ui/statusline-context-label.test.mjs +15 -0
  258. package/src/ui/statusline.mjs +386 -178
  259. package/src/ui/tool-card.mjs +3 -16
  260. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  261. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  262. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  263. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  264. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  265. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  266. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  267. package/src/workflows/default/WORKFLOW.md +12 -5
  268. package/src/workflows/default/workflow.json +0 -1
  269. package/src/workflows/solo/WORKFLOW.md +15 -0
  270. package/src/workflows/solo/workflow.json +7 -0
  271. package/vendor/ink/build/output.js +6 -1
  272. package/src/agents/scheduler-task.md +0 -3
  273. package/src/agents/web-researcher/AGENT.md +0 -3
  274. package/src/agents/web-researcher/agent.json +0 -6
  275. package/src/agents/webhook-handler.md +0 -3
  276. package/src/rules/bridge/00-common.md +0 -5
  277. package/src/rules/bridge/30-explorer.md +0 -4
  278. package/src/rules/lead/00-tool-lead.md +0 -5
  279. package/src/rules/shared/00-language.md +0 -3
  280. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  281. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  282. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  283. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  284. package/src/standalone/bridge-tool.mjs +0 -1414
  285. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -6,12 +6,12 @@ const MIN_PROVIDER_TIMEOUT_MS = 30_000;
6
6
  export const STALL_TICK_MS = 15_000;
7
7
  export const DEFAULT_STALL_WARN_S = 300;
8
8
  export const DEFAULT_STALL_ABORT_S = 600;
9
- // First-byte (no-stream-delta) abort for the bridge stall watchdog. A wedged
9
+ // First-byte (no-stream-delta) abort for the agent stall watchdog. A wedged
10
10
  // socket can sit at stage=requesting with zero server events. The 30s deadline
11
11
  // trialed here false-aborted slow high-reasoning first bytes (e.g. gpt-5.5
12
12
  // XHIGH, which can legitimately think >30s before the first delta) and, paired
13
13
  // with dispatch auto-retry, produced premature aborts + duplicate re-dispatches.
14
- // Auto-retry is now removed (bridge-retry.mjs MAX_ATTEMPTS = 1), so a single
14
+ // Auto-retry is now removed, so a single
15
15
  // attempt must get a generous first-byte window: 300s (5 min). Env-overridable.
16
16
  export const DEFAULT_STALL_FIRST_BYTE_ABORT_S = (() => {
17
17
  const raw = process.env.MIXDOG_STALL_FIRST_BYTE_ABORT_S;
@@ -58,6 +58,14 @@ export function resolveTimeoutMs(envNames, fallbackMs, { minMs = 1_000, maxMs =
58
58
  return Math.min(Math.max(fallbackMs, minMs), maxMs);
59
59
  }
60
60
 
61
+ // While a tool call is in flight, bump lastProgressAt on this cadence so long
62
+ // executions stay "active" without emitting model-visible content.
63
+ export const DEFAULT_ACTIVITY_HEARTBEAT_MS = resolveTimeoutMs(
64
+ 'MIXDOG_AGENT_ACTIVITY_HEARTBEAT_MS',
65
+ 30_000,
66
+ { minMs: 5_000, maxMs: 300_000 },
67
+ );
68
+
61
69
  export const PROVIDER_FIRST_BYTE_TIMEOUT_MS = resolveTimeoutMs(
62
70
  'MIXDOG_PROVIDER_FIRST_BYTE_TIMEOUT_MS',
63
71
  Math.min(120_000, PROVIDER_MAX_BEFORE_WARN_MS),
@@ -94,19 +102,26 @@ export const PROVIDER_HTTP_RESPONSE_TIMEOUT_MS = resolveTimeoutMs(
94
102
  { minMs: 10_000, maxMs: PROVIDER_MAX_BEFORE_WARN_MS },
95
103
  );
96
104
 
97
- // Stream idle watchdog is OFF by default matches Claude Code native
98
- // behaviour (its watchdog is gated behind CLAUDE_ENABLE_STREAM_WATCHDOG).
99
- // The bridge stall watchdog (STALL_ABORT_S, 600s) is the backstop for
100
- // genuinely dead streams; this short inter-chunk idle watchdog only adds
101
- // value when explicitly enabled, and otherwise prematurely kills slow
102
- // high-reasoning streams. Enable with MIXDOG_ENABLE_STREAM_WATCHDOG=1.
105
+ // Stream idle watchdog is ON by default. Earlier it was OFF (reference-agent
106
+ // parity) on the theory that a short inter-chunk timer would false-abort slow
107
+ // high-reasoning streams but every provider's SSE loop resets the idle timer
108
+ // on EVERY chunk, including content-free keepalives (Anthropic `:ping`,
109
+ // OpenAI/compat reasoning_content deltas, Gemini chunks). So a live
110
+ // extended-thinking stream keeps the timer fresh; only a genuinely silent
111
+ // socket (no bytes at all for the whole window) trips it. With the watchdog
112
+ // OFF, such a wedged socket sat until the 600s agent stall backstop, which the
113
+ // trace logs showed as the dominant mid-stream hang (sse_parse_ms p99 ~183s,
114
+ // max ~411s). Defaulting ON with a generous 120s window cuts that tail ~5x
115
+ // while leaving genuine reasoning pauses (kept alive by keepalive frames)
116
+ // untouched. Force OFF with MIXDOG_ENABLE_STREAM_WATCHDOG=0.
103
117
  const _sseWatchdogRaw = process.env.MIXDOG_ENABLE_STREAM_WATCHDOG;
104
- export const PROVIDER_SSE_IDLE_WATCHDOG_ENABLED =
105
- _sseWatchdogRaw === '1' || _sseWatchdogRaw === 'true' || _sseWatchdogRaw === 'yes';
118
+ export const PROVIDER_SSE_IDLE_WATCHDOG_ENABLED = _sseWatchdogRaw === undefined || _sseWatchdogRaw === ''
119
+ ? true
120
+ : (_sseWatchdogRaw === '1' || _sseWatchdogRaw === 'true' || _sseWatchdogRaw === 'yes');
106
121
 
107
122
  export const PROVIDER_SSE_IDLE_TIMEOUT_MS = resolveTimeoutMs(
108
123
  'MIXDOG_PROVIDER_SSE_IDLE_TIMEOUT_MS',
109
- 90_000,
124
+ 120_000,
110
125
  { minMs: 10_000, maxMs: STALL_WARN_MS },
111
126
  );
112
127
 
@@ -143,15 +158,15 @@ export const PROVIDER_RETRY_JITTER_RATIO = (() => {
143
158
  return 0.2;
144
159
  })();
145
160
 
146
- export function resolveBridgeStallThresholds(role, env = process.env) {
161
+ export function resolveAgentStallThresholds(role, env = process.env) {
147
162
  const cfg = role ? getHiddenRole(role) : null;
148
163
  const cfgAbort = cfg?.stallCap?.idleSeconds > 0 ? cfg.stallCap.idleSeconds : STALL_ABORT_S;
149
164
  const envOverride = envThresholdSeconds(env);
150
165
  const abort = envOverride != null ? envOverride : cfgAbort;
151
- // Mid-stream "slow" warning disabled — a bridge stall now notifies ONLY at
166
+ // Mid-stream "slow" warning disabled — an agent stall now notifies ONLY at
152
167
  // the abort deadline (10 min default). warn === abort means the watchdog's
153
168
  // warn branch (verdict 'ok' && stale >= warn) can never fire before 'stall',
154
- // so the only notification a stalled bridge worker emits is at the deadline.
169
+ // so the only notification a stalled agent worker emits is at the deadline.
155
170
  const warn = abort;
156
171
  // First-byte deadline: a request still in 'requesting' that never produced a
157
172
  // single SSE delta is hung, not slow-reasoning. Abort on this shorter deadline
@@ -160,7 +175,7 @@ export function resolveBridgeStallThresholds(role, env = process.env) {
160
175
  return { warn, abort, firstByteAbort };
161
176
  }
162
177
 
163
- export function resolveBridgeToolThresholdSeconds(role, thresholdSeconds) {
178
+ export function resolveAgentToolThresholdSeconds(role, thresholdSeconds) {
164
179
  const cfg = role ? getHiddenRole(role) : null;
165
180
  if (cfg?.stallCap?.toolRunningSeconds > 0) return cfg.stallCap.toolRunningSeconds;
166
181
  return thresholdSeconds;
@@ -206,7 +221,7 @@ export function createTimeoutSignal(parentSignal, timeoutMs, label) {
206
221
  // replaced-by-newer-request). Used for the streaming generation phase, where a fixed
207
222
  // total-lifetime cap would false-abort a stream that is still emitting SSE deltas; the
208
223
  // streaming phase is bounded instead by the per-attempt first-byte timeout, the parent
209
- // signal, and the bridge stall watchdog (STALL_ABORT_S, progress-based). cleanup()
224
+ // signal, and the agent stall watchdog (STALL_ABORT_S, progress-based). cleanup()
210
225
  // detaches the parent listener so no listener leaks on the (long-lived) parent signal.
211
226
  export function createPassthroughSignal(parentSignal) {
212
227
  if (!parentSignal) return { signal: null, cleanup: () => {} };
@@ -1,229 +1,13 @@
1
1
  /**
2
- * Shell file-lookup classifier + soft-warn marker stripper.
2
+ * Soft-warn marker stripper.
3
3
  *
4
4
  * The tool-loop guard subsystem (repeated/fanned-out tool-use detection and its
5
- * soft-warn sidecars) was removed once every warn path had been disabled. Two
6
- * standalone utilities remain:
7
- * • classifyBashFileLookupCommand — detects a `bash` call whose first token is
8
- * a file-lookup that has a dedicated in-process tool (read/grep/glob/list).
9
- * Used by the bridge-worker permission gate to block the shell route.
10
- * • classifyBridgeWorkerGitMutationCommand — detects bridge-worker shell
11
- * calls that try to run git operations reserved for Lead.
5
+ * soft-warn sidecars) was removed once every warn path had been disabled. This
6
+ * module now retains a single utility:
12
7
  * • stripSoftWarns — strips legacy soft-warn marker blocks from outbound
13
8
  * bodies so older transcripts that still carry them stay clean.
14
9
  */
15
10
 
16
- // File-lookup-via-shell detector. Matches a `bash` command whose first token
17
- // is a unix find/grep/cat/ls/head/tail OR a powershell file-discovery cmdlet
18
- // (Get-ChildItem / Select-String / Get-Content). These have dedicated tools
19
- // (glob / grep / read / list) per rules/shared/01-tool.md; using bash routes
20
- // around the in-process tool cache and the path-permission check, and the
21
- // outputs were observed in PG telemetry running 100x slower than the dedicated
22
- // path. Get-CimInstance / Win32_Process etc. are NOT matched — process
23
- // enumeration genuinely needs the shell. One advisory per session per matched
24
- // command class; subsequent matches stay silent so the model isn't drowned.
25
- const SHELL_FILE_LOOKUP_COMMANDS = [
26
- 'find', 'ls', 'grep', 'rg', 'fd', 'cat', 'head', 'tail',
27
- 'sed', 'awk', 'less', 'more', 'wc', 'tree',
28
- // File-metadata mirror paths called out in rules/shared/01-tool.md
29
- // Tool Routing section. Added after probe HS-A3 surfaced that the
30
- // rule explicitly lists `stat`, `du`, `file`, and `ls -l` as
31
- // forbidden mirror paths but the policy under-enforced them.
32
- 'stat', 'du', 'file',
33
- 'dir', 'type', 'findstr',
34
- 'Get-ChildItem', 'gci', 'Select-String', 'sls', 'Get-Content', 'gc',
35
- ].join('|');
36
- const BASH_FILE_LOOKUP_RE = new RegExp(
37
- `^\\s*(?:git\\s+(?:grep|ls-files)\\b|(?:${SHELL_FILE_LOOKUP_COMMANDS})\\b)`,
38
- 'i',
39
- );
40
- // Wrapper / eval forms that route the same file-lookup intent through a host
41
- // shell (`bash -c "grep ..."`, `pwsh -Command Get-Content ...`) or an
42
- // inline interpreter eval (`node -e fs.readFileSync(...)`,
43
- // `python -c "open(...)"`). Without these the advisory was bypassable just
44
- // by quoting the call inside another command.
45
- const BASH_WRAPPER_FILE_LOOKUP_RE = new RegExp(
46
- `^\\s*(?:bash|sh|zsh|fish|dash)\\b[^\\n]*\\s-\\w*c\\w*\\s+["']?\\s*(?:${SHELL_FILE_LOOKUP_COMMANDS})\\b`,
47
- 'i',
48
- );
49
- const CMD_WRAPPER_FILE_LOOKUP_RE = /^\s*cmd(?:\.exe)?\s+\/[cs]\s+["']?\s*(?:dir|type|findstr)\b/i;
50
- const PWSH_WRAPPER_FILE_LOOKUP_RE = /^\s*(?:pwsh|powershell)(?:\.exe)?\b[\s\S]*?(?:-|\/)(?:Command|c)\s+["']?\s*(?:Get-ChildItem|gci|dir|ls|Select-String|sls|Get-Content|gc|cat|type)\b/i;
51
- const NODE_FS_EVAL_RE = /^\s*node(?:\.exe)?\b[\s\S]*?\s-e\s+["'](?![\s\S]*\bJSON\.parse\s*\()[\s\S]*?\b(?:fs\.|readFileSync|readFile|readdirSync|readdir|statSync|stat)\b/i;
52
- const PYTHON_OPEN_EVAL_RE = /^\s*python3?(?:\.exe)?\b[\s\S]*?\s-c\s+["'][\s\S]*?\b(?:open\s*\(|Path\s*\(|read_text\s*\(|read_bytes\s*\(|iterdir\s*\()/i;
53
- function _classifyBashFileLookup(command) {
54
- if (typeof command !== 'string' || !command) return null;
55
- const direct = command.match(BASH_FILE_LOOKUP_RE);
56
- if (direct) return direct[0].trim().toLowerCase();
57
- if (BASH_WRAPPER_FILE_LOOKUP_RE.test(command)) return 'bash-wrapped';
58
- if (CMD_WRAPPER_FILE_LOOKUP_RE.test(command)) return 'cmd-wrapped';
59
- if (PWSH_WRAPPER_FILE_LOOKUP_RE.test(command)) return 'pwsh-wrapped';
60
- if (NODE_FS_EVAL_RE.test(command)) return 'node-fs-eval';
61
- if (PYTHON_OPEN_EVAL_RE.test(command)) return 'python-open-eval';
62
- return null;
63
- }
64
-
65
- export function classifyBashFileLookupCommand(command) {
66
- return _classifyBashFileLookup(command);
67
- }
68
-
69
- const SHELL_WRAPPER_COMMANDS = new Set([
70
- 'bash', 'sh', 'zsh', 'fish', 'dash',
71
- 'cmd', 'cmd.exe',
72
- 'pwsh', 'pwsh.exe', 'powershell', 'powershell.exe',
73
- ]);
74
- const POSIX_SHELL_WRAPPER_COMMANDS = new Set(['bash', 'sh', 'zsh', 'fish', 'dash']);
75
- const CMD_SHELL_WRAPPER_COMMANDS = new Set(['cmd', 'cmd.exe']);
76
- const POWERSHELL_WRAPPER_COMMANDS = new Set(['pwsh', 'pwsh.exe', 'powershell', 'powershell.exe']);
77
- const SHELL_SEPARATORS = new Set([';', '\n', '&&', '||', '|', '&']);
78
- const READONLY_GIT_SUBCOMMANDS = new Set([
79
- 'status', 'diff', 'show', 'log', 'rev-parse', 'ls-files', 'grep', 'blame', 'describe', 'merge-base',
80
- ]);
81
-
82
- function shellTokenizeLoose(command) {
83
- const tokens = [];
84
- let current = '';
85
- let quote = null;
86
- let escape = false;
87
- const push = () => {
88
- if (current !== '') tokens.push(current);
89
- current = '';
90
- };
91
- const pushSep = (sep) => {
92
- push();
93
- tokens.push(sep);
94
- };
95
- for (let i = 0; i < command.length; i += 1) {
96
- const ch = command[i];
97
- if (escape) {
98
- current += ch;
99
- escape = false;
100
- continue;
101
- }
102
- if (ch === '\\') {
103
- escape = true;
104
- continue;
105
- }
106
- if (quote) {
107
- if (ch === quote) quote = null;
108
- else current += ch;
109
- continue;
110
- }
111
- if (ch === '\'' || ch === '"') {
112
- quote = ch;
113
- continue;
114
- }
115
- if (ch === '\n') {
116
- pushSep('\n');
117
- continue;
118
- }
119
- if (ch === ';') {
120
- pushSep(';');
121
- continue;
122
- }
123
- if ((ch === '&' || ch === '|') && command[i + 1] === ch) {
124
- pushSep(`${ch}${ch}`);
125
- i += 1;
126
- continue;
127
- }
128
- if (ch === '|' || ch === '&') {
129
- pushSep(ch);
130
- continue;
131
- }
132
- if (/\s/.test(ch)) {
133
- push();
134
- continue;
135
- }
136
- current += ch;
137
- }
138
- push();
139
- return tokens;
140
- }
141
-
142
- function isCommandPosition(tokens, index) {
143
- for (let i = index - 1; i >= 0; i -= 1) {
144
- if (!tokens[i]) continue;
145
- return SHELL_SEPARATORS.has(tokens[i]);
146
- }
147
- return true;
148
- }
149
-
150
- function skipGitGlobalOptions(tokens, index) {
151
- let i = index;
152
- while (i < tokens.length) {
153
- const token = String(tokens[i] || '');
154
- const lower = token.toLowerCase();
155
- if (!token || SHELL_SEPARATORS.has(token)) return i;
156
- if (lower === '-c' || lower === '-C' || lower === '--git-dir' || lower === '--work-tree' || lower === '--namespace') {
157
- i += 2;
158
- continue;
159
- }
160
- if (/^(?:--git-dir|--work-tree|--namespace)=/i.test(token)) {
161
- i += 1;
162
- continue;
163
- }
164
- if (lower === '--no-pager' || lower === '-p' || lower === '--literal-pathspecs' || lower === '--glob-pathspecs' || lower === '--noglob-pathspecs' || lower === '--icase-pathspecs') {
165
- i += 1;
166
- continue;
167
- }
168
- if (/^-/.test(token)) {
169
- i += 1;
170
- continue;
171
- }
172
- return i;
173
- }
174
- return i;
175
- }
176
-
177
- function readonlyGitInvocation(tokens, subIndex) {
178
- const sub = String(tokens[subIndex] || '').toLowerCase();
179
- if (!READONLY_GIT_SUBCOMMANDS.has(sub)) return false;
180
- if (sub === 'remote') {
181
- const next = String(tokens[subIndex + 1] || '').toLowerCase();
182
- return !next || next === '-v' || next === '--verbose' || next === 'show';
183
- }
184
- if (sub === 'branch') {
185
- const rest = tokens.slice(subIndex + 1).filter((token) => token && !SHELL_SEPARATORS.has(token));
186
- return rest.length === 0 || rest.every((token) => ['--show-current', '-a', '-r', '-v', '-vv', '--all', '--remotes', '--verbose', '--contains', '--merged', '--no-merged'].includes(String(token).toLowerCase()));
187
- }
188
- return true;
189
- }
190
-
191
- function isShellCommandArgFlag(wrapper, flag) {
192
- const lower = String(flag || '').toLowerCase();
193
- if (POSIX_SHELL_WRAPPER_COMMANDS.has(wrapper)) return /^-\w*c\w*$/.test(lower);
194
- if (CMD_SHELL_WRAPPER_COMMANDS.has(wrapper)) return lower === '/c';
195
- if (POWERSHELL_WRAPPER_COMMANDS.has(wrapper)) return lower === '-command' || lower === '/command' || lower === '-c';
196
- return false;
197
- }
198
-
199
- export function classifyBridgeWorkerGitMutationCommand(command) {
200
- if (typeof command !== 'string' || !command.trim()) return null;
201
- const tokens = shellTokenizeLoose(command);
202
- for (let i = 0; i < tokens.length; i += 1) {
203
- const token = String(tokens[i] || '').toLowerCase();
204
- if (!token || SHELL_SEPARATORS.has(token) || !isCommandPosition(tokens, i)) continue;
205
- if (SHELL_WRAPPER_COMMANDS.has(token)) {
206
- for (let j = i + 1; j < tokens.length; j += 1) {
207
- const flag = String(tokens[j] || '').toLowerCase();
208
- if (SHELL_SEPARATORS.has(flag)) break;
209
- if (!isShellCommandArgFlag(token, flag)) continue;
210
- const nested = tokens.slice(j + 1).join(' ');
211
- const nestedHit = classifyBridgeWorkerGitMutationCommand(nested || '');
212
- if (nestedHit) return nestedHit;
213
- break;
214
- }
215
- continue;
216
- }
217
- if (token !== 'git' && token !== 'git.exe') continue;
218
- const subIndex = skipGitGlobalOptions(tokens, i + 1);
219
- const sub = String(tokens[subIndex] || '').toLowerCase();
220
- if (!sub || SHELL_SEPARATORS.has(sub)) return 'git';
221
- if (readonlyGitInvocation(tokens, subIndex)) continue;
222
- return `git ${sub}`;
223
- }
224
- return null;
225
- }
226
-
227
11
  // Strip soft-warn marker blocks (header ⚠ <label> through next blank line / EOF)
228
12
  // from outbound bodies. Never call on tool-result bodies fed back to the model.
229
13
  // New compact format: each warn is a single line starting with `⚠ <Label>(` and ending at newline.
@@ -41,7 +41,7 @@ import * as nodeUtil from 'node:util';
41
41
  import { existsSync } from 'node:fs';
42
42
  import { randomUUID } from 'node:crypto';
43
43
  import { invalidateBuiltinResultCache, analyzeShellCommandEffects, preflightShellLargeFileProbe } from './builtin.mjs';
44
- import { markCodeGraphDirtyPaths, drainCodeGraphCache } from './code-graph.mjs';
44
+ import { markCodeGraphDirtyPaths, drainCodeGraphCache } from './code-graph-state.mjs';
45
45
  import { maybeRewriteWmicProcessCommand } from './shell-policy.mjs';
46
46
  import { _maybeEncodePowerShellCommand } from './shell-command.mjs';
47
47
  import { _captureTrackedMtimes, _trackedDriftNoteAfter, _injectionBlockTargets, getDedupedDestructiveWarnings } from './builtin/bash-tool.mjs';
@@ -49,6 +49,8 @@ import { scrubLoaderVars, scrubProviderSecrets } from './env-scrub.mjs';
49
49
  import { checkExecPolicyMessage } from './bash-policy-scan.mjs';
50
50
  import { startChildGuardian } from '../../../shared/child-guardian.mjs';
51
51
 
52
+ globalThis.__mixdogBashSessionRuntimeLoaded = true;
53
+
52
54
  // Default 600 s (10 min), max 1800 s. Aligned with the one-shot bash tool's
53
55
  // 600 s default (builtin/bash-tool.mjs); the persistent shell carries
54
56
  // longer-running pipelines (build/test/install) than one-shot calls, so a
@@ -171,15 +173,18 @@ function _startReaper() {
171
173
  _killSession(id, 'idle-timeout');
172
174
  }
173
175
  }
174
- if (_sessions.size === 0) {
175
- clearInterval(_reaperTimer);
176
- _reaperTimer = null;
177
- }
176
+ _clearReaperIfIdle();
178
177
  }, 30_000);
179
178
  // Don't keep the event loop alive just for the reaper.
180
179
  if (typeof _reaperTimer.unref === 'function') _reaperTimer.unref();
181
180
  }
182
181
 
182
+ function _clearReaperIfIdle() {
183
+ if (_sessions.size !== 0 || !_reaperTimer) return;
184
+ clearInterval(_reaperTimer);
185
+ _reaperTimer = null;
186
+ }
187
+
183
188
  // Kill a spawned shell along with any child processes it forked. Posix path
184
189
  // signals the process group (pgid == pid because we spawn with detached:true),
185
190
  // so `sleep 1000 &` or a node server started inside the session is reaped
@@ -239,6 +244,17 @@ async function _killSession(id, _reason) {
239
244
  try { s.proc.stdin?.end(); } catch { /* ignore */ }
240
245
  _killProcessTree(s.proc);
241
246
  await Promise.race([exited, new Promise((r) => setTimeout(r, 3000))]);
247
+ _clearReaperIfIdle();
248
+ }
249
+
250
+ function _killSessionNow(id, _reason) {
251
+ const s = _sessions.get(id);
252
+ if (!s) return false;
253
+ _sessions.delete(id);
254
+ try { s.proc.stdin?.end(); } catch { /* ignore */ }
255
+ _killProcessTree(s.proc);
256
+ _clearReaperIfIdle();
257
+ return true;
242
258
  }
243
259
 
244
260
  function shellQuoteSingle(s) {
@@ -713,10 +729,21 @@ export function closeBashSession(sessionId, reason = 'external-close') {
713
729
  return true;
714
730
  }
715
731
 
732
+ export async function shutdownBashSessions(reason = 'runtime-close') {
733
+ const ids = [..._sessions.keys()];
734
+ await Promise.allSettled(ids.map((id) => _killSession(id, reason)));
735
+ _clearReaperIfIdle();
736
+ return { closed: ids.length };
737
+ }
738
+
716
739
  // Best-effort cleanup on process exit so orphan bash children don't linger
717
740
  // when the plugin host shuts down. Self-registered exit drain; bare 'exit' hook stays as idempotent backup.
718
- export function drainBashSessions() {
719
- for (const id of [..._sessions.keys()]) _killSession(id, 'process-exit');
741
+ export function drainBashSessions(reason = 'process-exit') {
742
+ let closed = 0;
743
+ for (const id of [..._sessions.keys()]) {
744
+ if (_killSessionNow(id, reason)) closed += 1;
745
+ }
746
+ return { closed };
720
747
  }
721
748
  if (typeof process?.on === 'function') {
722
749
  process.on('exit', drainBashSessions);
@@ -1,4 +1,4 @@
1
- // Cross-process advisory lock for write/edit operations. Uses a sibling
1
+ // Cross-process advisory lock for patch/write operations. Uses a sibling
2
2
  // lockfile written exclusively (`wx`) and a process.kill(pid, 0) liveness
3
3
  // check to clean up after crashed holders. Pair with the in-process
4
4
  // withPathLock for the same target: in-process serialises async callers
@@ -311,6 +311,24 @@ function guardList(a) {
311
311
  return null;
312
312
  }
313
313
 
314
+ function guardFind(a) {
315
+ if (!hasOwn(a, 'query') || typeof a.query !== 'string' || a.query.trim().length === 0) {
316
+ return `Error: find requires non-empty string "query" (got ${describeType(a.query)})`;
317
+ }
318
+ if (hasOwn(a, 'path') && !isString(a.path)) {
319
+ return `Error: find arg "path" must be a string (got ${describeType(a.path)})`;
320
+ }
321
+ if (hasOwn(a, 'head_limit') && a.head_limit !== undefined && a.head_limit !== null) {
322
+ if (!isFiniteInt(a.head_limit)) {
323
+ return `Error: find arg "head_limit" must be a finite integer (got ${describeType(a.head_limit)})`;
324
+ }
325
+ if (a.head_limit < 0) {
326
+ return `Error: find arg "head_limit" must be >= 0 (0 means no cap); got ${a.head_limit}`;
327
+ }
328
+ }
329
+ return null;
330
+ }
331
+
314
332
  function guardGlob(a) {
315
333
  // path alias root; pattern aliases glob/name/file_pattern
316
334
  for (const k of ['path', 'root']) {
@@ -365,6 +383,7 @@ const GUARDS = {
365
383
  shell: guardShell,
366
384
  task: guardTask,
367
385
  list: guardList,
386
+ find: guardFind,
368
387
  glob: guardGlob,
369
388
  code_graph: guardCodeGraph,
370
389
  };
@@ -4,7 +4,7 @@ import { wrapCommandWithSnapshot } from '../shell-snapshot.mjs';
4
4
  import { getDestructiveCommandWarning } from '../destructive-warning.mjs';
5
5
  import { maybeRewriteWmicProcessCommand } from '../shell-policy.mjs';
6
6
  import { buildBashPolicyScanTargets, checkExecPolicyMessage, injectionBlockTargets } from '../bash-policy-scan.mjs';
7
- import { markCodeGraphDirtyPaths, drainCodeGraphCache } from '../code-graph.mjs';
7
+ import { markCodeGraphDirtyPaths, drainCodeGraphCache } from '../code-graph-state.mjs';
8
8
  import {
9
9
  buildJobNotFoundMessage,
10
10
  startBackgroundShellJob,
@@ -60,7 +60,7 @@ export function _trackedDriftNoteAfter(_scope, _pre) {
60
60
  // AbortSignal (e.g. the MCP/request signal threaded through options.abortSignal).
61
61
  // Returns null when neither is present so existing session-only behavior is
62
62
  // preserved unchanged. Uses AbortSignal.any when available; falls back to a
63
- // manual controller + listener bridge otherwise. The returned signal aborts as
63
+ // manual controller + listener path otherwise. The returned signal aborts as
64
64
  // soon as either input signal aborts, which propagates to execShellCommand /
65
65
  // executeBashSessionTool and triggers the same child-kill path the session
66
66
  // signal already drives.
@@ -343,7 +343,7 @@ export async function executeBashTool(args, workDir, options = {}) {
343
343
  // the background task finished (no polling tool is auto-driven). The
344
344
  // notify ctx is threaded down from the MCP dispatch frame
345
345
  // (server-main agentContext / _dispatchByModule) the same way the
346
- // bridge/explore-style tools receive notifyFn/routingSessionId/clientHostPid.
346
+ // agent/explore-style tools receive notifyFn/routingSessionId/clientHostPid.
347
347
  // Missing notifyFn (e.g. a non-MCP caller) degrades to a stderr
348
348
  // diagnostic inside watchBackgroundShellJob — never fails the spawn.
349
349
  try {
@@ -548,12 +548,12 @@ export async function executeTaskTool(args, options = {}) {
548
548
 
549
549
  const taskId = typeof args.task_id === 'string' ? args.task_id.trim() : '';
550
550
  if (!taskId) return 'Error: task_id is required';
551
- // bridge_* / sess_* are bridge-worker / orchestrator session ids, not
552
- // background shell jobs. task only resolves `shell mode=async`
553
- // jobs, so surface a self-correcting hint instead of the bare
554
- // "job not found" that otherwise invites a wrong-tool retry loop.
555
- if (/^(?:bridge_|sess_)/.test(taskId)) {
556
- return `Error: "${taskId}" is a bridge/session id, not a background task_id. Bridge agents deliver completion notifications; use bridge list/read only for manual recovery.`;
551
+ // sess_* values are agent/orchestrator session ids, not background shell
552
+ // tasks. task only resolves `shell mode=async` tasks, so surface a
553
+ // self-correcting hint instead of the bare "task not found" that otherwise
554
+ // invites a wrong-tool retry loop.
555
+ if (/^sess_/.test(taskId)) {
556
+ return `Error: "${taskId}" is an agent/session id, not a background task_id. Agent tasks deliver completion notifications; use agent list/read only for manual recovery.`;
557
557
  }
558
558
 
559
559
  const task = getBackgroundTask(taskId, { context: options });