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,76 @@
1
+ /**
2
+ * src/tui/engine/notification-plan.mjs - pure runtime.onNotification delivery plan.
3
+ *
4
+ * Extracted from engine.mjs (no behavior change). These are stateless helpers
5
+ * that decide how a runtime notification envelope should be delivered to the
6
+ * TUI store (dedupe key + display/model routing). No closures over engine state.
7
+ */
8
+ import {
9
+ isStatusOnlyAgentCompletionNotification,
10
+ parseAgentJob,
11
+ parseAgentResultEnvelope,
12
+ parseBackgroundTaskEnvelope,
13
+ parseSyntheticAgentMessage,
14
+ } from './agent-envelope.mjs';
15
+ import { shortTextFingerprint } from './queue-helpers.mjs';
16
+ import { modelVisibleToolCompletionMessage } from '../../runtime/shared/tool-execution-contract.mjs';
17
+
18
+ export function notificationQueueKey(event, text, parsed) {
19
+ const meta = event?.meta && typeof event.meta === 'object' ? event.meta : {};
20
+ const synthetic = parseSyntheticAgentMessage(text);
21
+ if (synthetic?.name === 'agent' && String(synthetic.args?.type || '').toLowerCase() === 'result') {
22
+ const taskId = String(synthetic.args?.task_id || '').trim();
23
+ const executionId = String(meta.execution_id || '').trim();
24
+ const tag = String(synthetic.args?.tag || '').trim();
25
+ const resultId = taskId
26
+ ? `task:${taskId}`
27
+ : executionId
28
+ ? `exec:${executionId}`
29
+ : tag
30
+ ? `tag:${tag}:${shortTextFingerprint(synthetic.result || text)}`
31
+ : '';
32
+ const agent = String(synthetic.args?.agent || '').trim();
33
+ if (resultId || agent) return ['agent-result', resultId, agent].filter(Boolean).join(':');
34
+ }
35
+ const id = String(meta.execution_id || parsed?.taskId || '').trim();
36
+ if (!id) return '';
37
+ const type = String(meta.type || '').trim();
38
+ const status = String(meta.status || parsed?.status || '').trim();
39
+ const fallbackKind = String(text || '').split('\n', 1)[0]?.trim() || 'notification';
40
+ // Distinguish a body-carrying completion from a header-only preview that
41
+ // shares the same id/type/status. An early agent preview can arrive before
42
+ // the session is persisted (no result body); the canonical notification that
43
+ // follows DOES carry the body. Without this dimension the bodyless preview
44
+ // would claim the dedupe key and suppress the real result. A blank-line gap
45
+ // separates the task header block from the result body in the envelope.
46
+ const hasBody = /\n\s*\n[\s\S]*\S/.test(String(text || '')) ? 'b1' : 'b0';
47
+ return [id, type || fallbackKind, status, hasBody].filter(Boolean).join(':');
48
+ }
49
+
50
+ export function isExecutionNotification(event, text, parsed) {
51
+ const meta = event?.meta && typeof event.meta === 'object' ? event.meta : {};
52
+ if (meta.execution_id || meta.execution_surface) return true;
53
+ if (parseAgentResultEnvelope(text)) return true;
54
+ if (parseBackgroundTaskEnvelope(text)) return true;
55
+ return Boolean(parsed?.taskId && /^(?:agent task:|task_id:)/mi.test(String(text || '')));
56
+ }
57
+
58
+ /** Pure delivery plan for runtime.onNotification execution envelopes (tests + handler). */
59
+ export function resolveTuiRuntimeNotificationDelivery(event, text) {
60
+ const trimmed = String(text ?? '').trim();
61
+ if (!trimmed) return { action: 'ignore' };
62
+ const parsed = parseAgentJob(trimmed);
63
+ const meta = event?.meta && typeof event.meta === 'object' ? event.meta : {};
64
+ if (!isExecutionNotification(event, trimmed, parsed)) {
65
+ return { action: 'enqueue', displayText: trimmed, modelContent: trimmed };
66
+ }
67
+ if (isStatusOnlyAgentCompletionNotification(trimmed)) {
68
+ return { action: 'status-only', displayText: trimmed, modelContent: '' };
69
+ }
70
+ const modelContent = modelVisibleToolCompletionMessage(trimmed, meta);
71
+ return {
72
+ action: 'execution-ui',
73
+ displayText: trimmed,
74
+ modelContent,
75
+ };
76
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * src/tui/engine/queue-helpers.mjs - pure helpers for the prompt/notification
3
+ * queue: priority ordering, entry visibility/editability, prompt content
4
+ * flattening, session activity timestamps, and batch merging. Extracted from
5
+ * engine.mjs.
6
+ */
7
+ import {
8
+ agentJobResultText,
9
+ parseAgentJob,
10
+ parseSyntheticAgentMessage,
11
+ } from './agent-envelope.mjs';
12
+
13
+ const QUEUE_PRIORITY = { now: 0, next: 1, later: 2 };
14
+
15
+ export function queuePriorityValue(value) {
16
+ return QUEUE_PRIORITY[String(value || 'next')] ?? QUEUE_PRIORITY.next;
17
+ }
18
+
19
+ export function defaultQueuePriority(mode) {
20
+ // Queue priority defaults:
21
+ // - user/bashed prompt input defaults to `next`, so it can be attached at the
22
+ // next model-send boundary while a turn is active.
23
+ // - task notifications default to `later`, unless the caller explicitly marks
24
+ // them urgent (e.g. interactive shell stall/completion).
25
+ return mode === 'task-notification' ? 'later' : 'next';
26
+ }
27
+
28
+ export function isQueuedEntryEditable(entry) {
29
+ const mode = entry?.mode || 'prompt';
30
+ return mode !== 'task-notification' && mode !== 'pending-resume';
31
+ }
32
+
33
+ export function isQueuedEntryVisible(entry) {
34
+ // state.queued drives the user-command wait list above the prompt. Background
35
+ // task completions stay in the internal pending queue, but should never look
36
+ // like commands typed by the user while they wait to be drained.
37
+ const mode = entry?.mode || 'prompt';
38
+ if (mode === 'pending-resume') return false;
39
+ return isQueuedEntryEditable(entry);
40
+ }
41
+
42
+ export function isSlashQueuedEntry(entry) {
43
+ if (entry?.skipSlashCommands) return false;
44
+ const text = promptContentText(entry?.content ?? entry?.text ?? '');
45
+ return text.trim().startsWith('/');
46
+ }
47
+
48
+ export function firstQueueLine(text) {
49
+ return String(text || '').split('\n').map((line) => line.trim()).find(Boolean) || '';
50
+ }
51
+
52
+ export function shortTextFingerprint(text) {
53
+ const value = String(text || '').trim();
54
+ let hash = 2166136261;
55
+ for (let i = 0; i < value.length; i += 1) {
56
+ hash ^= value.charCodeAt(i);
57
+ hash = Math.imul(hash, 16777619);
58
+ }
59
+ return (hash >>> 0).toString(36);
60
+ }
61
+
62
+ export function notificationDisplayText(text) {
63
+ const parsed = parseAgentJob(text);
64
+ const result = agentJobResultText(text, parsed);
65
+ const synthetic = parseSyntheticAgentMessage(text);
66
+ return firstQueueLine(synthetic?.result || result || text) || 'agent notification';
67
+ }
68
+
69
+ export function promptContentText(content) {
70
+ if (typeof content === 'string') return content;
71
+ if (Array.isArray(content)) {
72
+ return content.map((part) => {
73
+ if (typeof part === 'string') return part;
74
+ if (part?.type === 'text') return part.text || '';
75
+ if (part?.type === 'image') return '[Image]';
76
+ return part?.text || '';
77
+ }).filter(Boolean).join('\n');
78
+ }
79
+ return String(content ?? '');
80
+ }
81
+
82
+ export function timestampMs(value) {
83
+ if (value == null || value === '') return 0;
84
+ const n = Number(value);
85
+ if (Number.isFinite(n) && n > 0) return n;
86
+ const parsed = Date.parse(String(value));
87
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
88
+ }
89
+
90
+ export function hasModelVisibleConversation(session) {
91
+ const messages = Array.isArray(session?.messages) ? session.messages : [];
92
+ return messages.some((message) => {
93
+ const role = message?.role;
94
+ if (role !== 'user' && role !== 'assistant' && role !== 'tool') return false;
95
+ const text = promptContentText(message.content).trim();
96
+ if (role === 'user' && text.startsWith('<system-reminder>')) return false;
97
+ if (role === 'assistant' && text === '.' && !Array.isArray(message.toolCalls)) return false;
98
+ return !!text || role === 'assistant' || role === 'tool';
99
+ });
100
+ }
101
+
102
+ export function sessionActivityTimestamp(session, fallback = 0) {
103
+ if (!hasModelVisibleConversation(session)) return 0;
104
+ return timestampMs(session?.lastUsedAt)
105
+ || timestampMs(session?.updatedAt)
106
+ || timestampMs(fallback);
107
+ }
108
+
109
+ export function promptDisplayText(content, options = {}) {
110
+ if (typeof options.displayText === 'string') return options.displayText;
111
+ return promptContentText(content);
112
+ }
113
+
114
+ export function mergePromptContents(entries) {
115
+ const batch = Array.isArray(entries) ? entries : [];
116
+ if (batch.every((entry) => typeof entry?.content === 'string')) {
117
+ return batch.map((entry) => entry.content).filter((text) => String(text || '').trim()).join('\n');
118
+ }
119
+ const parts = [];
120
+ for (const entry of batch) {
121
+ const content = entry?.content;
122
+ if (typeof content === 'string') {
123
+ if (content.trim()) parts.push({ type: 'text', text: content });
124
+ } else if (Array.isArray(content)) {
125
+ parts.push(...content);
126
+ }
127
+ parts.push({ type: 'text', text: '\n' });
128
+ }
129
+ while (parts.length && parts[parts.length - 1]?.type === 'text' && parts[parts.length - 1]?.text === '\n') parts.pop();
130
+ return parts.length === 1 && parts[0]?.type === 'text' ? parts[0].text : parts;
131
+ }
132
+
133
+ export function mergePastedImages(entries) {
134
+ const out = {};
135
+ for (const entry of entries || []) {
136
+ const images = entry?.pastedImages;
137
+ if (!images || typeof images !== 'object') continue;
138
+ for (const [id, image] of Object.entries(images)) {
139
+ if (image) out[id] = image;
140
+ }
141
+ }
142
+ return Object.keys(out).length > 0 ? out : null;
143
+ }
144
+
145
+ export function mergePastedTexts(entries) {
146
+ const out = {};
147
+ for (const entry of entries || []) {
148
+ const texts = entry?.pastedTexts;
149
+ if (!texts || typeof texts !== 'object') continue;
150
+ for (const [id, text] of Object.entries(texts)) {
151
+ if (text) out[id] = text;
152
+ }
153
+ }
154
+ return Object.keys(out).length > 0 ? out : null;
155
+ }
156
+
157
+ export function callCommitCallbacks(entries) {
158
+ for (const entry of entries || []) {
159
+ try { entry?.onCommitted?.(); } catch {}
160
+ }
161
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * src/tui/engine/render-timing.mjs - render-throttle timing helper.
3
+ *
4
+ * Extracted from engine.mjs (no behavior change).
5
+ *
6
+ * Ink renders through a maxFps throttle (120fps in index.jsx, ≈8.3ms). A plain
7
+ * setImmediate only yields to the event loop; if Ink already painted within the
8
+ * current throttle window, the next paint may still be queued and our following
9
+ * transcript mutation can coalesce into the same visible frame. Wait just past
10
+ * one render window when we intentionally split transcript commits for visual
11
+ * stability (preamble frame → tool-card frame).
12
+ */
13
+ export const RENDER_THROTTLE_FLUSH_MS = 12;
14
+
15
+ export const yieldToRenderer = () => new Promise((resolve) => {
16
+ setTimeout(resolve, RENDER_THROTTLE_FLUSH_MS);
17
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * src/tui/engine/session-stats.mjs - session-usage accumulator.
3
+ *
4
+ * Inlined (not imported from ui/statusline.mjs) so the engine has no static
5
+ * dependency on the vendored statusline closure. Extracted from engine.mjs.
6
+ */
7
+ export function createSessionStats() {
8
+ return {
9
+ inputTokens: 0,
10
+ outputTokens: 0,
11
+ cachedTokens: 0,
12
+ cacheWriteTokens: 0,
13
+ promptTokens: 0,
14
+ latestInputTokens: 0,
15
+ latestOutputTokens: 0,
16
+ latestCachedTokens: 0,
17
+ latestCacheWriteTokens: 0,
18
+ latestPromptTokens: 0,
19
+ currentContextTokens: 0,
20
+ costUsd: 0,
21
+ turns: 0,
22
+ };
23
+ }
24
+
25
+ function num(v) { const n = Number(v); return Number.isFinite(n) ? n : 0; }
26
+
27
+ export function applyUsageDelta(stats, delta = {}) {
28
+ if (!stats || !delta) return stats;
29
+ const inputTokens = num(delta.deltaInput);
30
+ const outputTokens = num(delta.deltaOutput);
31
+ const cachedTokens = num(delta.deltaCachedRead);
32
+ const cacheWriteTokens = num(delta.deltaCacheWrite);
33
+ const promptTokens = num(delta.deltaPrompt);
34
+ stats.inputTokens += inputTokens;
35
+ stats.outputTokens += outputTokens;
36
+ stats.cachedTokens += cachedTokens;
37
+ stats.cacheWriteTokens += cacheWriteTokens;
38
+ stats.promptTokens += promptTokens;
39
+ stats.latestInputTokens = inputTokens;
40
+ stats.latestOutputTokens = outputTokens;
41
+ stats.latestCachedTokens = cachedTokens;
42
+ stats.latestCacheWriteTokens = cacheWriteTokens;
43
+ stats.latestPromptTokens = promptTokens;
44
+ stats.costUsd += num(delta.costUsd);
45
+ return stats;
46
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * src/tui/engine/tool-approval.mjs — tool-approval queue plumbing extracted
3
+ * from createEngineSession (engine.mjs) as a dependency-injection factory.
4
+ *
5
+ * These handlers drive a small FIFO of hook approval requests and reflect the
6
+ * active one into store state (state.toolApproval). They need live session
7
+ * state, so the closure identifiers (state/set/nextId/disposed/timeout) are
8
+ * threaded through the factory argument via getters — never stale snapshots.
9
+ * Every body is the original engine.mjs logic verbatim.
10
+ */
11
+ export function createToolApproval({ getState, set, nextId, getDisposed, timeoutMs }) {
12
+ const toolApprovalQueue = [];
13
+ let activeToolApproval = null;
14
+
15
+ function normalizeToolApprovalRequest(input = {}, id = nextId()) {
16
+ const now = Date.now();
17
+ const requestTimeoutMs = timeoutMs;
18
+ return {
19
+ id,
20
+ name: String(input?.name || input?.tool_name || 'tool'),
21
+ args: input?.args ?? input?.tool_input ?? null,
22
+ cwd: input?.cwd || null,
23
+ sessionId: input?.sessionId || input?.session_id || null,
24
+ toolCallId: input?.toolCallId || input?.tool_use_id || null,
25
+ reason: String(input?.reason || input?.message || 'approval requested by hook').trim(),
26
+ requestedAt: now,
27
+ timeoutMs: requestTimeoutMs,
28
+ expiresAt: now + requestTimeoutMs,
29
+ };
30
+ }
31
+ function presentNextToolApproval() {
32
+ if (activeToolApproval || getDisposed()) return;
33
+ const entry = toolApprovalQueue.shift();
34
+ if (!entry) {
35
+ if (getState().toolApproval) set({ toolApproval: null });
36
+ return;
37
+ }
38
+ activeToolApproval = entry;
39
+ entry.timer = setTimeout(() => {
40
+ finishToolApproval(entry.id, false, 'approval timed out');
41
+ }, entry.request.timeoutMs);
42
+ entry.timer.unref?.();
43
+ set({ toolApproval: entry.request });
44
+ }
45
+ function finishToolApproval(id, approved, reason = '') {
46
+ const targetId = String(id || '');
47
+ if (activeToolApproval && activeToolApproval.id === targetId) {
48
+ const entry = activeToolApproval;
49
+ activeToolApproval = null;
50
+ if (entry.timer) clearTimeout(entry.timer);
51
+ set({ toolApproval: null });
52
+ try { entry.resolve({ approved: approved === true, reason: String(reason || '') }); } catch {}
53
+ presentNextToolApproval();
54
+ return true;
55
+ }
56
+ const index = toolApprovalQueue.findIndex((entry) => entry.id === targetId);
57
+ if (index >= 0) {
58
+ const [entry] = toolApprovalQueue.splice(index, 1);
59
+ if (entry?.timer) clearTimeout(entry.timer);
60
+ try { entry.resolve({ approved: approved === true, reason: String(reason || '') }); } catch {}
61
+ return true;
62
+ }
63
+ return false;
64
+ }
65
+ function denyAllToolApprovals(reason = 'approval cancelled') {
66
+ if (activeToolApproval) {
67
+ const entry = activeToolApproval;
68
+ activeToolApproval = null;
69
+ if (entry.timer) clearTimeout(entry.timer);
70
+ try { entry.resolve({ approved: false, reason }); } catch {}
71
+ }
72
+ while (toolApprovalQueue.length > 0) {
73
+ const entry = toolApprovalQueue.shift();
74
+ if (entry?.timer) clearTimeout(entry.timer);
75
+ try { entry.resolve({ approved: false, reason }); } catch {}
76
+ }
77
+ if (getState().toolApproval) set({ toolApproval: null });
78
+ }
79
+ function requestToolApproval(input = {}) {
80
+ if (getDisposed()) return Promise.resolve({ approved: false, reason: 'runtime disposed' });
81
+ return new Promise((resolve) => {
82
+ const id = nextId();
83
+ toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve, timer: null });
84
+ presentNextToolApproval();
85
+ });
86
+ }
87
+
88
+ return {
89
+ presentNextToolApproval,
90
+ finishToolApproval,
91
+ denyAllToolApprovals,
92
+ requestToolApproval,
93
+ };
94
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * src/tui/engine/tool-call-fields.mjs - accessors for the varied tool-call /
3
+ * tool-result id/name/arg shapes across providers. Extracted from engine.mjs.
4
+ */
5
+ export function toolCallId(call) {
6
+ return call?.id ?? call?.toolCallId ?? call?.tool_call_id ?? call?.call_id;
7
+ }
8
+
9
+ export function toolResultCallId(message) {
10
+ return message?.toolCallId
11
+ ?? message?.tool_call_id
12
+ ?? message?.tool_use_id
13
+ ?? message?.call_id
14
+ ?? message?.id;
15
+ }
16
+
17
+ export function toolCallName(call) {
18
+ return call?.name ?? call?.function?.name ?? call?.toolName ?? call?.tool_name ?? 'tool';
19
+ }
20
+
21
+ export function toolCallArgs(call) {
22
+ return call?.arguments ?? call?.args ?? call?.input ?? call?.function?.arguments;
23
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * src/tui/engine/tool-card-results.mjs — the tool-card result state machine
3
+ * (patchToolCardResult + flushToolResults) extracted from createEngineSession
4
+ * (engine.mjs) as a dependency-injection factory.
5
+ *
6
+ * These handlers own the per-turn accounting that reflects tool results into
7
+ * store items: aggregate cards, non-aggregate/legacy agent-job cards, grouped
8
+ * fallbacks, and the finalize/cancelled sweeps. They mutate live session state,
9
+ * so state/set/patchItem/markToolCallDone/updateAgentJobCard are threaded via
10
+ * the factory argument (getters/callbacks) — never stale snapshots. Every body
11
+ * is the original engine.mjs logic verbatim.
12
+ */
13
+ import { summarizeToolResult } from '../../runtime/shared/tool-surface.mjs';
14
+ import { toolResultText, toolErrorDisplay, toolGroupedDisplayFallback } from './tool-result-text.mjs';
15
+ import { toolResultCallId } from './tool-call-fields.mjs';
16
+ import { parseAgentJob, agentArgsWithResultMetadata, toolResultStatus, isErrorToolStatus } from './agent-envelope.mjs';
17
+ import {
18
+ withCancelledResultMarker,
19
+ groupedToolResultText,
20
+ aggregateRawResult,
21
+ aggregateSummaries,
22
+ assignAggregateSummaryOrder,
23
+ } from './tool-result-status.mjs';
24
+ import { formatAggregateDetail } from '../../runtime/shared/tool-surface.mjs';
25
+
26
+ export function createToolCardResults({
27
+ getState,
28
+ set,
29
+ patchItem,
30
+ markToolCallDone,
31
+ updateAgentJobCard,
32
+ agentStatusState,
33
+ }) {
34
+ function patchToolCardResult(card, message, toolGroups, done) {
35
+ if (!card || card.done) return false;
36
+ const callId = toolResultCallId(message) || card.callId;
37
+ if (callId && done.has(callId)) return false;
38
+ // Any resolving call clears its active-summary entry (keyed by the same
39
+ // callKey used at markToolCallActive; card.callId holds it for both branches).
40
+ markToolCallDone(card.callId);
41
+ // A result for this card arrived (possibly before its deferred push delay
42
+ // elapsed) — surface the card now so the patch below has a live item and the
43
+ // fast tool paints a completed card directly, no pending placeholder stage.
44
+ // ensureVisible flushes this card AND every earlier-created still-deferred
45
+ // card in order, so transcript order always matches call order.
46
+ (card.aggregate?.ensureVisible || card.ensureVisible)?.();
47
+ const rawText = toolResultText(message?.content);
48
+ const isError = message?.isError === true || message?.toolKind === 'error' || /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText));
49
+ const text = isError ? toolErrorDisplay(rawText, card?.name || 'tool') : rawText;
50
+
51
+ // Aggregate card handling — collect semantic summaries per call
52
+ const aggregate = card.aggregate;
53
+ if (aggregate && card.itemId === aggregate.itemId) {
54
+ const callRec = callId ? aggregate.calls.get(callId) : null;
55
+ if (!callRec) return false;
56
+ if (callRec.resolved) {
57
+ card.done = true;
58
+ if (callId) done.add(callId);
59
+ return false;
60
+ }
61
+ callRec.summary = !isError ? summarizeToolResult(callRec.name, callRec.args, rawText, isError) : null;
62
+ assignAggregateSummaryOrder(aggregate, callRec);
63
+ callRec.isError = isError;
64
+ callRec.resultText = text;
65
+ callRec.resolved = true;
66
+ const allCalls = [...aggregate.calls.values()];
67
+ const completed = allCalls.filter((r) => r.resolved).length;
68
+ const errors = allCalls.filter((r) => r.isError).length;
69
+ // Collapsed detail carries the merged per-call count summary
70
+ // ("512 lines, 6 matches, 3 files") so the finished card answers "how
71
+ // much" without ctrl+o. Failures keep a bare 'N Ok · N Failed' status so
72
+ // an error stays visible while collapsed.
73
+ const succeeded = completed - errors;
74
+ const detailText = errors > 0
75
+ ? (succeeded > 0 ? `${succeeded} Ok · ${errors} Failed` : `${errors} Failed`)
76
+ : formatAggregateDetail(aggregateSummaries(aggregate));
77
+ const currentItem = getState().items.find((it) => it.id === card.itemId);
78
+ const earlyCompleted = allCalls.filter((r) => r.resolved || r.completedEarly).length;
79
+ const visualCompleted = Math.max(completed, earlyCompleted, Math.min(allCalls.length, Number(currentItem?.completedCount || 0)));
80
+ const rawResult = aggregateRawResult(allCalls);
81
+ // The numbered+labelled raw (rawResult) is preserved for ctrl+o expansion.
82
+ const displayDetail = detailText;
83
+ patchItem(card.itemId, {
84
+ result: displayDetail,
85
+ text: displayDetail,
86
+ rawResult: rawResult || null,
87
+ isError: errors > 0,
88
+ errorCount: errors,
89
+ count: allCalls.length,
90
+ completedCount: visualCompleted,
91
+ completedAt: Number(currentItem?.completedAt) || Date.now(),
92
+ });
93
+ card.done = true;
94
+ if (callId) done.add(callId);
95
+ return true;
96
+ }
97
+
98
+ // Non-aggregate (legacy agent-job cards, etc.)
99
+ const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, results: [] };
100
+ group.completed = Math.min(group.count, group.completed + 1);
101
+ group.errors += isError ? 1 : 0;
102
+ group.results.push({ text, isError });
103
+ toolGroups.set(card.itemId, group);
104
+ const resultText = groupedToolResultText(group);
105
+ const displayResult = toolGroupedDisplayFallback(resultText, text, rawText);
106
+ const patch = {
107
+ result: displayResult,
108
+ text: displayResult,
109
+ isError: group.errors > 0,
110
+ errorCount: group.errors,
111
+ count: group.count,
112
+ completedCount: group.completed,
113
+ completedAt: Date.now(),
114
+ };
115
+ if (group.count <= 1) {
116
+ const body = String(text || rawText || '').trim();
117
+ if (body) patch.rawResult = text || rawText;
118
+ const parsedAgent = parseAgentJob(rawText);
119
+ if (parsedAgent) {
120
+ patch.args = agentArgsWithResultMetadata(getState().items.find((it) => it.id === card.itemId)?.args, parsedAgent);
121
+ set(agentStatusState({ force: true }));
122
+ }
123
+ }
124
+ patchItem(card.itemId, patch);
125
+ if (group.count <= 1) updateAgentJobCard(card.itemId, rawText, isError);
126
+ card.done = true;
127
+ if (callId) done.add(callId);
128
+ return true;
129
+ }
130
+
131
+ const flushToolResults = (messages, toolCards, cardByCallId, toolGroups, done, { finalize = false, cancelled = false } = {}) => {
132
+ const results = [];
133
+ for (const m of messages || []) {
134
+ if (!m || m.role !== 'tool') continue;
135
+ const callId = toolResultCallId(m);
136
+ results.push({ message: m, callId, used: false });
137
+ if (!callId || done.has(callId)) continue;
138
+ const card = cardByCallId.get(callId);
139
+ if (patchToolCardResult(card, m, toolGroups, done)) {
140
+ results[results.length - 1].used = true;
141
+ }
142
+ }
143
+
144
+ const openCards = (toolCards || []).filter((card) => !card.done);
145
+ if (openCards.length === 0) return;
146
+
147
+ const unusedResults = results.filter((result) => !result.used);
148
+ const fallbackResults = unusedResults.slice(-openCards.length);
149
+ for (let i = 0; i < fallbackResults.length; i++) {
150
+ const card = openCards[i];
151
+ const result = fallbackResults[i];
152
+ if (!card || !result || card.done) continue;
153
+ if (patchToolCardResult(card, result.message, toolGroups, done)) {
154
+ if (result.callId) done.add(result.callId);
155
+ result.used = true;
156
+ }
157
+ }
158
+
159
+ if (!finalize) return;
160
+ for (const card of toolCards || []) {
161
+ if (card.done) continue;
162
+ // Finalize must surface any still-deferred card before patching its result
163
+ // so the completed/cancelled card is never silently dropped.
164
+ (card.aggregate?.ensureVisible || card.ensureVisible)?.();
165
+ // Aggregate finalize — mark any remaining calls as done
166
+ const aggregate = card.aggregate;
167
+ if (aggregate && card.itemId === aggregate.itemId) {
168
+ const allCalls = [...aggregate.calls.values()];
169
+ // Never let a call that truly never resolved be presented as a real
170
+ // completion. Stamp it resolved so completedCount reflects an honest
171
+ // (if degenerate) accounting instead of manufacturing success out of
172
+ // a call that never came back. A record already marked completedEarly
173
+ // (via __earlyNotify) already carries a real isError/resultText/summary
174
+ // from its actual result — preserve those; only blank-fill for calls
175
+ // truly never heard from (no completedEarly, no resolved).
176
+ for (const rec of allCalls) {
177
+ if (rec.resolved) continue;
178
+ rec.resolved = true;
179
+ if (!rec.completedEarly) {
180
+ rec.isError = false;
181
+ rec.resultText = rec.resultText || '';
182
+ }
183
+ }
184
+ const completed = allCalls.filter((r) => r.resolved).length;
185
+ const totalCompleted = completed;
186
+ const errors = allCalls.filter((r) => r.isError).length;
187
+ const succeeded = completed - errors;
188
+ const rawResult = aggregateRawResult(allCalls);
189
+ // Collapsed detail carries the merged per-call count summary; failures
190
+ // keep a bare 'N Ok · N Failed' status. Raw is kept for ctrl+o.
191
+ let displayDetail = errors > 0
192
+ ? (succeeded > 0 ? `${succeeded} Ok · ${errors} Failed` : `${errors} Failed`)
193
+ : formatAggregateDetail(aggregateSummaries(aggregate));
194
+ if (cancelled) {
195
+ // Cancelled aggregates MUST keep the [status: cancelled] marker on the
196
+ // result so terminalStatus parsing resolves to 'cancelled'. Only normal
197
+ // completions drop the summary; cancelled ones prepend the marker.
198
+ const currentItem = getState().items.find((it) => it.id === card.itemId);
199
+ displayDetail = withCancelledResultMarker(displayDetail, currentItem);
200
+ }
201
+ patchItem(card.itemId, {
202
+ result: displayDetail,
203
+ text: displayDetail,
204
+ rawResult: rawResult || null,
205
+ isError: errors > 0,
206
+ errorCount: errors,
207
+ count: allCalls.length,
208
+ completedCount: totalCompleted,
209
+ completedAt: Date.now(),
210
+ });
211
+ for (const sibling of toolCards || []) {
212
+ if (sibling.itemId !== card.itemId) continue;
213
+ sibling.done = true;
214
+ if (sibling.callId) done.add(sibling.callId);
215
+ }
216
+ continue;
217
+ }
218
+ // Non-aggregate finalize
219
+ const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, results: [] };
220
+ group.completed = Math.min(group.count, group.completed + 1);
221
+ toolGroups.set(card.itemId, group);
222
+ let resultText = groupedToolResultText(group);
223
+ if (cancelled) {
224
+ const currentItem = getState().items.find((it) => it.id === card.itemId);
225
+ resultText = withCancelledResultMarker(resultText, currentItem);
226
+ }
227
+ patchItem(card.itemId, { result: resultText, text: resultText, isError: group.errors > 0, errorCount: group.errors, count: group.count, completedCount: group.completed, completedAt: Date.now() });
228
+ card.done = true;
229
+ if (card.callId) done.add(card.callId);
230
+ }
231
+ };
232
+
233
+ return { patchToolCardResult, flushToolResults };
234
+ }