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,155 @@
1
+ // Prompt-layer rules builders + mtime-invalidated caches, extracted verbatim
2
+ // from manager.mjs. Behavior-preserving: same createRequire lookup, same cache
3
+ // semantics, same error messages.
4
+ import { createRequire } from 'module';
5
+ import { join } from 'path';
6
+ import { resolvePluginData, mixdogRoot } from '../../../../shared/plugin-paths.mjs';
7
+ import { maxMtimeRecursive } from '../../cache-mtime.mjs';
8
+ import { getAgentInstructionDir } from '../../internal-agents.mjs';
9
+
10
+ // Phase B: Pool B Tier 2 content builder (common rules only).
11
+ // Loaded once per process via createRequire so the CJS module reaches us.
12
+ const _require = createRequire(import.meta.url);
13
+ const _rulesBuilder = (() => {
14
+ const candidates = [
15
+ join(mixdogRoot(), 'lib', 'rules-builder.cjs'),
16
+ ].filter(Boolean);
17
+ for (const p of candidates) {
18
+ try { return _require(p); } catch { /* fall through */ }
19
+ }
20
+ // Fallback: walk up from this file's location to find lib/rules-builder.cjs.
21
+ try { return _require('../../../../../lib/rules-builder.cjs'); } catch { return null; }
22
+ })();
23
+
24
+ // BP1/BP2/BP3 prompt-layer caches — invalidated by source file mtime, not a
25
+ // timer. Cheap: O(sentinel-count) stat calls on each session creation, no file
26
+ // I/O when warm.
27
+ let _sharedRulesCache = null;
28
+ let _sharedRulesMtime = 0;
29
+ const _agentRulesCacheByProfile = new Map();
30
+ let _leadRulesCache = null;
31
+ let _leadRulesMtime = 0;
32
+ let _leadMetaCache = null;
33
+ let _leadMetaMtime = 0;
34
+
35
+ export function _buildSharedRules() {
36
+ if (!_rulesBuilder || typeof _rulesBuilder.buildSharedToolContent !== 'function') return '';
37
+ const PLUGIN_ROOT = mixdogRoot();
38
+ const RULES_DIR = join(PLUGIN_ROOT, 'rules');
39
+ const mtime = maxMtimeRecursive([
40
+ join(RULES_DIR, 'shared'),
41
+ ]);
42
+ if (_sharedRulesCache !== null && mtime <= _sharedRulesMtime) {
43
+ return _sharedRulesCache;
44
+ }
45
+ try {
46
+ const built = _rulesBuilder.buildSharedToolContent({ PLUGIN_ROOT, DATA_DIR: resolvePluginData() });
47
+ _sharedRulesCache = built;
48
+ _sharedRulesMtime = mtime;
49
+ return built;
50
+ } catch (e) {
51
+ throw new Error(`[session] shared tool rules build failed: ${e.message}`);
52
+ }
53
+ }
54
+
55
+ export function _buildAgentRules(profile = 'full') {
56
+ if (!_rulesBuilder || typeof _rulesBuilder.buildAgentRoleContent !== 'function') return '';
57
+ const key = String(profile || 'full');
58
+ const PLUGIN_ROOT = mixdogRoot();
59
+ const DATA_DIR = resolvePluginData();
60
+ const RULES_DIR = join(PLUGIN_ROOT, 'rules');
61
+ const mtime = maxMtimeRecursive([
62
+ join(RULES_DIR, 'agent'),
63
+ join(DATA_DIR, 'mixdog-config.json'),
64
+ ]);
65
+ const cached = _agentRulesCacheByProfile.get(key);
66
+ if (cached && mtime <= cached.mtime) {
67
+ return cached.value;
68
+ }
69
+ try {
70
+ const built = _rulesBuilder.buildAgentRoleContent({ PLUGIN_ROOT, DATA_DIR, profile: key });
71
+ _agentRulesCacheByProfile.set(key, { mtime, value: built });
72
+ return built;
73
+ } catch (e) {
74
+ throw new Error(`[session] agent role rules build failed: ${e.message}`);
75
+ }
76
+ }
77
+
78
+ export function _buildLeadRules() {
79
+ if (!_rulesBuilder || typeof _rulesBuilder.buildLeadRoleContent !== 'function') return '';
80
+ const PLUGIN_ROOT = mixdogRoot();
81
+ const DATA_DIR = resolvePluginData();
82
+ const RULES_DIR = join(PLUGIN_ROOT, 'rules');
83
+ const mtime = maxMtimeRecursive([
84
+ join(RULES_DIR, 'lead'),
85
+ join(DATA_DIR, 'mixdog-config.json'),
86
+ ]);
87
+ if (_leadRulesCache !== null && mtime <= _leadRulesMtime) {
88
+ return _leadRulesCache;
89
+ }
90
+ try {
91
+ const built = _rulesBuilder.buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR });
92
+ _leadRulesCache = built;
93
+ _leadRulesMtime = mtime;
94
+ return built;
95
+ } catch (e) {
96
+ throw new Error(`[session] lead role rules build failed: ${e.message}`);
97
+ }
98
+ }
99
+
100
+ export function _buildLeadMetaContext() {
101
+ if (!_rulesBuilder || typeof _rulesBuilder.buildLeadMetaContent !== 'function') return '';
102
+ const PLUGIN_ROOT = mixdogRoot();
103
+ const DATA_DIR = resolvePluginData();
104
+ const RULES_DIR = join(PLUGIN_ROOT, 'rules');
105
+ const mtime = maxMtimeRecursive([
106
+ join(RULES_DIR, 'lead'),
107
+ join(DATA_DIR, 'history'),
108
+ join(DATA_DIR, 'mixdog-config.json'),
109
+ join(DATA_DIR, 'user-workflow.md'),
110
+ join(PLUGIN_ROOT, 'output-styles'),
111
+ join(DATA_DIR, 'output-styles'),
112
+ ]);
113
+ if (_leadMetaCache !== null && mtime <= _leadMetaMtime) {
114
+ return _leadMetaCache;
115
+ }
116
+ try {
117
+ const built = _rulesBuilder.buildLeadMetaContent({ PLUGIN_ROOT, DATA_DIR });
118
+ _leadMetaCache = built;
119
+ _leadMetaMtime = mtime;
120
+ return built;
121
+ } catch (e) {
122
+ throw new Error(`[session] lead meta context build failed: ${e.message}`);
123
+ }
124
+ }
125
+
126
+ // BP4-adjacent agent-specific data cache — keyed by agent. webhook / schedule
127
+ // agents each have their own scoped instruction set; other agents return ''.
128
+ const _roleSpecificCache = new Map(); // agent → { value, mtime }
129
+ export function _buildAgentSpecific(currentAgent) {
130
+ if (!_rulesBuilder || typeof _rulesBuilder.buildAgentRoleSpecificContent !== 'function') return '';
131
+ if (!currentAgent) return '';
132
+ const PLUGIN_ROOT = mixdogRoot();
133
+ const DATA_DIR = resolvePluginData();
134
+ const RULES_DIR = join(PLUGIN_ROOT, 'rules');
135
+ const roleInstructionDir = getAgentInstructionDir(currentAgent);
136
+ const mtime = maxMtimeRecursive([
137
+ join(RULES_DIR, 'shared'),
138
+ join(DATA_DIR, 'mixdog-config.json'),
139
+ join(DATA_DIR, 'webhooks'),
140
+ join(DATA_DIR, 'schedules'),
141
+ ...(roleInstructionDir ? [join(DATA_DIR, roleInstructionDir)] : []),
142
+ join(PLUGIN_ROOT, 'defaults', 'agents.json'),
143
+ ]);
144
+ const entry = _roleSpecificCache.get(currentAgent);
145
+ if (entry && mtime <= entry.mtime) {
146
+ return entry.value;
147
+ }
148
+ try {
149
+ const built = _rulesBuilder.buildAgentRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentAgent });
150
+ _roleSpecificCache.set(currentAgent, { mtime, value: built });
151
+ return built;
152
+ } catch (e) {
153
+ throw new Error(`[session] agent-specific rules build failed (agent: ${currentAgent}): ${e.message}`);
154
+ }
155
+ }
@@ -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
+ }