mixdog 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,239 @@
1
+ // Tool dispatch/execution cluster, extracted from loop.mjs.
2
+ // executeTool routes a single tool call (Skill/skills/mcp/code_graph/internal/
3
+ // shell/apply_patch/builtin/external-adapter) through before/after hooks and the
4
+ // scoped-cache outcome bookkeeping. No behavior change: bodies are verbatim from
5
+ // loop.mjs, re-exported via the facade so existing importers keep working.
6
+ import { executeMcpTool, isMcpTool, mcpToolHasField } from '../../mcp/client.mjs';
7
+ import { executeBuiltinTool, formatUnknownBuiltinToolMessage, isBuiltinTool, isExternalAdapterTool } from '../../tools/builtin.mjs';
8
+ import { executeBashSessionTool } from '../../tools/bash-session.mjs';
9
+ import { executePatchTool } from '../../tools/patch.mjs';
10
+ import { executeInternalTool, isInternalTool } from '../../internal-tools.mjs';
11
+ import { normalizeToolEnvelope, makeToolEnvelope } from '../tool-envelope.mjs';
12
+ import { getSessionAbortSignal, enqueuePendingMessage } from '../manager.mjs';
13
+ import { createScopedCacheOutcome } from '../cache/scoped-cache-outcome.mjs';
14
+ import { modelVisibleToolCompletionMessage } from '../../../../shared/tool-execution-contract.mjs';
15
+ import { _isScopedCacheableTool } from './tool-classify.mjs';
16
+ import {
17
+ buildSkillsListResponse,
18
+ viewSkill,
19
+ resolveToolResultAfterHook,
20
+ extractBashSessionId,
21
+ buildAgentBashSessionArgs,
22
+ resolvePreToolAskApproval,
23
+ } from './tool-helpers.mjs';
24
+
25
+ let codeGraphRuntimePromise = null;
26
+ async function executeCodeGraphToolLazy(name, args, cwd, signal = null, options = {}) {
27
+ codeGraphRuntimePromise ??= import('../../tools/code-graph.mjs');
28
+ const mod = await codeGraphRuntimePromise;
29
+ if (typeof mod.executeCodeGraphTool !== 'function') throw new Error('code_graph runtime is not available');
30
+ return mod.executeCodeGraphTool(name, args, cwd, signal, options);
31
+ }
32
+
33
+ export function _scopedCacheOutcomeForCall(sessionRef, toolCallId, toolName, callerSessionId, executeOpts = {}) {
34
+ if (executeOpts.scopedCacheOutcome) {
35
+ if (sessionRef && toolCallId) {
36
+ // instanceof guard: a session revived from disk (JSON round-trip) turns
37
+ // this Map into a plain object `{}` — truthy, but without Map methods.
38
+ if (!(sessionRef._scopedCacheOutcomeByCallId instanceof Map)) sessionRef._scopedCacheOutcomeByCallId = new Map();
39
+ sessionRef._scopedCacheOutcomeByCallId.set(toolCallId, executeOpts.scopedCacheOutcome);
40
+ }
41
+ return executeOpts.scopedCacheOutcome;
42
+ }
43
+ if (!callerSessionId || !toolCallId || !_isScopedCacheableTool(toolName)) return null;
44
+ const outcome = createScopedCacheOutcome();
45
+ if (sessionRef) {
46
+ if (!(sessionRef._scopedCacheOutcomeByCallId instanceof Map)) sessionRef._scopedCacheOutcomeByCallId = new Map();
47
+ sessionRef._scopedCacheOutcomeByCallId.set(toolCallId, outcome);
48
+ }
49
+ return outcome;
50
+ }
51
+
52
+ export async function executeTool(name, args, cwd, callerSessionId, sessionRef, executeOpts = {}) {
53
+ const scopedCacheOutcome = _scopedCacheOutcomeForCall(
54
+ sessionRef,
55
+ executeOpts.toolCallId,
56
+ name,
57
+ callerSessionId,
58
+ executeOpts,
59
+ );
60
+ const toolOpts = scopedCacheOutcome
61
+ ? { ...executeOpts, scopedCacheOutcome }
62
+ : executeOpts;
63
+ const notificationSessionId = String(executeOpts.notifySessionId || sessionRef?.ownerSessionId || callerSessionId || '').trim();
64
+ const notifyFn = typeof executeOpts.notifyFn === 'function'
65
+ ? executeOpts.notifyFn
66
+ : (text, meta = {}) => {
67
+ if (!notificationSessionId) return;
68
+ try {
69
+ const visible = modelVisibleToolCompletionMessage(text, meta);
70
+ if (visible) enqueuePendingMessage(notificationSessionId, visible);
71
+ } catch { /* best effort */ }
72
+ };
73
+ const completionToolOpts = {
74
+ ...toolOpts,
75
+ sessionId: callerSessionId,
76
+ callerSessionId: notificationSessionId || callerSessionId,
77
+ routingSessionId: callerSessionId,
78
+ clientHostPid: sessionRef?.clientHostPid,
79
+ notifyFn,
80
+ };
81
+ const beforeToolHook = typeof executeOpts.beforeToolHook === 'function'
82
+ ? executeOpts.beforeToolHook
83
+ : sessionRef?.beforeToolHook;
84
+ const toolApprovalHook = typeof executeOpts.toolApprovalHook === 'function'
85
+ ? executeOpts.toolApprovalHook
86
+ : sessionRef?.toolApprovalHook;
87
+ if (beforeToolHook) {
88
+ try {
89
+ const decision = await beforeToolHook({
90
+ name,
91
+ args,
92
+ cwd,
93
+ sessionId: callerSessionId,
94
+ toolCallId: executeOpts.toolCallId || null,
95
+ });
96
+ const action = String(decision?.action || decision?.decision || '').toLowerCase();
97
+ if (action === 'deny' || action === 'block') {
98
+ const reason = decision?.reason ? `: ${decision.reason}` : '';
99
+ return `Error: tool "${name}" denied by hook${reason}`;
100
+ }
101
+ if (action === 'ask') {
102
+ const askReason = String(decision?.reason || 'approval requested by hook').trim();
103
+ const askOutcome = await resolvePreToolAskApproval({
104
+ toolName: name,
105
+ args,
106
+ cwd,
107
+ sessionId: callerSessionId,
108
+ toolCallId: executeOpts.toolCallId || null,
109
+ askReason,
110
+ toolApprovalHook,
111
+ });
112
+ if (askOutcome.denial) return askOutcome.denial;
113
+ const approval = askOutcome.approval;
114
+ if (approval && typeof approval === 'object' && approval.args && typeof approval.args === 'object' && !Array.isArray(approval.args)) {
115
+ args = approval.args;
116
+ }
117
+ }
118
+ if ((action === 'modify' || action === 'rewrite') && decision?.args && typeof decision.args === 'object' && !Array.isArray(decision.args)) {
119
+ args = decision.args;
120
+ }
121
+ } catch {
122
+ // Hooks are policy extensions. A broken hook must not wedge the agent loop.
123
+ }
124
+ }
125
+ const afterToolHook = typeof executeOpts.afterToolHook === 'function'
126
+ ? executeOpts.afterToolHook
127
+ : sessionRef?.afterToolHook;
128
+ const __result = await (async () => {
129
+ if (name === 'Skill') {
130
+ return viewSkill(cwd, args?.name);
131
+ }
132
+ if (name === 'skills_list') {
133
+ return buildSkillsListResponse(cwd);
134
+ }
135
+ if (name === 'skill_view') {
136
+ return viewSkill(cwd, args?.name);
137
+ }
138
+ if (isMcpTool(name)) {
139
+ // 24h trace data shows ~24% of external MCP calls are cwd-sensitive
140
+ // (bash / grep / read / list / glob etc.) but the worker session's
141
+ // cwd was previously dropped here. Inject cwd only when the tool's
142
+ // inputSchema declares the field — schemas without it would reject
143
+ // an unknown argument.
144
+ const needsCwdInjection = cwd
145
+ && mcpToolHasField(name, 'cwd')
146
+ && (args == null || args.cwd == null);
147
+ const finalArgs = needsCwdInjection ? { ...(args || {}), cwd } : args;
148
+ return executeMcpTool(name, finalArgs);
149
+ }
150
+ if (name === 'code_graph') {
151
+ // cwd chain: args.cwd (caller-explicit) → session cwd → undefined (handler throws)
152
+ const graphCwd = (typeof args?.cwd === 'string' && args.cwd.trim()) ? args.cwd.trim() : cwd;
153
+ return executeCodeGraphToolLazy(name, args, graphCwd, null, toolOpts);
154
+ }
155
+ if (isInternalTool(name)) {
156
+ // callerSessionId propagates into server.mjs dispatchTool so that
157
+ // dispatchAiWrapped can detect and reject recursive calls from a
158
+ // hidden-role session (recall/search/explore → self).
159
+ return executeInternalTool(name, args, {
160
+ callerSessionId,
161
+ callerCwd: cwd,
162
+ clientHostPid: sessionRef?.clientHostPid,
163
+ signal: executeOpts.signal,
164
+ routingSessionId: callerSessionId,
165
+ notifyFn,
166
+ });
167
+ }
168
+ if (name === 'shell') {
169
+ const routedArgs = buildAgentBashSessionArgs(args, sessionRef);
170
+ if (!routedArgs) {
171
+ // clientHostPid scopes background shell-jobs to the dispatching
172
+ // terminal's claude.exe pid (agent sessions store it on sessionRef);
173
+ // without it resolveJobOwnerHostPid falls back to the daemon-global env.
174
+ return executeBuiltinTool(name, args, cwd, completionToolOpts);
175
+ }
176
+ // Thread the session's AbortSignal so agent type=close can interrupt the
177
+ // persistent child process. getSessionAbortSignal is imported from
178
+ // manager.mjs; callerSessionId identifies the controller.
179
+ let _bashAbortSignal = null;
180
+ try { _bashAbortSignal = getSessionAbortSignal(callerSessionId); } catch { /* ignore */ }
181
+ const result = await executeBashSessionTool('bash_session', routedArgs, cwd, {
182
+ sessionId: callerSessionId,
183
+ abortSignal: _bashAbortSignal,
184
+ });
185
+ const bashSid = extractBashSessionId(result);
186
+ if (bashSid) {
187
+ sessionRef.implicitBashSessionId = bashSid;
188
+ // Track all persistent bash sessions for bulk teardown on close.
189
+ if (sessionRef.allBashSessionIds) {
190
+ if (!sessionRef.allBashSessionIds.includes(bashSid)) {
191
+ sessionRef.allBashSessionIds.push(bashSid);
192
+ }
193
+ } else {
194
+ sessionRef.allBashSessionIds = [bashSid];
195
+ }
196
+ }
197
+ return result;
198
+ }
199
+ if (name === 'apply_patch') {
200
+ const patchArgs = typeof args === 'string' ? { patch: args } : args;
201
+ return executePatchTool(name, patchArgs, cwd, { sessionId: callerSessionId, toolCallId: executeOpts.toolCallId || null });
202
+ }
203
+ if (isBuiltinTool(name)) {
204
+ // clientHostPid threaded for the same per-terminal job-scope reason as
205
+ // the bash branch above (see resolveJobOwnerHostPid).
206
+ return executeBuiltinTool(name, args, cwd, completionToolOpts);
207
+ }
208
+ if (isExternalAdapterTool(name)) {
209
+ // Foreign-CLI tool names (StrReplace/Write/bash variants) adapt to a
210
+ // native execution inside executeBuiltinTool's default: case; on a
211
+ // shape mismatch it falls back to the redirect guidance message.
212
+ return executeBuiltinTool(name, args, cwd, completionToolOpts);
213
+ }
214
+ return formatUnknownBuiltinToolMessage(name, args, 'tool');
215
+ })();
216
+ if (typeof afterToolHook === 'function') {
217
+ try {
218
+ const hookResult = await afterToolHook({
219
+ name,
220
+ args,
221
+ cwd,
222
+ sessionId: callerSessionId,
223
+ toolCallId: executeOpts.toolCallId || null,
224
+ result: __result,
225
+ });
226
+ // Envelope-aware hook override: a PostToolUse hook may override the
227
+ // model-VISIBLE tool output (the envelope's `result` / stub), but it
228
+ // must NEVER drop the `newMessages` channel. Split first, apply the
229
+ // override to `result` only, then re-wrap so newMessages survive.
230
+ const { result: __res, newMessages: __nm } = normalizeToolEnvelope(__result);
231
+ const __overridden = resolveToolResultAfterHook(__res, hookResult);
232
+ if (__nm.length) return makeToolEnvelope(__overridden, __nm);
233
+ return __overridden;
234
+ } catch {
235
+ // PostToolUse hooks are best-effort; never let one break the tool result.
236
+ }
237
+ }
238
+ return __result;
239
+ }
@@ -0,0 +1,218 @@
1
+ // Miscellaneous tool helpers extracted from loop.mjs: eager-dispatch check,
2
+ // message-array change detect, tool-kind classification, skills, hook overrides,
3
+ // native tool-search payload parsing, bash-session routing, and approval.
4
+ import { isMcpTool } from '../../mcp/client.mjs';
5
+ import { isBuiltinTool } from '../../tools/builtin.mjs';
6
+ import { isInternalTool } from '../../internal-tools.mjs';
7
+ import { collectSkillsCached, loadSkillResource, buildSkillToolEnvelope } from '../../context/collect.mjs';
8
+ import { isAgentOwner } from '../../agent-owner.mjs';
9
+
10
+ // Eager-dispatch: tools with readOnlyHint:true in their declaration are safe
11
+ // to execute during SSE parsing so tool work overlaps with the rest of the
12
+ // stream. Writes, bash, MCP and skills stay serial after send() returns.
13
+ export function isEagerDispatchable(name, tools) {
14
+ if (!Array.isArray(tools)) return false;
15
+ const def = tools.find(t => t?.name === name);
16
+ return def?.annotations?.readOnlyHint === true;
17
+ }
18
+ export function messagesArrayChanged(before, after) {
19
+ if (!Array.isArray(before) || !Array.isArray(after)) return before !== after;
20
+ if (before.length !== after.length) return true;
21
+ for (let i = 0; i < before.length; i += 1) {
22
+ if (before[i] !== after[i]) return true;
23
+ }
24
+ return false;
25
+ }
26
+
27
+ const SKILL_TOOL_NAMES = new Set(['Skill', 'skills_list', 'skill_view']);
28
+ const SPECIAL_TOOL_NAMES = new Set(['bash_session', 'apply_patch', 'code_graph']);
29
+ const BASH_SESSION_HEADER_RE = /\[session: ([^\]\r\n]+)\]/;
30
+
31
+ export function getToolKind(name) {
32
+ if (SKILL_TOOL_NAMES.has(name)) return 'skill';
33
+ if (SPECIAL_TOOL_NAMES.has(name)) return 'builtin';
34
+ if (isMcpTool(name)) return 'mcp';
35
+ if (isInternalTool(name)) return 'internal';
36
+ if (isBuiltinTool(name)) return 'builtin';
37
+ return 'builtin';
38
+ }
39
+ export function buildSkillsListResponse(cwd) {
40
+ const skills = collectSkillsCached(cwd);
41
+ const entries = skills.map(s => ({ name: s.name, description: s.description || '' }));
42
+ return JSON.stringify({ skills: entries });
43
+ }
44
+ export function viewSkill(cwd, name) {
45
+ if (!name) return 'Error: skill name is required';
46
+ const res = loadSkillResource(name, cwd);
47
+ if (!res) return `Error: skill "${name}" not found`;
48
+ // Return the general tool envelope: the model-visible tool_result is the
49
+ // short stub (`Loaded skill: <name>`) and the full SKILL.md body is
50
+ // delivered ONCE as a separate injected role:'user' message (newMessages).
51
+ return buildSkillToolEnvelope(name, res.content, res.dir);
52
+ }
53
+
54
+ /** Normalize PostToolUse hook override values (legacy MCP text envelopes only). */
55
+ export function normalizeHookUpdatedToolOutput(value) {
56
+ if (typeof value === 'string') return value;
57
+ if (value == null) return '';
58
+ if (typeof value === 'object' && Array.isArray(value.content)) {
59
+ const hasNonText = value.content.some((c) => c && typeof c === 'object' && c.type && c.type !== 'text');
60
+ if (hasNonText) return value;
61
+ return value.content
62
+ .map((c) => (c?.type === 'text' ? c.text || '' : JSON.stringify(c)))
63
+ .join('\n');
64
+ }
65
+ return value;
66
+ }
67
+
68
+ export function resolveToolResultAfterHook(originalResult, hookResult) {
69
+ if (!hookResult || typeof hookResult !== 'object' || hookResult.updatedToolOutput === undefined) {
70
+ return originalResult;
71
+ }
72
+ const updated = normalizeHookUpdatedToolOutput(hookResult.updatedToolOutput);
73
+ return updated === undefined ? originalResult : updated;
74
+ }
75
+
76
+ export function parseNativeToolSearchPayload(toolName, result) {
77
+ if (toolName !== 'tool_search' || typeof result !== 'string') return null;
78
+ try {
79
+ const parsed = JSON.parse(result);
80
+ const native = parsed?.nativeToolSearch;
81
+ if (!native || typeof native !== 'object') return null;
82
+ const rawToolReferences = Array.isArray(native.toolReferences) ? native.toolReferences : [];
83
+ const toolReferences = rawToolReferences
84
+ .filter((name) => typeof name === 'string')
85
+ .map((name) => name.trim())
86
+ .filter(Boolean);
87
+ const rawOpenaiTools = Array.isArray(native.openaiTools) ? native.openaiTools : [];
88
+ const openaiTools = rawOpenaiTools.filter((tool) => (
89
+ tool
90
+ && typeof tool === 'object'
91
+ && !Array.isArray(tool)
92
+ && typeof tool.name === 'string'
93
+ && tool.name.trim()
94
+ && (tool.type === undefined || typeof tool.type === 'string')
95
+ ));
96
+ if (!toolReferences.length && !openaiTools.length) return null;
97
+ const baseSummary = typeof native.summary === 'string' && native.summary
98
+ ? native.summary
99
+ : `Loaded deferred tools: ${toolReferences.join(', ') || openaiTools.map((tool) => tool.name).filter(Boolean).join(', ')}`;
100
+ const selectedTools = parsed?.selected?.tools;
101
+ const missing = Array.isArray(selectedTools?.missing)
102
+ ? selectedTools.missing.map((name) => String(name || '').trim()).filter(Boolean)
103
+ : [];
104
+ const blocked = Array.isArray(selectedTools?.blocked)
105
+ ? selectedTools.blocked
106
+ .map((entry) => {
107
+ if (entry && typeof entry === 'object') {
108
+ const name = String(entry.name || '').trim();
109
+ if (!name) return '';
110
+ const reason = String(entry.reason || '').trim();
111
+ return reason ? `${name} (${reason})` : name;
112
+ }
113
+ return String(entry || '').trim();
114
+ })
115
+ .filter(Boolean)
116
+ : [];
117
+ const extraLines = [];
118
+ if (missing.length) extraLines.push(`missing: ${missing.join(', ')}`);
119
+ if (blocked.length) extraLines.push(`blocked: ${blocked.join(', ')}`);
120
+ const summary = extraLines.length ? `${baseSummary}\n${extraLines.join('; ')}` : baseSummary;
121
+ return {
122
+ toolReferences,
123
+ openaiTools,
124
+ summary,
125
+ };
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ export function extractBashSessionId(result) {
131
+ if (typeof result !== 'string') return null;
132
+ const match = BASH_SESSION_HEADER_RE.exec(result);
133
+ return match ? match[1] : null;
134
+ }
135
+
136
+ export function buildAgentBashSessionArgs(args, sessionRef) {
137
+ if (!isAgentOwner(sessionRef)) return null;
138
+ // run_in_background is a detached one-shot job, incompatible with the
139
+ // persistent bash session. Fall through to the background-job path
140
+ // (executeBuiltinTool -> startBackgroundShellJob) so the worker gets a
141
+ // task_id that task control can resolve — otherwise the persistent
142
+ // session returns a [session: ...] header and task control reports "task not found".
143
+ if (args?.run_in_background === true) return null;
144
+ const routedArgs = { ...(args || {}) };
145
+ const explicitSessionId = typeof routedArgs.session_id === 'string' && routedArgs.session_id.trim()
146
+ ? routedArgs.session_id.trim()
147
+ : null;
148
+ const wantsPersistent = routedArgs.persistent === true || !!explicitSessionId;
149
+ if (!wantsPersistent) return null;
150
+ if (!explicitSessionId && sessionRef?.implicitBashSessionId) {
151
+ routedArgs.session_id = sessionRef.implicitBashSessionId;
152
+ } else if (explicitSessionId) {
153
+ routedArgs.session_id = explicitSessionId;
154
+ }
155
+ delete routedArgs.persistent;
156
+ return routedArgs;
157
+ }
158
+
159
+ export function formatMissingToolApprovalUiDenial(toolName, askReason) {
160
+ const reason = String(askReason || 'approval requested by hook').trim();
161
+ const name = String(toolName || 'tool');
162
+ return `Error: tool "${name}" denied by hook: approval required but no approval UI is available${reason ? ` (${reason})` : ''}`;
163
+ }
164
+
165
+ /**
166
+ * Resolve PreToolUse `{ action: 'ask' }` against an optional approval callback.
167
+ * Returns `{ denial }` when the tool must not run; otherwise `{ approval }`.
168
+ */
169
+ export async function resolvePreToolAskApproval({
170
+ toolName,
171
+ args,
172
+ cwd,
173
+ sessionId,
174
+ toolCallId,
175
+ askReason,
176
+ toolApprovalHook,
177
+ }) {
178
+ const name = String(toolName || 'tool');
179
+ const reason = String(askReason || 'approval requested by hook').trim();
180
+ if (typeof toolApprovalHook !== 'function') {
181
+ return { denial: formatMissingToolApprovalUiDenial(name, reason) };
182
+ }
183
+ let approval;
184
+ try {
185
+ approval = await toolApprovalHook({
186
+ name,
187
+ args,
188
+ cwd,
189
+ sessionId,
190
+ toolCallId: toolCallId || null,
191
+ reason,
192
+ });
193
+ } catch (error) {
194
+ const detail = error?.message || String(error || 'approval failed');
195
+ return { denial: `Error: tool "${name}" denied by hook: ${detail}` };
196
+ }
197
+ if (!approvalGranted(approval)) {
198
+ const detail = approvalReason(approval, reason || 'not approved');
199
+ return { denial: `Error: tool "${name}" denied by hook: ${detail}` };
200
+ }
201
+ return { approval };
202
+ }
203
+
204
+ export function approvalGranted(value) {
205
+ if (value === true) return true;
206
+ if (!value || typeof value !== 'object') return false;
207
+ if (value.approved === true || value.allow === true || value.allowed === true) return true;
208
+ const decision = String(value.decision || value.action || value.result || '').trim().toLowerCase();
209
+ return decision === 'approve' || decision === 'approved' || decision === 'allow' || decision === 'yes';
210
+ }
211
+
212
+ export function approvalReason(value, fallback = '') {
213
+ if (value && typeof value === 'object') {
214
+ const reason = String(value.reason || value.message || '').trim();
215
+ if (reason) return reason;
216
+ }
217
+ return fallback;
218
+ }
@@ -0,0 +1,101 @@
1
+ // Pre-provider transcript pairing repair, extracted from loop.mjs.
2
+ // Anthropic 400-rejects when an assistant message ends with tool_use blocks and
3
+ // the next message isn't tool results for those exact ids. abort/timeout/error
4
+ // races in the loop body can leave a dangling assistant tool_use at the tail;
5
+ // strip any trailing/orphaned tool_use|tool_result so provider.send sees a
6
+ // valid transcript instead of leaking the 400 to the user.
7
+ import { sanitizeToolPairs } from '../context-utils.mjs';
8
+
9
+ // Transcript pairing guard. Anthropic 400-rejects when an assistant message
10
+ // ends with tool_use blocks and the next message isn't tool results for
11
+ // those exact ids. abort/timeout/error race in the loop body can leave a
12
+ // dangling assistant tool_use at the tail (e.g. the structure_probe loop
13
+ // running 12 deep then aborting between push-assistant and push-tool).
14
+ // Strip any trailing assistant tool_use that has no matching tool result
15
+ // so provider.send sees a valid transcript instead of leaking the 400 to
16
+ // the user. Repair runs every iteration but is a no-op on healthy paths.
17
+ function _ensureTranscriptPairing(msgs, sessionId) {
18
+ // Walk backwards to find the last assistant message that emitted
19
+ // tool_use, then validate that every id has a matching tool result
20
+ // inside the CONTIGUOUS tool-message block immediately following it.
21
+ // Earlier guard splice'd the entire tail — which silently deleted any
22
+ // user prompt appended after the dangling assistant by manager.mjs:
23
+ // when the guard fired with shape
24
+ // [..., assistant{a,b}, tool{a}, user{new prompt}]
25
+ // the splice removed user{new prompt} along with the orphan suffix.
26
+ // Fix: remove only assistant + the contiguous tool block; preserve
27
+ // anything past it (user / system / next assistant) untouched.
28
+ let popped = 0;
29
+ while (msgs.length > 0) {
30
+ let lastAssistantIdx = -1;
31
+ for (let i = msgs.length - 1; i >= 0; i--) {
32
+ const m = msgs[i];
33
+ if (m?.role === 'assistant' && Array.isArray(m.toolCalls) && m.toolCalls.length > 0) {
34
+ lastAssistantIdx = i;
35
+ break;
36
+ }
37
+ }
38
+ if (lastAssistantIdx === -1) break;
39
+ // Collect the contiguous tool messages directly after this assistant.
40
+ // Anything past that block is unrelated (next user prompt, system
41
+ // marker, etc.) and must survive the repair.
42
+ let toolBlockEnd = lastAssistantIdx + 1;
43
+ while (toolBlockEnd < msgs.length && msgs[toolBlockEnd]?.role === 'tool') {
44
+ toolBlockEnd += 1;
45
+ }
46
+ const toolBlock = msgs.slice(lastAssistantIdx + 1, toolBlockEnd);
47
+ const ids = msgs[lastAssistantIdx].toolCalls.map(c => c.id);
48
+ const matched = ids.every(id => toolBlock.some(m => m.toolCallId === id));
49
+ if (matched) break;
50
+ const removed = toolBlockEnd - lastAssistantIdx;
51
+ msgs.splice(lastAssistantIdx, removed);
52
+ popped += removed;
53
+ }
54
+ // Second sweep — catch dangling tool results that survived the
55
+ // contiguous-block splice. Anthropic strict spec requires every
56
+ // tool result to sit in a contiguous block right after the
57
+ // assistant whose toolCalls produced it; a `[..., assistant{a,b},
58
+ // tool{a}, user, tool{b}]` shape leaves tool{b} orphaned even
59
+ // after assistant + tool{a} are repaired by the loop above.
60
+ // Walk back from each tool message to the nearest non-tool
61
+ // ancestor; if it is not an assistant whose toolCalls include
62
+ // this id, drop the orphan.
63
+ for (let i = msgs.length - 1; i >= 0; i--) {
64
+ const m = msgs[i];
65
+ if (m?.role !== 'tool') continue;
66
+ if (!m.toolCallId) {
67
+ msgs.splice(i, 1);
68
+ popped += 1;
69
+ continue;
70
+ }
71
+ let prevIdx = i - 1;
72
+ while (prevIdx >= 0 && msgs[prevIdx]?.role === 'tool') prevIdx--;
73
+ const anchor = prevIdx >= 0 ? msgs[prevIdx] : null;
74
+ const anchorOk = anchor?.role === 'assistant'
75
+ && Array.isArray(anchor.toolCalls)
76
+ && anchor.toolCalls.some(c => c.id === m.toolCallId);
77
+ if (!anchorOk) {
78
+ msgs.splice(i, 1);
79
+ popped += 1;
80
+ }
81
+ }
82
+ if (popped > 0 && sessionId) {
83
+ try { process.stderr.write(`[transcript-repair] sess=${sessionId} popped=${popped} dangling assistant tool_use\n`); } catch {}
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Pre-provider transcript repair for the agent loop. Reattach valid tool
89
+ * results (non-destructive) before any destructive orphan pairing cleanup.
90
+ * Mutates `messages` in place to preserve the session array reference.
91
+ */
92
+ export function repairTranscriptBeforeProviderSend(messages, sessionId = null) {
93
+ if (!Array.isArray(messages)) return messages;
94
+ const sanitized = sanitizeToolPairs(messages);
95
+ if (sanitized !== messages) {
96
+ messages.length = 0;
97
+ messages.push(...sanitized);
98
+ }
99
+ _ensureTranscriptPairing(messages, sessionId);
100
+ return messages;
101
+ }
@@ -0,0 +1,35 @@
1
+ // Usage accumulation helpers extracted from loop.mjs.
2
+ // Normalize a provider usage payload into the canonical token/cost shape and
3
+ // fold successive deltas into a running total across loop iterations.
4
+
5
+ export function normalizeUsage(usage) {
6
+ if (!usage) return null;
7
+ const costUsd = Number(usage.costUsd);
8
+ return {
9
+ inputTokens: usage.inputTokens || 0,
10
+ outputTokens: usage.outputTokens || 0,
11
+ cachedTokens: usage.cachedTokens || 0,
12
+ cacheWriteTokens: usage.cacheWriteTokens || 0,
13
+ promptTokens: usage.promptTokens || 0,
14
+ ...(Number.isFinite(costUsd) ? { costUsd } : {}),
15
+ raw: usage.raw,
16
+ };
17
+ }
18
+
19
+ export function addUsage(total, usage) {
20
+ const delta = normalizeUsage(usage);
21
+ if (!delta) return total;
22
+ if (!total) return { ...delta };
23
+ const next = {
24
+ ...total,
25
+ inputTokens: (total.inputTokens || 0) + delta.inputTokens,
26
+ outputTokens: (total.outputTokens || 0) + delta.outputTokens,
27
+ cachedTokens: (total.cachedTokens || 0) + delta.cachedTokens,
28
+ cacheWriteTokens: (total.cacheWriteTokens || 0) + delta.cacheWriteTokens,
29
+ promptTokens: (total.promptTokens || 0) + delta.promptTokens,
30
+ };
31
+ if (delta.costUsd != null || total.costUsd != null) {
32
+ next.costUsd = (total.costUsd || 0) + (delta.costUsd || 0);
33
+ }
34
+ return next;
35
+ }