mixdog 1.2.5 → 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/package.json +1 -1
  2. package/scripts/lib/gate-scheduler.mjs +2 -2
  3. package/scripts/lib/run-node-tests.mjs +5 -14
  4. package/scripts/provider-pingpong-live.mjs +9 -9
  5. package/src/agents/advisor/AGENT.md +4 -4
  6. package/src/agents/advisor/agent.json +1 -1
  7. package/src/agents/reviewer/AGENT.md +2 -3
  8. package/src/agents/reviewer/agent.json +1 -1
  9. package/src/defaults/shipped-history.json +10 -0
  10. package/src/defaults/skills/browser-use/SKILL.md +2 -1
  11. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  12. package/src/defaults/skills/visualize/SKILL.md +3 -2
  13. package/src/lib/rules-builder.cjs +1 -1
  14. package/src/output-styles/common.md +22 -6
  15. package/src/output-styles/detailed.md +7 -5
  16. package/src/output-styles/minimal.md +7 -3
  17. package/src/output-styles/simple.md +2 -3
  18. package/src/rules/agent/AGENT.md +3 -1
  19. package/src/rules/lead/LEAD.md +4 -3
  20. package/src/rules/shared/10-tool-workflow.md +2 -1
  21. package/src/rules/shared/60-verification.md +4 -5
  22. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  23. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  24. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  26. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +53 -60
  27. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  28. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  29. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +40 -19
  31. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  32. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  33. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  34. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  35. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  37. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +1 -2
  38. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  39. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  40. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  41. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  42. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  43. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  44. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  45. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  46. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  47. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  49. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  50. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  51. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  53. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  56. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  57. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  58. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  61. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  64. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  65. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  66. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  67. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  68. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  69. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  70. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +2 -3
  71. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +3 -8
  72. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  73. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  74. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  75. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  76. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  77. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  78. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  79. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +9 -19
  80. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -3
  81. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  83. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  84. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  85. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  86. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  87. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  88. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  89. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  90. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  91. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  92. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  93. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  94. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  95. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  96. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  97. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  98. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +306 -0
  99. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +70 -21
  100. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  101. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  102. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  103. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  104. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  105. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  106. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  109. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  111. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  112. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  113. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  115. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  117. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  119. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  120. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  121. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  122. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  123. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  124. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  125. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  126. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  128. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  129. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  130. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  132. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  133. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  134. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  135. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  136. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  137. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +119 -32
  138. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  139. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  140. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  141. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  142. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  143. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  144. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  145. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  146. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  147. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  148. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +40 -2
  149. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -2
  150. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  151. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -1
  153. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +35 -4
  155. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  156. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  157. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  158. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  159. package/src/runtime/attachments/office-extract.mjs +11 -9
  160. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  161. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  162. package/src/runtime/memory/lib/tool-call-handler.mjs +7 -5
  163. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  164. package/src/runtime/office/core/tabular.mjs +1 -1
  165. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  166. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  167. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  168. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  169. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  170. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  171. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  172. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  173. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  174. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  175. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  176. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  177. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  178. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  179. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  180. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  181. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  182. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  183. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  184. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  185. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  186. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  187. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  188. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  189. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  190. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  191. package/src/runtime/shared/atomic-file.mjs +22 -13
  192. package/src/runtime/shared/background-tasks.mjs +60 -20
  193. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  194. package/src/runtime/shared/config.mjs +0 -10
  195. package/src/runtime/shared/file-lock.mjs +7 -1
  196. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  197. package/src/runtime/shared/file-permissions.d.mts +19 -4
  198. package/src/runtime/shared/file-permissions.mjs +310 -4
  199. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  200. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  201. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  202. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  203. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  204. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  205. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  206. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  207. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  208. package/src/runtime/shared/notify-trace.mjs +8 -1
  209. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  210. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  211. package/src/runtime/shared/process-runner.mjs +38 -0
  212. package/src/runtime/shared/provider-accounts.mjs +2 -1
  213. package/src/runtime/shared/provider-api-key.mjs +0 -1
  214. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  215. package/src/runtime/shared/resource-admission.mjs +60 -18
  216. package/src/runtime/shared/run-child.mjs +84 -0
  217. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  218. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  219. package/src/runtime/shared/singleton-owner.mjs +126 -42
  220. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  221. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  222. package/src/runtime/shared/user-data-guard.mjs +28 -9
  223. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  224. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  225. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  226. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  227. package/src/runtime/tidy/runners/biome.mjs +3 -1
  228. package/src/runtime/tidy/shadow.mjs +2 -1
  229. package/src/runtime/web-search/index.mjs +5 -7
  230. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  231. package/src/runtime/web-search/tool-defs.mjs +6 -1
  232. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  233. package/src/session-runtime/lifecycle/session-switch/resume.mjs +7 -4
  234. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  235. package/src/session-runtime/lifecycle/shared.mjs +19 -2
  236. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  237. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  238. package/src/session-runtime/lifecycle-api.mjs +3 -1
  239. package/src/session-runtime/provider-auth/login.mjs +10 -6
  240. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  241. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  242. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  243. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  244. package/src/session-runtime/provider-models.mjs +76 -10
  245. package/src/session-runtime/provider-readiness.mjs +4 -0
  246. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  247. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  248. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  249. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  250. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  251. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  252. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  253. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  254. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  255. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  256. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  257. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  258. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  259. package/src/session-runtime/services/agent-tool.mjs +20 -0
  260. package/src/session-runtime/services/provider-admin.mjs +92 -20
  261. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  262. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  263. package/src/session-runtime/turn/session-ops.mjs +2 -2
  264. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  265. package/src/session-runtime/usage-stats-api.mjs +6 -1
  266. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  267. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  268. package/src/standalone/daemon-owner.mjs +117 -25
  269. package/src/standalone/daemon.mjs +43 -13
  270. package/src/standalone/session-call-cache.mjs +6 -0
  271. package/src/standalone/session-protocol.mjs +1 -1
  272. package/src/standalone/session-service/retention/busy.mjs +7 -2
  273. package/src/standalone/session-transport/routes.mjs +7 -6
  274. package/src/tui/app/model-options.mjs +39 -2
  275. package/src/tui/app/model-picker.mjs +16 -2
  276. package/src/tui/app/provider-setup-picker.mjs +1 -2
  277. package/src/tui/dist/index.mjs +385 -104
  278. package/src/tui/session/notice-text.mjs +0 -1
  279. package/src/tui/session/session-api/intake/submission.mjs +7 -13
  280. package/src/tui/session/session-api/integrations.mjs +1 -5
  281. package/src/tui/session/session-flow/drain.mjs +8 -0
  282. package/src/tui/session/session-flow/steering.mjs +46 -0
  283. package/src/tui/session/session-flow.mjs +2 -0
  284. package/src/tui/session/turn-stream.mjs +33 -0
  285. package/src/tui/session/turn.mjs +30 -3
  286. package/src/ui/statusline.mjs +1 -6
  287. package/src/ui/streaming-markdown-heal.mjs +24 -7
  288. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  289. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  290. package/src/workflows/default/WORKFLOW.md +9 -13
@@ -7,7 +7,7 @@ import { cleanupArtifactOnTaskSettled, consumeTeeArtifact } from './transport-ar
7
7
  import { _backgroundResultLines, _prependDestructiveWarning } from './result-format.mjs';
8
8
 
9
9
  const DEFAULT_BACKGROUND_MESSAGE =
10
- 'auto-backgrounded; still running — judge from the partial output whether waiting can finish in budget, or diagnose and pursue an alternative.';
10
+ 'moved to background; still running. Its result is delivered automatically when it finishes.';
11
11
 
12
12
  // The label and owner context every task registered for a shell command
13
13
  // carries, whether promoted or tracking survivors.
@@ -73,13 +73,19 @@ export function renderBackgroundedResult({ result, command, cwd, options, starte
73
73
  let taskBlock = null;
74
74
  if (task) taskBlock = renderBackgroundTask(task, { ownerTool: true });
75
75
  else if (result.jobId) taskBlock = `[task_id: ${result.jobId}]`;
76
+ // The output so far in the shared delivery shape, with the progress digest;
77
+ // the task cursor then starts after it, so nothing here is sent again.
76
78
  const partialOutput = task
77
- ? readShellTaskOutput(task, {
78
- stdout_path: result.stdoutPath,
79
- stderr_path: result.stderrPath,
80
- stdout_preview: stdout,
81
- stderr_preview: stderr,
82
- })
79
+ ? readShellTaskOutput(
80
+ task,
81
+ {
82
+ stdout_path: result.stdoutPath,
83
+ stderr_path: result.stderrPath,
84
+ stdout_preview: stdout,
85
+ stderr_preview: stderr,
86
+ },
87
+ { digest: true }
88
+ )
83
89
  : renderBackgroundPartialOutput(stdout, stderr);
84
90
  const lines = _backgroundResultLines({
85
91
  taskBlock,
@@ -23,7 +23,7 @@ const HEREDOC_OPENER = /^<<-?[ \t]*(?:'([A-Za-z_]\w*)'|"([A-Za-z_]\w*)"|([A-Za-z
23
23
  /** Marks each character 1 when it is live PowerShell code and 0 when it sits in a
24
24
  * string, here-string, comment or after a backtick. null when the text cannot be
25
25
  * tokenised with confidence (unterminated string, `$(` inside a double quote). */
26
- function codeMask(text) {
26
+ export function codeMask(text) {
27
27
  const mask = new Uint8Array(text.length);
28
28
  // Bash heredoc bodies start on the line after their `<<DELIM` opener and are
29
29
  // opaque text (quotes inside them must not be tokenised).
@@ -45,7 +45,13 @@ function _stripShellComment(text) {
45
45
  out += ch;
46
46
  continue;
47
47
  }
48
- if (ch === '#' && (i === 0 || /\s/.test(text[i - 1]))) break;
48
+ if (ch === '#' && (i === 0 || /[\s;&|(]/.test(text[i - 1]))) {
49
+ // A comment ends at the newline; later lines are still live commands.
50
+ const nl = text.indexOf('\n', i);
51
+ if (nl < 0) break;
52
+ i = nl - 1;
53
+ continue;
54
+ }
49
55
  out += ch;
50
56
  }
51
57
  return out;
@@ -4,6 +4,8 @@ import { releaseShellJobWorkspace } from '../lib/shell-job-records.mjs';
4
4
  import { consumeFilterTeeCapture } from '../shell-analysis.mjs';
5
5
  import { compactShellOutputLosslessly, renderShellOutputBody } from '../shell-lossless-compact.mjs';
6
6
  import { removeTransportFileNow } from './transport-artifacts.mjs';
7
+ import { DELIVERY_FULL_CHARS, EMPTY_PIECE, fitPieces, shellTextPiece } from '../lib/shell-delivery.mjs';
8
+ import { shellLogPiece } from '../lib/shell-task-output.mjs';
7
9
  import { shellTaskIdentity } from './backgrounded-result.mjs';
8
10
  import { _isBenignSearchExitOne } from './benign-exit.mjs';
9
11
  import {
@@ -24,7 +26,7 @@ import {
24
26
  function trackSurvivingDescendants(handle, { command, cwd, options, startedAtMs } = {}) {
25
27
  if (!handle?.taskId) return null;
26
28
  try {
27
- return startBackgroundTask({
29
+ const task = startBackgroundTask({
28
30
  taskId: handle.taskId,
29
31
  startedAtMs: startedAtMs || Date.now(),
30
32
  surface: 'shell',
@@ -44,10 +46,15 @@ function trackSurvivingDescendants(handle, { command, cwd, options, startedAtMs
44
46
  detail: 'every process the command left running has exited',
45
47
  };
46
48
  },
49
+ // The kill's own re-observation is what `task cancel` reports.
47
50
  cancel: () => {
48
- void killShellDescendants(handle);
51
+ task.cancelOutcome = killShellDescendants(handle);
49
52
  },
50
53
  });
54
+ // The command already returned its result; these processes are a side
55
+ // effect it left running, not work the agent is waiting on.
56
+ task.detached = true;
57
+ return task;
51
58
  } catch {
52
59
  return null;
53
60
  }
@@ -110,6 +117,20 @@ function classifyCompletion(result, timeout, analysisCommand) {
110
117
  };
111
118
  }
112
119
 
120
+ // A spilled capture is rendered from its log (the in-memory string is already
121
+ // a head+tail cut); an inline one from the captured text.
122
+ function foregroundPiece(text, path, label) {
123
+ if (!text) return EMPTY_PIECE;
124
+ if (path) {
125
+ try {
126
+ return shellLogPiece(path);
127
+ } catch {
128
+ /* unreadable log: deliver the captured text */
129
+ }
130
+ }
131
+ return shellTextPiece(text, { label });
132
+ }
133
+
113
134
  export function renderCompletedResult({
114
135
  result,
115
136
  command,
@@ -151,11 +172,23 @@ export function renderCompletedResult({
151
172
  // Without a boundary a stdout tail with no trailing newline glued itself
152
173
  // onto the first stderr line and corrupted both. No spill-path block: a
153
174
  // truncated stream already carries its own "full output at <path>" marker.
154
- const payload = `${renderShellOutputBody(
155
- losslessCompaction?.stdout ?? stdout,
156
- losslessCompaction?.stderr ?? stderr,
157
- losslessCompaction
158
- )}${rescueNote}`;
175
+ // Each stream in the shared delivery shape: whole when short, else head,
176
+ // marker and tail, the marker naming the spill log (or a saved copy).
177
+ // A compacted stream goes through the same cap, its marker naming the
178
+ // stream's preserved full capture.
179
+ const streamPiece = (stream, text, logPath) => {
180
+ const compacted = losslessCompaction?.[stream];
181
+ if (compacted == null) return foregroundPiece(text, logPath, stream);
182
+ const archived = losslessCompaction.recovery?.find((item) => item.stream === stream)?.path;
183
+ return compacted ? shellTextPiece(compacted, { path: archived || null, label: stream }) : EMPTY_PIECE;
184
+ };
185
+ // Both streams share one delivery budget; the line break between them is
186
+ // taken off it.
187
+ const [outBody, errBody] = fitPieces(
188
+ [streamPiece('stdout', stdout, result.stdoutPath), streamPiece('stderr', stderr, result.stderrPath)],
189
+ DELIVERY_FULL_CHARS - 1
190
+ );
191
+ const payload = `${renderShellOutputBody(outBody, errBody, losslessCompaction)}${rescueNote}`;
159
192
  if (outcome.statusMarker) {
160
193
  const bashismHint = commandExitedNonzero || isReallyErrored ? _powershellBashismHint(analysisCommand, stderr) : '';
161
194
  const failure = _composeShellFailure(
@@ -1,12 +1,14 @@
1
- // Commands start in the foreground. Only work still running after the
2
- // 10 s coordination budget is promoted to a tracked background task.
3
- // Short commands therefore complete in the original tool turn, while longer
4
- // work returns partial output plus task_id and finishes by notification.
5
- // A per-call wait_ms may stretch the window for a command whose result should
6
- // return in the same call, never past 30 s, so the conversation is never held
7
- // open long; anything longer finishes by notification.
8
- export const DEFAULT_SHELL_AUTO_BACKGROUND_MS = 10_000;
9
- export const MAX_SHELL_WAIT_MS = 30_000;
1
+ // Commands start in the foreground and hold the call for the shell hold
2
+ // (wait-budgets.mjs). Only work still running when the hold ends — or when new
3
+ // user input ends it early — moves to a tracked background task, returning
4
+ // partial output plus task_id and finishing by notification. A per-call
5
+ // wait_ms picks a different hold, clamped to the published range.
6
+ import { SHELL_HOLD_DEFAULT_MS, SHELL_HOLD_MAX_MS, SHELL_HOLD_MIN_MS } from '../wait-budgets.mjs';
7
+
8
+ export const DEFAULT_SHELL_AUTO_BACKGROUND_MS = SHELL_HOLD_DEFAULT_MS;
9
+ // The hold of an explicit background start: long enough for the child to be
10
+ // spawned and promoted, short enough to return at once.
11
+ const BACKGROUND_START_HOLD_MS = 1;
10
12
 
11
13
  // JS timers (setTimeout) and PS WaitForExit(ms) are 32-bit: a delay above
12
14
  // 2^31-1 wraps to a tiny/negative value and fires immediately. Clamp the
@@ -15,26 +17,25 @@ export const MAX_SHELL_WAIT_MS = 30_000;
15
17
  // per-site guards.
16
18
  const TIMER_MAX_MS = 2_147_483_647;
17
19
 
18
- // Main-agent blocking budget. A timeout is the command's total deadline, not
19
- // permission to hold the conversation open for that whole duration: after
20
- // 10 s a still-running command becomes a tracked background task and
21
- // completion is pushed to the owner. MIXDOG_SHELL_AUTO_BACKGROUND_MS
22
- // overrides; an explicit 0 disables. Gated on backgroundOnTimeout so
23
- // disabled background tasks remain foreground.
24
- // A per-call wait_ms extends that default for the call, capped by
25
- // MAX_SHELL_WAIT_MS and the foreground maximum; it never shortens it. A
26
- // shorter window promotes a command that would have returned its result in
27
- // the starting call and spends a whole follow-up request on `task wait`.
28
- function autoBackgroundBudget(backgroundOnTimeout, timeout, waitMs, maxForegroundMs) {
20
+ // The shell hold. A timeout is the command's total deadline, not permission to
21
+ // hold the conversation open for that whole duration: when the hold ends a
22
+ // still-running command becomes a tracked background task and completion is
23
+ // pushed to the owner. MIXDOG_SHELL_AUTO_BACKGROUND_MS overrides the default;
24
+ // an explicit 0 disables it. A per-call wait_ms replaces the hold for that
25
+ // call, clamped to SHELL_HOLD_MIN_MS..SHELL_HOLD_MAX_MS. Gated on
26
+ // backgroundOnTimeout so disabled background tasks remain foreground.
27
+ function shellHoldMs(waitMs) {
28
+ if (waitMs > 0) return Math.min(SHELL_HOLD_MAX_MS, Math.max(SHELL_HOLD_MIN_MS, Math.floor(waitMs)));
29
29
  const raw = process.env.MIXDOG_SHELL_AUTO_BACKGROUND_MS;
30
30
  const parsed = Number(raw);
31
- let defaultMs =
32
- raw != null && String(raw).trim() !== '' && Number.isFinite(parsed) && parsed >= 0
33
- ? Math.floor(parsed)
34
- : DEFAULT_SHELL_AUTO_BACKGROUND_MS;
35
- if (waitMs > 0) defaultMs = Math.max(defaultMs, Math.min(waitMs, MAX_SHELL_WAIT_MS, maxForegroundMs));
36
- if (!backgroundOnTimeout || defaultMs <= 0) return 0;
37
- return timeout > 0 ? Math.min(defaultMs, timeout) : defaultMs;
31
+ return raw != null && String(raw).trim() !== '' && Number.isFinite(parsed) && parsed >= 0
32
+ ? Math.floor(parsed)
33
+ : SHELL_HOLD_DEFAULT_MS;
34
+ }
35
+
36
+ function autoBackgroundBudget(backgroundOnTimeout, timeout, holdMs) {
37
+ if (!backgroundOnTimeout || holdMs <= 0) return 0;
38
+ return timeout > 0 ? Math.min(holdMs, timeout) : holdMs;
38
39
  }
39
40
 
40
41
  // Background safety net. An omitted timeout_ms means "no caller deadline",
@@ -55,7 +56,14 @@ function backgroundMaxBudget() {
55
56
  // timeout becomes the background deadline.
56
57
  export function planShellDeadlines({ args, wmicRewrite, backgroundOnTimeout }) {
57
58
  const envMaxTimeout = parseInt(process.env.BASH_MAX_TIMEOUT_MS ?? '', 10);
58
- const maxForegroundMs = envMaxTimeout > 0 ? envMaxTimeout : 120_000;
59
+ // background:true (servers, watchers) skips the hold: the command moves to
60
+ // the background as soon as it is running.
61
+ const holdMs =
62
+ args.background === true
63
+ ? BACKGROUND_START_HOLD_MS
64
+ : shellHoldMs(typeof args.wait_ms === 'number' ? args.wait_ms : 0);
65
+ // The foreground cap never cuts the requested hold short.
66
+ const maxForegroundMs = Math.max(envMaxTimeout > 0 ? envMaxTimeout : 120_000, holdMs);
59
67
  const hasExplicitTimeout = typeof args.timeout_ms === 'number' && args.timeout_ms > 0;
60
68
  const timeoutMs = hasExplicitTimeout ? args.timeout_ms : 0;
61
69
  // A rewrite-supplied cap (wmic → Get-CimInstance, 30 s) is a HARD deadline
@@ -79,11 +87,6 @@ export function planShellDeadlines({ args, wmicRewrite, backgroundOnTimeout }) {
79
87
  // Soft/interrupt promotion happens before the foreground cap; an explicit
80
88
  // total deadline is preserved separately.
81
89
  backgroundDeadlineMs: hasHardDeadline ? totalTimeout : backgroundMaxBudget(),
82
- autoBackgroundMs: autoBackgroundBudget(
83
- backgroundOnTimeout,
84
- timeout,
85
- typeof args.wait_ms === 'number' ? args.wait_ms : 0,
86
- maxForegroundMs
87
- ),
90
+ autoBackgroundMs: autoBackgroundBudget(backgroundOnTimeout, timeout, holdMs),
88
91
  };
89
92
  }
@@ -19,7 +19,7 @@ import {
19
19
  export function hoistInlineScript(command, shellType) {
20
20
  const transport = { platform: process.platform, shellType };
21
21
  const hoist =
22
- planInlineScriptHoist(command) ||
22
+ planInlineScriptHoist(command, { shellType }) ||
23
23
  planLongInlineScriptFileTransport(command, transport) ||
24
24
  planLongShellScriptFileTransport(command, transport);
25
25
  if (!hoist) return { command, hoistPath: null };
@@ -1,4 +1,3 @@
1
- import { resourceAdmission } from '../../../../../shared/resource-admission.mjs';
2
1
  import { withSkillLauncherPath } from '../../../../../shared/skill-script-runtime.mjs';
3
2
  import {
4
3
  applyShellEgressPolicy,
@@ -10,9 +9,6 @@ import {
10
9
  export function buildShellSpawnEnv(cwd, baseEnv = process.env) {
11
10
  const spawnEnv = {
12
11
  ...baseEnv,
13
- // Lets fan-out-heavy children (the test runner) size themselves to their
14
- // share of the machine: up to this many shells run concurrently.
15
- MIXDOG_SHELL_CONCURRENCY_CAP: String(resourceAdmission.limits.maxShells),
16
12
  LANG: 'C.UTF-8',
17
13
  LC_ALL: 'C.UTF-8',
18
14
  MIXDOG_SESSION_CWD: String(cwd || ''),
@@ -14,6 +14,7 @@ import { invalidateBuiltinResultCache } from './cache-layers.mjs';
14
14
  import { formatShellToolFailure } from './bash-tool/result-format.mjs';
15
15
  import { cleanupArtifactOnTaskSettled, removeTransportFileNow } from './bash-tool/transport-artifacts.mjs';
16
16
  import { combineAbortSignals, sessionAbortSignal } from './bash-tool/abort-signals.mjs';
17
+ import { beginInterruptibleTaskWait } from '../../session/task-wait-control.mjs';
17
18
  import { prepareShellCommand } from './bash-tool/command-preflight.mjs';
18
19
  import { hoistInlineScript } from './bash-tool/inline-hoist.mjs';
19
20
  import { planShellDeadlines } from './bash-tool/deadline-plan.mjs';
@@ -84,6 +85,8 @@ export async function executeBashTool(args, workDir, options = {}) {
84
85
  const deadlines = planShellDeadlines({ args, wmicRewrite, backgroundOnTimeout });
85
86
 
86
87
  let combinedAbort = null;
88
+ // New user input ends the hold early, the same wake `task wait` uses.
89
+ const inputWake = backgroundOnTimeout ? beginInterruptibleTaskWait(options?.sessionId) : null;
87
90
  let backgrounded = false;
88
91
  let backgroundJobId = null;
89
92
  let queuedSettled = null;
@@ -104,6 +107,8 @@ export async function executeBashTool(args, workDir, options = {}) {
104
107
  cwd: bashWorkDir,
105
108
  timeoutMs: deadlines.timeout,
106
109
  abortSignal: combinedAbort.signal,
110
+ wakeSignal: inputWake?.signal || null,
111
+ autoBackgroundReason: args.background === true ? 'background' : 'threshold',
107
112
  autoBackgroundMs: deadlines.autoBackgroundMs,
108
113
  // On a foreground timeout, promote the still-running child to a tracked
109
114
  // background job (task_id + notify) instead of tree-killing it.
@@ -144,6 +149,7 @@ export async function executeBashTool(args, workDir, options = {}) {
144
149
  rewriteNote: [wmicRewrite?.note, bashismRewrite?.note].filter(Boolean).join('\n'),
145
150
  });
146
151
  } finally {
152
+ inputWake?.dispose();
147
153
  combinedAbort?.cleanup?.();
148
154
  if (hoisted.hoistPath) {
149
155
  // A promoted or queued command still runs from the hoisted file; a
@@ -9,25 +9,24 @@ import { GIT_TOOL_DEF } from './git-command-tool.mjs';
9
9
  import { PUBLIC_PATH_BATCH_LIMIT, PUBLIC_READ_WINDOW_MAX } from './arg-guard.mjs';
10
10
  import { GITHUB_TOOL_DEF } from '../../../../github/tool.mjs';
11
11
  import { envFlag } from '../../../../shared/env.mjs';
12
- // action=wait ceiling, colocated with the schema that publishes it so the
13
- // documented bounds and the runtime clamp cannot drift. The wait returns the
14
- // instant the task settles, so the ceiling only bounds how long a STILL-running
15
- // task may hold the call. The floor is the load-bearing part: measured runs
16
- // show a caller with no wait primitive re-reading the same task every 2-3 s
17
- // (177 reads in one trial), and a caller that can pass a timeout occasionally
18
- // asks for 1 s. Both collapse into a busy loop without a floor.
19
- //
20
- // The ceiling is bounded at 10 min for the mirror-image failure: a single wait
21
- // holding the ONLY decision point for longer than the caller's whole remaining
22
- // budget. Measured run: one `wait` with timeout_ms 2_400_000 blocked 1_611 s of
23
- // an 1_800 s budget and the caller never got another turn — no partial result,
24
- // no alternative path. A still-running task returns its current output at the
25
- // ceiling, so the caller re-decides and may wait again; nothing is lost except
26
- // the unbounded block. 10 min also matches the reference agent's equivalent
27
- // wait primitive, which caps at 600_000 ms.
28
- export const TASK_WAIT_TIMEOUT_DEFAULT_MS = 60_000;
29
- export const TASK_WAIT_TIMEOUT_MIN_MS = 10_000;
30
- export const TASK_WAIT_TIMEOUT_MAX_MS = 600_000;
12
+ import {
13
+ SHELL_HOLD_DEFAULT_MS,
14
+ SHELL_HOLD_MAX_MS,
15
+ SHELL_HOLD_MIN_MS,
16
+ AGENT_TASK_WAIT_DEFAULT_MS,
17
+ TASK_WAIT_DEFAULT_MS,
18
+ TASK_WAIT_MAX_MS,
19
+ TASK_WAIT_MIN_MS,
20
+ budgetSeconds,
21
+ } from './wait-budgets.mjs';
22
+ // action=wait bounds, published by the schema below and clamped by the task
23
+ // tool; the values live in wait-budgets.mjs. The wait returns the instant the
24
+ // task settles or new user input or a task notification arrives, so the ceiling only bounds a
25
+ // still-running task; the floor keeps wait from collapsing into a busy poll.
26
+ export const TASK_WAIT_TIMEOUT_DEFAULT_MS = TASK_WAIT_DEFAULT_MS;
27
+ export const TASK_WAIT_TIMEOUT_MIN_MS = TASK_WAIT_MIN_MS;
28
+ export const TASK_WAIT_TIMEOUT_MAX_MS = TASK_WAIT_MAX_MS;
29
+ const _holdS = budgetSeconds(SHELL_HOLD_DEFAULT_MS);
31
30
  const _shellSyntaxCheat =
32
31
  process.platform === 'win32'
33
32
  ? " PowerShell: ; between independent commands, if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } between dependent ones; no heredoc, </dev/null or | head/tail/wc (Select-Object -First/-Last); multiline python/node via @'…'@ | python - (node -); 2>$null; /c/→C:\\; $PID is reserved."
@@ -146,7 +145,7 @@ export const BUILTIN_TOOLS = [
146
145
  openWorldHint: true,
147
146
  compressible: true,
148
147
  },
149
- description: `Run programs, builds, tests and computation. Never use shell commands or scripts for work covered by dedicated tools: cat/head/tail→read, ls→list, find→glob, filename lookup→find, grep/rg→grep, code structure→code_graph, file edits/writes (sed/awk/redirection)→edit/apply_patch, Git→git when that tool is on the surface. Tool names are not shell commands. ${_shellBackgroundDisabled ? 'Commands run in the foreground until completion.' : 'After a 10s foreground window (not a timeout; omit wait_ms by default, set it up to 30s only for a command you expect to finish in 10–30s, e.g. one test file), unfinished work continues under task_id; use task wait, not read polling. If no execution slot frees within the admission wait, the command is queued as a background task with a task_id. Dev servers/watchers: pass timeout_ms or stop them with task cancel when done.'}`,
148
+ description: `Run programs, builds, tests and computation. Never use shell commands or scripts for work covered by dedicated tools: cat/head/tail→read, ls→list, find→glob, filename lookup→find, grep/rg→grep, code structure→code_graph, file edits/writes (sed/awk/redirection)→edit/apply_patch, Git→git when that tool is on the surface. Tool names are not shell commands. ${_shellBackgroundDisabled ? 'Commands run in the foreground until completion.' : `Holds up to ${_holdS}s for the result. Still running at ${_holdS}s, or new user input or a task notification arrives: it moves to the background (never killed) and returns task_id plus output so far. Completion is delivered automatically; task wait only when you need the result now. If no execution slot frees, the command is queued as a background task. Dev servers/watchers: background:true, and stop them with task cancel when done.`}`,
150
149
  inputSchema: {
151
150
  type: 'object',
152
151
  properties: {
@@ -154,15 +153,18 @@ export const BUILTIN_TOOLS = [
154
153
  timeout_ms: {
155
154
  type: 'integer',
156
155
  minimum: 0,
157
- description:
158
- 'Hard kill deadline in ms (omit or 0 = none), separate from the 10s foreground window; set one for throwaway probes so a slow check fails fast.',
156
+ description: `Hard kill deadline in ms (omit or 0 = none), separate from the ${_holdS}s hold; set one for throwaway probes so a slow check fails fast.`,
159
157
  },
160
158
  wait_ms: {
161
159
  type: 'integer',
162
- minimum: 10000,
163
- maximum: 30000,
160
+ minimum: SHELL_HOLD_MIN_MS,
161
+ maximum: SHELL_HOLD_MAX_MS,
162
+ description: `Hold in ms before a still-running command moves to the background (default ${SHELL_HOLD_DEFAULT_MS}). Usually omit.`,
163
+ },
164
+ background: {
165
+ type: 'boolean',
164
166
  description:
165
- 'Foreground wait in ms before unfinished work continues as a background task (default 10000). Omit it by default. Set it only when you expect the command to finish within 10–30s (e.g. one test file or a small build step); never for work expected to take longer, which finishes by notification.',
167
+ 'Start in the background at once (servers, watchers, `npm run dev`); returns task_id right away.',
166
168
  },
167
169
  },
168
170
  required: ['command'],
@@ -188,7 +190,7 @@ export const BUILTIN_TOOLS = [
188
190
  openWorldHint: false,
189
191
  },
190
192
  description:
191
- 'Manage shell tasks. read/wait return new output, prioritizing the tail on completion. output:tail re-reads recent output without advancing the cursor; use read on the log path for omitted content. Wait instead of polling; completion notifications are automatic.',
193
+ 'Manage shell tasks. Completion is delivered automatically; wait only when you need the result now, never poll with read. wait returns when the task finishes, or when new user input or any task notification arrives (the task keeps running). read/wait return new output, prioritizing the tail on completion. output:tail re-reads recent output without advancing the cursor; use read on the log path for omitted content.',
192
194
  inputSchema: {
193
195
  type: 'object',
194
196
  properties: {
@@ -201,7 +203,7 @@ export const BUILTIN_TOOLS = [
201
203
  timeout_ms: {
202
204
  type: 'integer',
203
205
  minimum: 0,
204
- description: `Wait ceiling in ms; returns on completion, else current output at the ceiling. Default ${TASK_WAIT_TIMEOUT_DEFAULT_MS}; clamped to ${TASK_WAIT_TIMEOUT_MIN_MS}-${TASK_WAIT_TIMEOUT_MAX_MS}.`,
206
+ description: `Wait ceiling in ms; returns on completion, new user input or a task notification, else current output at the ceiling. Default ${TASK_WAIT_TIMEOUT_DEFAULT_MS} (agents ${AGENT_TASK_WAIT_DEFAULT_MS}); clamped to ${TASK_WAIT_TIMEOUT_MIN_MS}-${TASK_WAIT_TIMEOUT_MAX_MS}.`,
205
207
  },
206
208
  output: {
207
209
  type: 'string',
@@ -708,7 +708,7 @@ async function applyEditNative(edit, oldStr, newStr, editPlan, replaceAll) {
708
708
  if (/old_string (?:not found|found \d+ times)/i.test(message)) {
709
709
  const latest = readEditTextForDisplay(fullPath, fileEnc) ?? content;
710
710
  const hinted = /found \d+ times/i.test(message)
711
- ? message.replace(/(found \d+ times)/i, `$1${matchLineHint(latest, oldStr)}`)
711
+ ? message.replace(/(found \d+ times)/i, (__m, __1) => `${__1}${matchLineHint(latest, oldStr)}`)
712
712
  : message;
713
713
  return `Error: edit failed (${hinted})${formatEditFailureExcerpt(latest, oldStr, message)}`;
714
714
  }
@@ -15,7 +15,7 @@ import { markScopedCacheIncomplete } from '../../session/cache/scoped-cache-outc
15
15
  import { cacheGet, cacheSet, statPathsForMtime } from './cache-layers.mjs';
16
16
  import { formatListSize, formatMtime } from './list-formatting.mjs';
17
17
  import { compileSimpleGlob } from './glob-walk.mjs';
18
- import { capLineOrientedToolOutput, LOCATOR_OUTPUT_MAX_BYTES } from './tool-output-limit.mjs';
18
+ import { capLineOrientedToolOutput, capMarker, LOCATOR_OUTPUT_MAX_BYTES } from './tool-output-limit.mjs';
19
19
  import { runRg } from './native-search-runner.mjs';
20
20
  import { tryServeFuzzySearch } from './native-search-client.mjs';
21
21
  import {
@@ -23,7 +23,12 @@ import {
23
23
  recordLocalSearchCacheHit,
24
24
  recordNativeSearchTiming,
25
25
  } from './local-search-telemetry.mjs';
26
- import { guardedWalkRoot, normalizeListHeadLimit, pageContinuationLine } from './lib/list-helpers.mjs';
26
+ import {
27
+ comparePathsStable,
28
+ guardedWalkRoot,
29
+ normalizeListHeadLimit,
30
+ pageContinuationLine,
31
+ } from './lib/list-helpers.mjs';
27
32
  import { reportToolProgress } from './lib/tool-progress.mjs';
28
33
  import { displayRelPath, statWalkRoot, toolErrorText } from './list-tool-shared.mjs';
29
34
 
@@ -198,11 +203,16 @@ export async function executeFuzzyFindTool(args, workDir, options = {}) {
198
203
  const { query, fullPath, headLimit } = request;
199
204
  const cacheKey = fuzzyFindCacheKey(request);
200
205
  const capFindResult = (value) =>
201
- capLineOrientedToolOutput(
202
- value,
203
- _findOutputBudgetBytes(options),
204
- () => `... [find result budget reached for query=${JSON.stringify(query)}; narrow path/limit]`
205
- );
206
+ capLineOrientedToolOutput(value, _findOutputBudgetBytes(options), (kept) => {
207
+ const rows = kept.filter((line) => line && !/^(?:#|\.\.\.|\[|\()/.test(line)).length;
208
+ return capMarker({
209
+ limit: _findOutputBudgetBytes(options),
210
+ shown: `${rows} match row(s) shown for query=${JSON.stringify(query)}`,
211
+ // Fuzzy find ranks by score and has no offset: a continuation offset
212
+ // would be ignored, so the cut gets narrowing advice instead.
213
+ narrow: rows > 0 ? 'use a more specific query or add path' : 'narrow path/limit',
214
+ });
215
+ });
206
216
  const cached = cacheGet(cacheKey);
207
217
  if (cached !== null) {
208
218
  recordLocalSearchCacheHit('result');
@@ -444,9 +454,12 @@ async function collectFindMatches(request, relPaths, nameMatches, workDir) {
444
454
  }
445
455
 
446
456
  function findComparator(sortMode) {
447
- if (sortMode === 'name') return (a, b) => normalizeOutputPath(a.path).localeCompare(normalizeOutputPath(b.path));
448
- if (sortMode === 'size') return (a, b) => b.size - a.size;
449
- return (a, b) => b.mtimeMs - a.mtimeMs;
457
+ const byPath = (a, b) => comparePathsStable(normalizeOutputPath(a.path), normalizeOutputPath(b.path));
458
+ if (sortMode === 'name') {
459
+ return (a, b) => normalizeOutputPath(a.path).localeCompare(normalizeOutputPath(b.path)) || byPath(a, b);
460
+ }
461
+ if (sortMode === 'size') return (a, b) => b.size - a.size || byPath(a, b);
462
+ return (a, b) => b.mtimeMs - a.mtimeMs || byPath(a, b);
450
463
  }
451
464
 
452
465
  function findResultLine(match, fullPath) {
@@ -467,7 +480,11 @@ function renderFindResult(
467
480
  if (paged) lines.push(pageContinuationLine(offset, sliced.length, matches.length));
468
481
  if (truncatedByCap) {
469
482
  lines.push(
470
- `... walk truncated at ${FIND_ABSOLUTE_CAP} matches; narrow the scope (path/name/modified_after) for accurate global sort`
483
+ capMarker({
484
+ limit: `${FIND_ABSOLUTE_CAP} matches`,
485
+ shown: 'walk cut, global sort inexact',
486
+ narrow: 'narrow the scope (path/name/modified_after)',
487
+ })
471
488
  );
472
489
  }
473
490
  if (walkIncomplete) {
@@ -108,6 +108,11 @@ function gitActionOf(operation, args) {
108
108
  return first || DEFAULT_GIT_ACTIONS.get(operation) || 'list';
109
109
  }
110
110
 
111
+ const GIT_BRANCH_WRITE_FLAGS =
112
+ /^(?:-[dDmMcCfu]|--(?:delete|move|copy|force|set-upstream-to(?:=.*)?|unset-upstream|edit-description|track|no-track|create-reflog))$/;
113
+ const GIT_TAG_WRITE_FLAGS =
114
+ /^(?:-[adfsumFe]|--(?:annotate|delete|force|sign|local-user(?:=.*)?|message(?:=.*)?|file(?:=.*)?|edit))$/;
115
+
111
116
  const GIT_CONFIG_MUTATION_FLAGS = new Set([
112
117
  '--add',
113
118
  '--edit',
@@ -140,9 +145,13 @@ export function gitPlanIsReadOnly(plan) {
140
145
  }
141
146
  if (operation === 'hash-object') return !args.includes('-w') && !args.includes('--stdin-paths');
142
147
  if (operation === 'branch' || operation === 'tag') {
143
- return (
144
- args.length === 0 || args.some((value) => ['--list', '-l', '-a', '--all', '-r', '--remotes'].includes(value))
145
- );
148
+ // A listing flag does not make the call read-only when another flag writes:
149
+ // `branch -a -D x` deletes and `tag -a v1` creates (there `-a` is
150
+ // --annotate, not --all).
151
+ const writes = operation === 'tag' ? GIT_TAG_WRITE_FLAGS : GIT_BRANCH_WRITE_FLAGS;
152
+ if (args.some((value) => writes.test(value))) return false;
153
+ const listFlags = operation === 'tag' ? ['--list', '-l'] : ['--list', '-l', '-a', '--all', '-r', '--remotes'];
154
+ return args.length === 0 || args.some((value) => listFlags.includes(value));
146
155
  }
147
156
  if (operation === 'config') {
148
157
  if (args.some((value) => GIT_CONFIG_MUTATION_FLAGS.has(value))) return false;