mixdog 0.9.3 → 0.9.5

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 (382) hide show
  1. package/README.md +112 -38
  2. package/package.json +10 -3
  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/r5-orchestrated-task.json +7 -0
  7. package/scripts/bench/review-tasks.json +20 -0
  8. package/scripts/bench/round-codex.json +114 -0
  9. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  10. package/scripts/bench/round-mixdog-lead.json +269 -0
  11. package/scripts/bench/round-mixdog.json +126 -0
  12. package/scripts/bench/round-r10-bigsample.json +679 -0
  13. package/scripts/bench/round-r11-codexalign.json +257 -0
  14. package/scripts/bench/round-r13-clientmeta.json +464 -0
  15. package/scripts/bench/round-r14-betafeatures.json +466 -0
  16. package/scripts/bench/round-r15-fulldefault.json +462 -0
  17. package/scripts/bench/round-r16-sessionid.json +466 -0
  18. package/scripts/bench/round-r17-wirebytes.json +456 -0
  19. package/scripts/bench/round-r18-prewarm.json +468 -0
  20. package/scripts/bench/round-r19-clean.json +472 -0
  21. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  22. package/scripts/bench/round-r21-delta-retry.json +473 -0
  23. package/scripts/bench/round-r22-full-probe.json +693 -0
  24. package/scripts/bench/round-r23-itemprobe.json +701 -0
  25. package/scripts/bench/round-r24-shapefix.json +677 -0
  26. package/scripts/bench/round-r25-serial.json +464 -0
  27. package/scripts/bench/round-r26-parallel3.json +671 -0
  28. package/scripts/bench/round-r27-parallel10.json +894 -0
  29. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  30. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  31. package/scripts/bench/round-r30-instid.json +253 -0
  32. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  33. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  34. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  35. package/scripts/bench/round-r34-orchestrated.json +120 -0
  36. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  37. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  38. package/scripts/bench/round-r4-codex.json +114 -0
  39. package/scripts/bench/round-r4-mixed.json +225 -0
  40. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  41. package/scripts/bench/round-r6-codex.json +114 -0
  42. package/scripts/bench/round-r6-solo.json +257 -0
  43. package/scripts/bench/round-r7-full.json +254 -0
  44. package/scripts/bench/round-r8-fulldefault.json +255 -0
  45. package/scripts/bench-run.mjs +251 -32
  46. package/scripts/freevar-smoke.mjs +95 -0
  47. package/scripts/internal-comms-bench.mjs +3 -4
  48. package/scripts/internal-comms-smoke.mjs +10 -9
  49. package/scripts/model-catalog-audit.mjs +209 -0
  50. package/scripts/model-list-sanitize-test.mjs +37 -0
  51. package/scripts/mouse-probe.mjs +45 -0
  52. package/scripts/output-style-bench.mjs +13 -6
  53. package/scripts/output-style-smoke.mjs +4 -4
  54. package/scripts/provider-toolcall-test.mjs +7 -3
  55. package/scripts/recall-bench.mjs +76 -13
  56. package/scripts/recall-quality-cases.json +12 -0
  57. package/scripts/recall-usecase-cases.json +18 -0
  58. package/scripts/session-bench.mjs +152 -6
  59. package/scripts/tool-smoke.mjs +25 -65
  60. package/scripts/tui-render-smoke.mjs +90 -0
  61. package/scripts/webhook-smoke.mjs +208 -0
  62. package/src/agents/debugger/AGENT.md +4 -1
  63. package/src/agents/heavy-worker/AGENT.md +9 -8
  64. package/src/agents/maintainer/AGENT.md +4 -0
  65. package/src/agents/reviewer/AGENT.md +2 -1
  66. package/src/agents/scheduler-task/AGENT.md +2 -3
  67. package/src/agents/webhook-handler/AGENT.md +2 -3
  68. package/src/agents/worker/AGENT.md +10 -7
  69. package/src/app.mjs +12 -1
  70. package/src/headless-role.mjs +7 -1
  71. package/src/lib/rules-builder.cjs +4 -0
  72. package/src/mixdog-session-runtime.mjs +647 -2056
  73. package/src/output-styles/default.md +30 -9
  74. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  75. package/src/output-styles/minimal.md +8 -6
  76. package/src/output-styles/simple.md +21 -7
  77. package/src/rules/agent/00-common.md +6 -3
  78. package/src/rules/agent/30-explorer.md +16 -5
  79. package/src/rules/lead/01-general.md +5 -5
  80. package/src/rules/lead/lead-brief.md +15 -0
  81. package/src/rules/lead/lead-tool.md +6 -15
  82. package/src/rules/shared/01-tool.md +17 -21
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  84. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  85. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  86. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  87. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  88. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  89. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  90. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  91. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  92. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  93. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  95. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  97. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  100. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  101. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  102. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  103. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
  104. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  109. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  110. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
  111. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  112. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
  113. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
  117. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  119. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  120. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  121. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  122. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  123. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  124. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  125. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  126. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  127. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  128. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  129. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
  132. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  133. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  134. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  135. package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
  136. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  137. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  138. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  139. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  140. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  143. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  144. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  145. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  146. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  147. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
  149. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  152. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  155. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  157. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  158. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  159. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  161. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  163. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  164. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  165. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  166. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  167. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  168. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  169. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  170. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  171. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  172. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  173. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  174. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  175. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  176. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  177. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  178. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  179. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  180. package/src/runtime/channels/backends/discord.mjs +27 -318
  181. package/src/runtime/channels/backends/telegram.mjs +8 -12
  182. package/src/runtime/channels/index.mjs +247 -701
  183. package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
  184. package/src/runtime/channels/lib/config.mjs +37 -149
  185. package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
  186. package/src/runtime/channels/lib/event-queue.mjs +78 -13
  187. package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
  188. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  189. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  190. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  191. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  192. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  193. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  194. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  195. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  196. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  197. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  198. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  199. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  200. package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
  201. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  202. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  203. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  204. package/src/runtime/channels/lib/webhook.mjs +43 -616
  205. package/src/runtime/channels/tool-defs.mjs +11 -130
  206. package/src/runtime/memory/index.mjs +210 -1948
  207. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  208. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  209. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  210. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  211. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  212. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  213. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  214. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  215. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  216. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  217. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  218. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  219. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  220. package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
  221. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  222. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  223. package/src/runtime/memory/lib/query-handlers.mjs +802 -0
  224. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  225. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  226. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  227. package/src/runtime/memory/tool-defs.mjs +5 -13
  228. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  229. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  230. package/src/runtime/search/lib/web-tools.mjs +24 -602
  231. package/src/runtime/shared/atomic-file.mjs +26 -1
  232. package/src/runtime/shared/config.mjs +14 -4
  233. package/src/runtime/shared/launcher-control.mjs +2 -2
  234. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  235. package/src/runtime/shared/schedules-store.mjs +13 -3
  236. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  237. package/src/runtime/shared/tool-primitives.mjs +308 -0
  238. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  239. package/src/runtime/shared/tool-surface.mjs +80 -898
  240. package/src/runtime/shared/transcript-writer.mjs +23 -0
  241. package/src/runtime/shared/update-checker.mjs +7 -4
  242. package/src/session-runtime/config-helpers.mjs +119 -2
  243. package/src/session-runtime/config-lifecycle.mjs +232 -0
  244. package/src/session-runtime/cwd-plugins.mjs +226 -0
  245. package/src/session-runtime/mcp-glue.mjs +177 -0
  246. package/src/session-runtime/model-recency.mjs +111 -0
  247. package/src/session-runtime/native-search.mjs +247 -0
  248. package/src/session-runtime/output-styles.mjs +11 -9
  249. package/src/session-runtime/prewarm.mjs +142 -0
  250. package/src/session-runtime/provider-models.mjs +278 -0
  251. package/src/session-runtime/provider-usage.mjs +120 -0
  252. package/src/session-runtime/quick-model-rows.mjs +205 -0
  253. package/src/session-runtime/quick-search-models.mjs +47 -0
  254. package/src/session-runtime/session-hooks.mjs +93 -0
  255. package/src/session-runtime/settings-api.mjs +352 -0
  256. package/src/session-runtime/tool-catalog.mjs +29 -29
  257. package/src/session-runtime/tool-defs.mjs +84 -0
  258. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  259. package/src/session-runtime/workflow.mjs +1 -1
  260. package/src/standalone/agent-tool/helpers.mjs +237 -0
  261. package/src/standalone/agent-tool/notify.mjs +107 -0
  262. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  263. package/src/standalone/agent-tool/render.mjs +152 -0
  264. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  265. package/src/standalone/agent-tool.mjs +138 -669
  266. package/src/standalone/channel-admin.mjs +102 -90
  267. package/src/standalone/channel-worker.mjs +4 -7
  268. package/src/standalone/explore-tool.mjs +64 -14
  269. package/src/standalone/hook-bus/config.mjs +207 -0
  270. package/src/standalone/hook-bus/constants.mjs +90 -0
  271. package/src/standalone/hook-bus/handlers.mjs +481 -0
  272. package/src/standalone/hook-bus/payload.mjs +31 -0
  273. package/src/standalone/hook-bus/rules.mjs +77 -0
  274. package/src/standalone/hook-bus.mjs +77 -870
  275. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  276. package/src/standalone/opencode-go-login.mjs +5 -1
  277. package/src/standalone/provider-admin.mjs +1 -16
  278. package/src/standalone/usage-dashboard.mjs +3 -1
  279. package/src/tui/App.jsx +1059 -8110
  280. package/src/tui/app/app-format.mjs +213 -0
  281. package/src/tui/app/channel-pickers.mjs +508 -0
  282. package/src/tui/app/clipboard.mjs +67 -0
  283. package/src/tui/app/core-memory-picker.mjs +210 -0
  284. package/src/tui/app/extension-pickers.mjs +506 -0
  285. package/src/tui/app/input-parsers.mjs +193 -0
  286. package/src/tui/app/maintenance-pickers.mjs +356 -0
  287. package/src/tui/app/model-options.mjs +334 -0
  288. package/src/tui/app/model-picker.mjs +365 -0
  289. package/src/tui/app/onboarding-steps.mjs +400 -0
  290. package/src/tui/app/project-picker.mjs +247 -0
  291. package/src/tui/app/provider-setup-picker.mjs +580 -0
  292. package/src/tui/app/resume-picker.mjs +55 -0
  293. package/src/tui/app/route-pickers.mjs +419 -0
  294. package/src/tui/app/settings-picker.mjs +489 -0
  295. package/src/tui/app/slash-commands.mjs +101 -0
  296. package/src/tui/app/slash-dispatch.mjs +427 -0
  297. package/src/tui/app/text-layout.mjs +46 -0
  298. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  299. package/src/tui/app/transcript-window.mjs +677 -0
  300. package/src/tui/app/use-mouse-input.mjs +460 -0
  301. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  302. package/src/tui/app/use-transcript-scroll.mjs +512 -0
  303. package/src/tui/app/use-transcript-window.mjs +607 -0
  304. package/src/tui/components/ConfirmBar.jsx +10 -7
  305. package/src/tui/components/Picker.jsx +64 -15
  306. package/src/tui/components/PromptInput.jsx +33 -102
  307. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  308. package/src/tui/components/StatusLine.jsx +69 -15
  309. package/src/tui/components/TextEntryPanel.jsx +11 -0
  310. package/src/tui/components/ToolExecution.jsx +52 -594
  311. package/src/tui/components/TranscriptItem.jsx +105 -0
  312. package/src/tui/components/UsagePanel.jsx +18 -4
  313. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  314. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  315. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  316. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  317. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  318. package/src/tui/display-width.mjs +20 -3
  319. package/src/tui/dist/index.mjs +13553 -12384
  320. package/src/tui/engine/agent-job-feed.mjs +133 -0
  321. package/src/tui/engine/notification-plan.mjs +76 -0
  322. package/src/tui/engine/render-timing.mjs +17 -0
  323. package/src/tui/engine/tool-approval.mjs +94 -0
  324. package/src/tui/engine/tool-card-results.mjs +234 -0
  325. package/src/tui/engine/tool-result-status.mjs +135 -0
  326. package/src/tui/engine.mjs +170 -574
  327. package/src/tui/figures.mjs +5 -0
  328. package/src/tui/index.jsx +65 -1
  329. package/src/tui/input-editing.mjs +2 -2
  330. package/src/tui/markdown/format-token.mjs +4 -1
  331. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  332. package/src/tui/theme.mjs +6 -0
  333. package/src/ui/statusline-agents.mjs +213 -0
  334. package/src/ui/statusline-format.mjs +146 -0
  335. package/src/ui/statusline-segments.mjs +148 -0
  336. package/src/ui/statusline.mjs +77 -501
  337. package/src/ui/tool-card.mjs +0 -1
  338. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  339. package/src/workflows/default/WORKFLOW.md +16 -18
  340. package/src/workflows/sequential/WORKFLOW.md +16 -18
  341. package/vendor/ink/build/display-width.js +19 -3
  342. package/vendor/ink/build/ink.js +112 -7
  343. package/vendor/ink/build/log-update.js +17 -3
  344. package/vendor/ink/build/wrap-text.js +125 -0
  345. package/scripts/_test-folder-dialog.mjs +0 -30
  346. package/scripts/fix-brief-fn.mjs +0 -35
  347. package/scripts/fix-format-tool-surface.mjs +0 -24
  348. package/scripts/fix-tool-exec-visible.mjs +0 -42
  349. package/scripts/patch-agent-brief.mjs +0 -48
  350. package/scripts/patch-app.mjs +0 -21
  351. package/scripts/patch-app2.mjs +0 -18
  352. package/scripts/patch-dist-brief.mjs +0 -96
  353. package/scripts/patch-tool-exec.mjs +0 -70
  354. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  355. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  356. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  357. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  358. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  359. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  360. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  361. package/src/runtime/channels/lib/holidays.mjs +0 -138
  362. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  363. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  364. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  365. package/src/standalone/agent-task-status.test.mjs +0 -76
  366. package/src/tui/components/tool-output-format.test.mjs +0 -399
  367. package/src/tui/display-width.test.mjs +0 -35
  368. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  369. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  370. package/src/tui/input-editing.selection.test.mjs +0 -75
  371. package/src/tui/markdown/format-token.test.mjs +0 -354
  372. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  373. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  374. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  375. package/src/tui/paste-fix.test.mjs +0 -119
  376. package/src/tui/prompt-history-store.test.mjs +0 -52
  377. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  378. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  379. package/src/ui/markdown.test.mjs +0 -70
  380. package/src/ui/statusline-context-label.test.mjs +0 -15
  381. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  382. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,406 @@
1
+ // ── Runtime liveness map ──────────────────────────────────────────────
2
+ // In-memory only. Tracks per-session stage + stream heartbeat so agent type=list
3
+ // can surface whether a session is actually alive vs stuck. Never persisted —
4
+ // heartbeats would otherwise churn the session JSON on every SSE delta.
5
+ //
6
+ // Extracted from manager.mjs (pass-3). The Map and its timer companion are
7
+ // module-level singletons — preserving the baseline's single-process shape.
8
+ // manager.mjs owns askSession's controller/generation lifecycle and calls the
9
+ // accessors below via imports; a small set of accessors that need the session
10
+ // store / provider registry are injected through configureRuntimeLiveness() to
11
+ // avoid a circular import back into manager.mjs.
12
+ //
13
+ // Entry shape: {
14
+ // stage, lastStreamDeltaAt, lastToolCall, lastError, updatedAt,
15
+ // controller?: AbortController, // set while an ask is in flight
16
+ // generation?: number, // snapshot taken at ask start
17
+ // closed?: boolean, // flipped by closeSession()
18
+ // }
19
+ import { createAbortController } from '../../../../shared/abort-controller.mjs';
20
+ import { publishHeartbeat, deleteHeartbeat } from '../store.mjs';
21
+ import { DEFAULT_ACTIVITY_HEARTBEAT_MS } from '../../stall-policy.mjs';
22
+ import {
23
+ configureUsageMetricsRuntime,
24
+ dropMetricSeenState,
25
+ bumpUsageMetricsTurnId,
26
+ } from './usage-metrics.mjs';
27
+
28
+ const HEARTBEAT_THROTTLE_MS = 60_000; // 60s
29
+
30
+ const _runtimeState = new Map();
31
+ const _toolActivityHeartbeats = new Map();
32
+ const VALID_STAGES = new Set([
33
+ 'connecting', 'requesting', 'streaming', 'tool_running', 'idle', 'error', 'done', 'cancelling',
34
+ ]);
35
+
36
+ // Injected deps that would otherwise pull manager.mjs's store/provider surface
37
+ // back into this module (circular). Wired once from manager.mjs at load time.
38
+ let _deps = {
39
+ loadSession: () => null,
40
+ saveSessionAsync: async () => {},
41
+ };
42
+ export function configureRuntimeLiveness(deps = {}) {
43
+ _deps = { ..._deps, ...deps };
44
+ }
45
+
46
+ export function _touchRuntime(id) {
47
+ let entry = _runtimeState.get(id);
48
+ if (!entry) {
49
+ entry = { stage: 'idle', lastStreamDeltaAt: null, lastToolCall: null, lastError: null, updatedAt: Date.now() };
50
+ _runtimeState.set(id, entry);
51
+ }
52
+ return entry;
53
+ }
54
+
55
+ export function _stopToolActivityHeartbeat(id) {
56
+ if (!id) return;
57
+ const timer = _toolActivityHeartbeats.get(id);
58
+ if (!timer) return;
59
+ try { clearInterval(timer); } catch { /* ignore */ }
60
+ _toolActivityHeartbeats.delete(id);
61
+ }
62
+
63
+ function _touchSessionActivityProgress(id) {
64
+ const entry = _runtimeState.get(id);
65
+ if (!entry || entry.closed || entry.controller?.signal?.aborted) return;
66
+ if (entry.stage !== 'tool_running') return;
67
+ const now = Date.now();
68
+ entry.lastProgressAt = now;
69
+ entry.updatedAt = now;
70
+ publishHeartbeat(id, now);
71
+ }
72
+
73
+ function _startToolActivityHeartbeat(id) {
74
+ _stopToolActivityHeartbeat(id);
75
+ if (!(DEFAULT_ACTIVITY_HEARTBEAT_MS > 0)) return;
76
+ const timer = setInterval(() => _touchSessionActivityProgress(id), DEFAULT_ACTIVITY_HEARTBEAT_MS);
77
+ if (typeof timer.unref === 'function') timer.unref();
78
+ _toolActivityHeartbeats.set(id, timer);
79
+ }
80
+
81
+ export function updateSessionStage(id, stage) {
82
+ if (!id || !VALID_STAGES.has(stage)) return;
83
+ const entry = _touchRuntime(id);
84
+ const now = Date.now();
85
+ entry.stage = stage;
86
+ if (stage === 'connecting' || stage === 'requesting') {
87
+ entry.modelRequestStartedAt = now;
88
+ }
89
+ entry.lastProgressAt = now;
90
+ entry.updatedAt = now;
91
+ if (stage !== 'tool_running') _stopToolActivityHeartbeat(id);
92
+ }
93
+
94
+ /**
95
+ * Reset heartbeat-visible fields for a new ask. Preserves controller/generation/
96
+ * closed (lifecycle) but clears the previous run's streaming state so stale
97
+ * lastToolCall / lastStreamDeltaAt from the previous ask don't leak into the
98
+ * new one.
99
+ */
100
+ export function markSessionAskStart(id) {
101
+ if (!id) return;
102
+ _stopToolActivityHeartbeat(id);
103
+ const entry = _touchRuntime(id);
104
+ entry.usageMetricsTurnIncremental = false;
105
+ const sessionForTurn = entry.session ?? _deps.loadSession(id);
106
+ if (sessionForTurn) bumpUsageMetricsTurnId(sessionForTurn);
107
+ entry.stage = 'connecting';
108
+ entry.lastStreamDeltaAt = null;
109
+ entry.lastToolCall = null;
110
+ entry.toolStartedAt = null;
111
+ entry.lastError = null;
112
+ // A new ask starts a fresh turn lifecycle — clear any stale empty-final
113
+ // classification from the prior turn so inspectBridgeEntry doesn't keep
114
+ // short-circuiting to 'empty-synthesis' (which would disable stall
115
+ // detection for the entire new turn).
116
+ entry.emptyFinal = false;
117
+ entry.emptyFinalAt = null;
118
+ // askStartedAt is the watchdog's fallback reference when a session
119
+ // hangs before any stream delta arrives. Without it, a provider that
120
+ // never returns a first token would stall forever because the watchdog
121
+ // keys solely on lastStreamDeltaAt.
122
+ const now = Date.now();
123
+ entry.askStartedAt = now;
124
+ entry.modelRequestStartedAt = now;
125
+ entry.lastProgressAt = now;
126
+ entry.updatedAt = now;
127
+ // Publish heartbeat immediately so the status aggregator picks the
128
+ // session up in the connecting / requesting window. Without this the
129
+ // .hb file only landed on the first stream chunk — producing a 3–10s
130
+ // (xhigh: 30s+) invisible gap where agent sessions ran but the CC
131
+ // statusline showed no maintenance/agent badge. STREAM_FRESH_MS (5 min)
132
+ // still drops a session whose provider truly never returns a chunk;
133
+ // markSessionStreamDelta keeps refreshing once chunks arrive.
134
+ publishHeartbeat(id, now);
135
+ }
136
+ export async function markSessionStreamDelta(id) {
137
+ if (!id) return;
138
+ // Non-creating lookup: a live ask ALWAYS has a runtime entry (markSessionAskStart
139
+ // creates it before streaming begins). _touchRuntime would instead resurrect a
140
+ // blank entry — and closeSession()/idle-sweep clear _runtimeState on a deferred
141
+ // tick while a detached provider stream may still be trickling deltas. A delta
142
+ // arriving after that clear must NOT re-create an entry or it would republish the
143
+ // .hb heartbeat that markSessionClosed deleted, orphaning a dead session's
144
+ // heartbeat indefinitely (the disk tombstone blocks ask resumption but not this
145
+ // path). Skip a missing, tombstoned, or aborted entry — never refresh liveness.
146
+ const entry = _runtimeState.get(id);
147
+ if (!entry || entry.closed || entry.controller?.signal?.aborted) return;
148
+ _stopToolActivityHeartbeat(id);
149
+ const now = Date.now();
150
+ entry.lastStreamDeltaAt = now;
151
+ entry.lastProgressAt = now;
152
+ // Only promote to 'streaming' if we were in a pre-stream stage; never downgrade
153
+ // mid-tool (tool_running has its own delta source if the tool streams back).
154
+ if (entry.stage === 'connecting' || entry.stage === 'requesting') {
155
+ entry.stage = 'streaming';
156
+ }
157
+ // Lightweight heartbeat (≤5s self-throttled) for the status aggregator.
158
+ // Disk-side session.lastHeartbeatAt below is the heavy 60s zombie-reaper
159
+ // signal; the .hb file is the fast fresh-session signal consumed by the
160
+ // status line.
161
+ publishHeartbeat(id, now);
162
+ const session = entry.session;
163
+ if (session && now - (session.lastHeartbeatAt || 0) > HEARTBEAT_THROTTLE_MS) {
164
+ session.lastHeartbeatAt = now;
165
+ await _deps.saveSessionAsync(session, { expectedGeneration: session.generation });
166
+ }
167
+ entry.updatedAt = now;
168
+ }
169
+ export function markSessionToolCall(id, toolName) {
170
+ if (!id) return;
171
+ const entry = _touchRuntime(id);
172
+ entry.stage = 'tool_running';
173
+ entry.lastToolCall = toolName || null;
174
+ entry.toolStartedAt = Date.now();
175
+ entry.lastProgressAt = entry.toolStartedAt;
176
+ entry.updatedAt = entry.toolStartedAt;
177
+ publishHeartbeat(id, entry.toolStartedAt);
178
+ _startToolActivityHeartbeat(id);
179
+ }
180
+ // Parent AbortSignal listeners are dropped on askSession unwind (finally /
181
+ // terminal return) and on error/cancel/close — not in markSessionDone, which
182
+ // also runs between queued follow-up turns within one ask.
183
+ export function markSessionDone(id, { empty = false } = {}) {
184
+ if (!id) return;
185
+ _stopToolActivityHeartbeat(id);
186
+ const entry = _touchRuntime(id);
187
+ entry.stage = 'done';
188
+ entry.lastError = null;
189
+ entry.askStartedAt = null;
190
+ entry.toolStartedAt = null;
191
+ // Non-empty completion: drop any stale empty-final flag so a subsequent
192
+ // ask on the same reusable runtime entry starts clean. Empty-final
193
+ // completions preserve the flag (set by markSessionEmptyFinal just prior).
194
+ if (!empty) {
195
+ entry.emptyFinal = false;
196
+ entry.emptyFinalAt = null;
197
+ }
198
+ const doneTs = Date.now();
199
+ entry.doneAt = doneTs;
200
+ entry.lastProgressAt = doneTs;
201
+ entry.updatedAt = doneTs;
202
+ // Terminal stage — drop the heartbeat so the status badge releases
203
+ // immediately. A subsequent ask on the same session re-publishes via
204
+ // markSessionStreamDelta on the first chunk.
205
+ deleteHeartbeat(id);
206
+ }
207
+ // Tag a session as having completed with empty final synthesis (no
208
+ // content/reasoning). Distinct from `markSessionDone`: still a success
209
+ // (no abort), but the stall watchdog and post-mortem tools can
210
+ // distinguish "finished empty" from "finished with content" without
211
+ // mistaking the silence for a stall.
212
+ export function markSessionEmptyFinal(id) {
213
+ if (!id) return;
214
+ const entry = _touchRuntime(id);
215
+ entry.emptyFinal = true;
216
+ entry.emptyFinalAt = Date.now();
217
+ }
218
+ export function markSessionError(id, msg) {
219
+ if (!id) return;
220
+ _stopToolActivityHeartbeat(id);
221
+ const entry = _touchRuntime(id);
222
+ entry.stage = 'error';
223
+ entry.lastError = msg ? String(msg).slice(0, 200) : null;
224
+ entry.askStartedAt = null;
225
+ entry.toolStartedAt = null;
226
+ // Error path is a non-empty completion (we have an error message, not a
227
+ // silent empty final). Clear the flag so the next ask starts clean.
228
+ entry.emptyFinal = false;
229
+ entry.emptyFinalAt = null;
230
+ const errTs = Date.now();
231
+ entry.doneAt = errTs;
232
+ entry.lastProgressAt = errTs;
233
+ entry.updatedAt = errTs;
234
+ deleteHeartbeat(id);
235
+ _unlinkParentAbortListener(entry);
236
+ }
237
+ export function markSessionCancelled(id) {
238
+ if (!id) return;
239
+ _stopToolActivityHeartbeat(id);
240
+ const entry = _touchRuntime(id);
241
+ entry.stage = 'done';
242
+ entry.lastError = null;
243
+ entry.askStartedAt = null;
244
+ entry.toolStartedAt = null;
245
+ entry.emptyFinal = false;
246
+ entry.emptyFinalAt = null;
247
+ const doneTs = Date.now();
248
+ entry.doneAt = doneTs;
249
+ entry.lastProgressAt = doneTs;
250
+ entry.updatedAt = doneTs;
251
+ deleteHeartbeat(id);
252
+ _unlinkParentAbortListener(entry);
253
+ }
254
+ export function getSessionRuntime(id) {
255
+ return id ? (_runtimeState.get(id) || null) : null;
256
+ }
257
+
258
+ const _COMPACTION_BLOCKED_STAGES = new Set([
259
+ 'connecting', 'requesting', 'streaming', 'tool_running', 'cancelling',
260
+ ]);
261
+
262
+ export function isSessionCompactionBlocked(sessionId) {
263
+ if (!sessionId) return false;
264
+ const entry = _runtimeState.get(sessionId);
265
+ if (!entry || entry.closed === true) return false;
266
+ if (entry.controller && !entry.controller.signal?.aborted) return true;
267
+ return _COMPACTION_BLOCKED_STAGES.has(entry.stage);
268
+ }
269
+
270
+ export function getSessionProgressSnapshot(sessionId) {
271
+ const entry = _runtimeState.get(sessionId);
272
+ if (!entry) return null;
273
+ const askStartedAt = entry.askStartedAt || 0;
274
+ const modelRequestStartedAt = entry.modelRequestStartedAt || askStartedAt;
275
+ const firstActivityAt = Math.max(
276
+ entry.lastStreamDeltaAt || 0,
277
+ entry.toolStartedAt || 0,
278
+ );
279
+ const stage = entry.stage || 'idle';
280
+ const waitingForFirstActivity = Boolean(
281
+ modelRequestStartedAt
282
+ && (stage === 'connecting' || stage === 'requesting')
283
+ && firstActivityAt <= modelRequestStartedAt
284
+ );
285
+ return {
286
+ stage,
287
+ askStartedAt,
288
+ modelRequestStartedAt,
289
+ firstActivityAt,
290
+ lastStreamDeltaAt: entry.lastStreamDeltaAt || 0,
291
+ toolStartedAt: entry.toolStartedAt || 0,
292
+ lastProgressAt: entry.lastProgressAt || 0,
293
+ updatedAt: entry.updatedAt || 0,
294
+ hasFirstActivity: Boolean(firstActivityAt && (!askStartedAt || firstActivityAt >= askStartedAt)),
295
+ waitingForFirstActivity,
296
+ };
297
+ }
298
+
299
+ /**
300
+ * Iterate all active session runtimes. Used by the stream watchdog.
301
+ * Returns an iterable of [sessionId, entry] pairs; consumers should
302
+ * treat entries as read-only snapshots and avoid mutating them.
303
+ */
304
+ export function forEachSessionRuntime() {
305
+ return _runtimeState.entries();
306
+ }
307
+
308
+ // Wire the usage-metrics runtime accessor to this module's _runtimeState so
309
+ // persistIterationMetrics can read the live in-memory session and flag
310
+ // usageMetricsTurnIncremental. (Moved with the map from manager.mjs.)
311
+ configureUsageMetricsRuntime({ getRuntimeEntry: (id) => _runtimeState.get(id) });
312
+
313
+ /** Mark session hidden so listSessions() filters it out (runtime-only). */
314
+ export function hideSessionFromList(sessionId) {
315
+ if (!sessionId) return;
316
+ const entry = _runtimeState.get(sessionId);
317
+ if (entry) entry.listHidden = true;
318
+ }
319
+
320
+ export function getSessionAbortSignal(sessionId) {
321
+ return _runtimeState.get(sessionId)?.controller?.signal ?? null;
322
+ }
323
+
324
+ /**
325
+ * Return the most recent "session is making progress" timestamp.
326
+ *
327
+ * Combines three independent progress signals so an idle watchdog can stay
328
+ * alive across both streaming and long tool calls:
329
+ * - lastStreamDeltaAt: provider stream chunk landed
330
+ * - toolStartedAt: a tool call just kicked off (nested tool work may
331
+ * stall the outer stream for a while; this keeps the watchdog from
332
+ * killing legitimate sub-agent runs)
333
+ * - askStartedAt: ask just started; covers the pre-stream connect window
334
+ *
335
+ * Returns 0 when the runtime entry is unknown so callers can decide to
336
+ * either skip the watchdog or treat 0 as "no progress yet".
337
+ */
338
+ export function getSessionLastProgressAt(sessionId) {
339
+ const entry = _runtimeState.get(sessionId);
340
+ if (!entry) return 0;
341
+ return Math.max(
342
+ entry.lastProgressAt || 0,
343
+ entry.lastStreamDeltaAt || 0,
344
+ entry.toolStartedAt || 0,
345
+ entry.askStartedAt || 0,
346
+ );
347
+ }
348
+
349
+ /**
350
+ * Link a parent AbortSignal to a sub-session's controller so that aborting
351
+ * the parent (fan-out deadline or caller ESC) tears down the agent role's
352
+ * provider call promptly. Safe to call after prepareAgentSession but before
353
+ * askSession completes. No-op if the session runtime isn't found.
354
+ *
355
+ * @param {string} sessionId — the sub-session to abort
356
+ * @param {AbortSignal} parentSignal — upstream signal (from fan-out coordinator)
357
+ */
358
+ export function linkParentSignalToSession(sessionId, parentSignal) {
359
+ if (!(parentSignal instanceof AbortSignal)) return;
360
+ const entry = _touchRuntime(sessionId);
361
+ if (!entry.controller) entry.controller = createAbortController();
362
+ const abortReason = () => {
363
+ const reason = parentSignal.reason;
364
+ if (reason instanceof Error) return reason;
365
+ if (reason !== undefined && reason !== null && reason !== '') return new Error(String(reason));
366
+ return new Error('parent signal aborted');
367
+ };
368
+ if (parentSignal.aborted) {
369
+ _unlinkParentAbortListener(entry);
370
+ try { entry.controller.abort(abortReason()); } catch { /* ignore */ }
371
+ return;
372
+ }
373
+ _unlinkParentAbortListener(entry);
374
+ const onParentAbort = () => {
375
+ try { entry.controller?.abort(abortReason()); } catch { /* ignore */ }
376
+ };
377
+ parentSignal.addEventListener('abort', onParentAbort, { once: true });
378
+ entry.parentAbortLink = { signal: parentSignal, listener: onParentAbort };
379
+ }
380
+ export function _unlinkParentAbortListener(entry) {
381
+ const link = entry?.parentAbortLink;
382
+ if (!link) return;
383
+ try { link.signal.removeEventListener('abort', link.listener); } catch { /* ignore */ }
384
+ entry.parentAbortLink = null;
385
+ }
386
+ export function _clearSessionRuntime(id) {
387
+ if (id) {
388
+ _stopToolActivityHeartbeat(id);
389
+ _unlinkParentAbortListener(_runtimeState.get(id));
390
+ _runtimeState.delete(id);
391
+ // R15: also drop the per-session metric-idempotency Set; otherwise it
392
+ // grows O(sessions x iterations) for the whole server lifetime since
393
+ // nothing else deletes from _metricSeenIter on session close.
394
+ dropMetricSeenState(id);
395
+ }
396
+ }
397
+
398
+ // Direct-Map accessors for manager.mjs internals (askSession / closeSession /
399
+ // idle-sweep) that mutate entries in place. Kept as thin exports so the Map
400
+ // stays private to this module.
401
+ export function _getRuntimeEntry(id) {
402
+ return _runtimeState.get(id);
403
+ }
404
+ export function _runtimeEntries() {
405
+ return _runtimeState.entries();
406
+ }
@@ -0,0 +1,80 @@
1
+ // Standalone-status route info + gateway usage telemetry for sessions.
2
+ // Extracted verbatim from manager.mjs (behavior-preserving). Fire-and-forget
3
+ // statusline telemetry — must never affect the model turn.
4
+ import { getProvider } from '../../providers/registry.mjs';
5
+ import { fetchOAuthUsageSnapshot } from '../../providers/oauth-usage.mjs';
6
+ import {
7
+ buildGatewayLimits,
8
+ recordGatewayUsageEvent,
9
+ summarizeGatewayUsage,
10
+ } from '../../providers/statusline-route-meta.mjs';
11
+ import { estimateTranscriptContextUsage } from '../context-utils.mjs';
12
+
13
+ export function standaloneStatusRouteInfo(session) {
14
+ if (!session) return null;
15
+ // autoCompactTokenLimit is an EXPLICIT sub-boundary auto-compact limit only.
16
+ // Do NOT fall back to compactBoundaryTokens/contextWindow here — that
17
+ // labels a derived full-window value as an explicit limit, which the
18
+ // runtime would treat as the compaction trigger and collapse the buffer.
19
+ // The boundary/window stays available via contextWindow/rawContextWindow.
20
+ const _boundary = Number(session.compactBoundaryTokens || session.contextWindow || 0);
21
+ const _limit = Number(session.autoCompactTokenLimit || 0);
22
+ const explicitAutoCompactTokenLimit = _limit > 0 && (!_boundary || _limit < _boundary) ? _limit : null;
23
+ return {
24
+ provider: session.provider,
25
+ model: session.model,
26
+ modelDisplay: session.modelDisplay || session.displayName || session.model,
27
+ effort: session.effort || '',
28
+ fast: session.fast === true,
29
+ contextWindow: session.contextWindow || null,
30
+ rawContextWindow: session.rawContextWindow || session.contextWindow || null,
31
+ effectiveContextWindowPercent: session.effectiveContextWindowPercent || null,
32
+ autoCompactTokenLimit: explicitAutoCompactTokenLimit,
33
+ presetId: session.presetId || null,
34
+ presetName: session.presetName || null,
35
+ };
36
+ }
37
+
38
+ export function recordStandaloneStatusTelemetry(session, result, durationMs) {
39
+ if (!session || !result?.usage) return;
40
+ const routeInfo = standaloneStatusRouteInfo(session);
41
+ if (!routeInfo?.provider || !routeInfo?.model) return;
42
+ const providerOut = {
43
+ usage: result.usage,
44
+ model: result.model,
45
+ serviceTier: result.serviceTier,
46
+ };
47
+ // The transcript estimate is the SSOT for the displayed context footprint.
48
+ // agentLoop()'s result has no `compact` field, so build a synthetic compact
49
+ // arg carrying the live monotonic estimate (estimateMessagesTokens+reserve)
50
+ // as afterTokens. This lights up summarizeGatewayUsage's estimate-based
51
+ // contextUsedPct branch (provider input_tokens swing wildly / unbounded on
52
+ // e.g. OpenAI gpt-5.5), and lets a genuine >100% pass through.
53
+ const _estTokens = estimateTranscriptContextUsage(session.messages, session.tools || []);
54
+ const _compactArg = { ...(result.compact && typeof result.compact === 'object' ? result.compact : {}), afterTokens: _estTokens };
55
+ try {
56
+ const summary = {
57
+ ...summarizeGatewayUsage(routeInfo, providerOut, _compactArg, durationMs),
58
+ requestKind: 'chat',
59
+ sessionId: session.id || null,
60
+ toolCount: result.toolCallsTotal ?? null,
61
+ messageCount: Array.isArray(session.messages) ? session.messages.length : null,
62
+ cacheStrategy: session.providerCacheOpts?.cacheStrategy || null,
63
+ };
64
+ recordGatewayUsageEvent(summary);
65
+ } catch {
66
+ // Statusline telemetry must never affect the model turn.
67
+ }
68
+
69
+ const provider = getProvider(routeInfo.provider);
70
+ if (!provider) return;
71
+ fetchOAuthUsageSnapshot(routeInfo, provider, (message) => {
72
+ if (process.env.MIXDOG_STATUSLINE_TRACE) {
73
+ process.stderr.write(`[statusline] ${message}\n`);
74
+ }
75
+ })
76
+ .then((snapshot) => {
77
+ try { buildGatewayLimits(routeInfo, providerOut, snapshot); } catch {}
78
+ })
79
+ .catch(() => {});
80
+ }