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
@@ -9,47 +9,28 @@
9
9
  * reused as read-only data sources, but display identity always belongs to the
10
10
  * CLI route passed here.
11
11
  */
12
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
13
- import { homedir } from 'node:os';
14
- import { join } from 'node:path';
15
- import { bold, colorEnabled, rgb } from './ansi.mjs';
16
- import { displayModelName, shortenModelName } from './model-display.mjs';
17
- import { createSessionStats } from './session-stats.mjs';
18
- import { forEachSessionRuntime } from '../runtime/agent/orchestrator/session/manager.mjs';
19
- import { listHiddenAgentNames } from '../runtime/agent/orchestrator/internal-agents.mjs';
20
- import { getModelMetadataSync } from '../runtime/agent/orchestrator/providers/model-catalog.mjs';
21
12
  import { readCachedOAuthUsageSnapshot } from '../runtime/agent/orchestrator/providers/oauth-usage.mjs';
22
13
  import { readCachedOpenCodeGoUsageSnapshot } from '../runtime/agent/orchestrator/providers/opencode-go-usage.mjs';
23
14
  import { buildGatewayLimits } from '../runtime/agent/orchestrator/providers/statusline-route-meta.mjs';
24
15
  import { compactBoundaryForStatus, formatGatewayLimitSegments, loadGatewayStatus } from '../vendor/statusline/bin/statusline-route.mjs';
16
+ import { createSessionStats } from './session-stats.mjs';
17
+ import {
18
+ FALLBACK_CONTEXT_WINDOW, statusSubtle,
19
+ R, B, D, GRN, YLW, RED,
20
+ terminalColumns, modelContextWindow, formatModelSegment,
21
+ formatContextSegment, colourPct, epochMsToHHMM,
22
+ num, formatElapsed,
23
+ } from './statusline-format.mjs';
24
+ import { shellJobsStatus, memoryCycleStatus } from './statusline-segments.mjs';
25
+ import {
26
+ summarizeWorkerTags, agentStatuslinePayload, classifyAgentWorkers, activeHiddenAgentWorkers,
27
+ } from './statusline-agents.mjs';
25
28
  export { createSessionStats, applyUsageDelta } from './session-stats.mjs';
29
+ // Facade re-exports: keep these public symbols resolving from statusline.mjs.
30
+ export { contextPctDisplayLabel } from './statusline-format.mjs';
26
31
 
27
- // Token window used to compute a fallback context% from our own session usage.
28
- // The live gateway (when up) overrides this with the real route's window. This
29
- // is only the last resort for unknown local models.
30
- const FALLBACK_CONTEXT_WINDOW = 200000;
31
- const statusText = rgb(198, 198, 198);
32
- const statusSubtle = rgb(136, 136, 136);
33
- const statusAccent = rgb(215, 119, 87);
34
- const DEFAULT_MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
35
- const DEFAULT_STANDALONE_DATA_DIR = join(DEFAULT_MIXDOG_HOME, 'data');
36
-
37
- function sgr(code) {
38
- return colorEnabled() ? `\x1b[${code}m` : '';
39
- }
40
-
41
- const R = sgr('0');
42
- const B = sgr('1');
43
- const D = sgr('38;2;136;136;136');
44
- const GRN = sgr('38;2;0;170;75');
45
- const YLW = sgr('38;2;255;193;7');
46
- const RED = sgr('38;2;220;70;88');
47
- const CYN = sgr('38;2;136;136;136');
48
- const GREY = sgr('38;2;136;136;136');
49
- const SHELL_JOBS_SEGMENT_CACHE_MS = 1000;
50
32
  const GATEWAY_QUOTA_STATUS_CACHE_MS = 500;
51
33
  const WORKER_SPINNER_FRAMES = Object.freeze(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']);
52
- const WORKER_SPINNER_FRAME_MS = 160;
53
34
  // L2 segment spinner: reuses the original WORKER_SPINNER_FRAMES dot glyphs (no
54
35
  // separate glyph list) but spins them at a faster 120ms step than the worker
55
36
  // spinner's 160ms. l2SpinnerFrame() indexes straight into WORKER_SPINNER_FRAMES.
@@ -61,11 +42,46 @@ const L2_SPINNER_FRAME_MS = 120;
61
42
  // replaces them. Snapshots from a previous launch stay hidden during boot so the
62
43
  // statusline starts empty until the current session captures usage once.
63
44
  const STATUSLINE_PROCESS_STARTED_AT_MS = Date.now() - Math.floor((Number(process.uptime?.()) || 0) * 1000);
64
- const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
65
- let _shellJobsSegmentCache = { ownerPid: 0, at: 0, value: { count: 0, elapsedLabel: '' } };
66
45
  let _gatewayQuotaStatusCache = { key: '', at: 0, value: null };
67
46
  let _fallbackQuotaStatusCache = { key: '', at: 0, value: null };
68
- let _hiddenStatuslineAgents = null;
47
+ // Holds the last non-empty rendered L1 quota/usage segments per provider+route
48
+ // key, but ONLY for providers that have actually armed the OAuth boot latch
49
+ // (_oauthUsageArmedProviders) — non-OAuth providers (which oauthUsageSegmentReady
50
+ // also returns true for, since they are never gated) must keep their exact
51
+ // prior empty/null behavior, unaffected by this hold. Once armed and at least
52
+ // one non-empty quota segment set has been rendered for a route, a transient
53
+ // resolution failure (cache miss / gateway hiccup) that would otherwise
54
+ // collapse quotaSegments to [] instead re-uses the last held segments, so the
55
+ // L1 segment does not blink out. Replaced only when a newer non-empty result
56
+ // lands for the SAME key. The key includes sessionId + clientHostPid because
57
+ // rendered segments can embed session-scoped routeSpend — without those, a new
58
+ // session sharing provider/model/effort/fast could reuse a stale prior
59
+ // session's spend. Capped at a small LRU size so long-running processes that
60
+ // cycle through many sessions/routes don't grow this map unbounded.
61
+ const _lastNonEmptyQuotaSegmentsByKey = new Map();
62
+ const LAST_NON_EMPTY_QUOTA_SEGMENTS_CACHE_MAX = 8;
63
+
64
+ function quotaSegmentsHoldKey({ provider, model, effort, fast, sessionId, clientHostPid } = {}) {
65
+ return [
66
+ String(provider || '').trim().toLowerCase(),
67
+ String(model || '').trim(),
68
+ String(effort || '').trim(),
69
+ fast === true ? 'fast' : '',
70
+ String(sessionId || ''),
71
+ String(clientHostPid || ''),
72
+ ].join('\0');
73
+ }
74
+
75
+ function rememberNonEmptyQuotaSegments(key, segments) {
76
+ // Delete-then-set to bump this key to most-recently-used position (Map
77
+ // iterates in insertion order), then evict the oldest entry if over cap.
78
+ _lastNonEmptyQuotaSegmentsByKey.delete(key);
79
+ _lastNonEmptyQuotaSegmentsByKey.set(key, segments);
80
+ if (_lastNonEmptyQuotaSegmentsByKey.size > LAST_NON_EMPTY_QUOTA_SEGMENTS_CACHE_MAX) {
81
+ const oldestKey = _lastNonEmptyQuotaSegmentsByKey.keys().next().value;
82
+ _lastNonEmptyQuotaSegmentsByKey.delete(oldestKey);
83
+ }
84
+ }
69
85
  // Option A boot gate: the L1 usage/quota segment stays fully empty until THIS
70
86
  // process has captured its FIRST confirmed (current-process) OAuth usage
71
87
  // snapshot for THAT provider. The latch is monotonic PER PROVIDER — once a
@@ -85,19 +101,6 @@ function isConfirmedCurrentProcessSnapshot(snapshot) {
85
101
  return cachedAt > 0 && cachedAt >= STATUSLINE_PROCESS_STARTED_AT_MS;
86
102
  }
87
103
 
88
- function summarizeWorkerTags(workers, limit = 3) {
89
- const cleanLabels = [...new Set((Array.isArray(workers) ? workers : [])
90
- .map((worker) => String(worker?.tag || '').trim())
91
- .filter(Boolean))];
92
- if (cleanLabels.length <= limit) return cleanLabels.join(', ');
93
- return `${cleanLabels.slice(0, limit).join(', ')}, +${cleanLabels.length - limit}`;
94
- }
95
-
96
- function workerSpinnerFrame(now = Date.now()) {
97
- const index = Math.floor(now / WORKER_SPINNER_FRAME_MS) % WORKER_SPINNER_FRAMES.length;
98
- return WORKER_SPINNER_FRAMES[index] || WORKER_SPINNER_FRAMES[0];
99
- }
100
-
101
104
  function l2SpinnerFrame(now = Date.now()) {
102
105
  const index = Math.floor(now / L2_SPINNER_FRAME_MS) % WORKER_SPINNER_FRAMES.length;
103
106
  return WORKER_SPINNER_FRAMES[index] || WORKER_SPINNER_FRAMES[0];
@@ -114,21 +117,6 @@ function l2SpinnerFrame(now = Date.now()) {
114
117
  * model / context% / effort / 5H-7D are overridden by the live gateway via
115
118
  * loadGatewayStatus() when it's running; these are the standalone fallbacks.
116
119
  */
117
- function providerInputExcludesCache(provider) {
118
- return String(provider || '').toLowerCase().includes('anthropic');
119
- }
120
-
121
- function promptFootprintTokens(provider, stats) {
122
- const s = stats || createSessionStats();
123
- const explicit = num(s.promptTokens);
124
- if (explicit > 0) return explicit;
125
- const input = num(s.inputTokens);
126
- if (providerInputExcludesCache(provider)) {
127
- return input + num(s.cachedTokens) + num(s.cacheWriteTokens);
128
- }
129
- return input;
130
- }
131
-
132
120
  function activeContextNumerator(provider, stats) {
133
121
  const s = stats || createSessionStats();
134
122
  const source = String(s.currentContextSource || '').toLowerCase();
@@ -144,18 +132,6 @@ function activeContextNumerator(provider, stats) {
144
132
  return 0;
145
133
  }
146
134
 
147
- function currentContextTokens(provider, stats) {
148
- return activeContextNumerator(provider, stats);
149
- }
150
-
151
- function modelContextWindow(provider, model, explicitContextWindow = 0) {
152
- const explicit = num(explicitContextWindow);
153
- if (explicit > 0) return explicit;
154
- const metaWindow = num(getModelMetadataSync(model, provider)?.contextWindow);
155
- if (metaWindow > 0) return metaWindow;
156
- return FALLBACK_CONTEXT_WINDOW;
157
- }
158
-
159
135
  function displayContextBoundary({
160
136
  contextWindow = 0,
161
137
  displayContextWindow = 0,
@@ -210,88 +186,6 @@ function resolveContextUsedPct({
210
186
  return 0;
211
187
  }
212
188
 
213
- function normalizeAgentWorkerForStatusline(worker = {}) {
214
- const tag = String(worker.tag || worker.agent || worker.name || '').trim();
215
- if (!tag) return null;
216
- const statusText = String(worker.stage || worker.status || '').toLowerCase();
217
- const status = isTerminalBridgeStatus(statusText) ? 'idle' : 'running';
218
- return {
219
- tag,
220
- status,
221
- startedAtMs: timeMs(worker.startedAt || worker.startTime || worker.createdAt),
222
- agent: worker.agent || null,
223
- stage: worker.stage || worker.status || null,
224
- sessionId: worker.sessionId || null,
225
- provider: worker.provider || null,
226
- model: worker.model || null,
227
- };
228
- }
229
-
230
- function normalizeAgentJobForStatusline(job = {}) {
231
- const statusText = String(job.status || job.stage || '').toLowerCase();
232
- if (!statusText) return null;
233
- const taskId = String(job.task_id || job.taskId || '').trim();
234
- const tag = String(job.tag || job.agent || job.type || taskId || '').trim();
235
- if (!tag && !taskId) return null;
236
- const startedAtMs = timeMs(job.startedAt);
237
- const finishedAtMs = timeMs(job.finishedAt);
238
- if (isTerminalBridgeStatus(statusText) && finishedAtMs > 0) {
239
- return {
240
- tag,
241
- taskId,
242
- status: 'finished',
243
- finalStatus: statusText,
244
- startedAtMs,
245
- finishedAtMs,
246
- agent: job.agent || null,
247
- stage: job.stage || job.workerStatus || job.status || null,
248
- sessionId: job.sessionId || null,
249
- provider: job.provider || null,
250
- model: job.model || null,
251
- };
252
- }
253
- if (!/running/.test(statusText)) return null;
254
- return {
255
- tag,
256
- taskId,
257
- status: 'running',
258
- startedAtMs,
259
- agent: job.agent || null,
260
- stage: job.stage || job.workerStatus || job.status || null,
261
- sessionId: job.sessionId || null,
262
- provider: job.provider || null,
263
- model: job.model || null,
264
- };
265
- }
266
-
267
- function agentStatuslinePayload(agentWorkers = [], agentJobs = []) {
268
- const byTag = new Map();
269
- const finishedJobs = [];
270
- for (const worker of Array.isArray(agentWorkers) ? agentWorkers : []) {
271
- const row = normalizeAgentWorkerForStatusline(worker);
272
- if (row) byTag.set(row.tag, row);
273
- }
274
- for (const job of Array.isArray(agentJobs) ? agentJobs : []) {
275
- const row = normalizeAgentJobForStatusline(job);
276
- if (!row) continue;
277
- if (row.status === 'finished') {
278
- finishedJobs.push(row);
279
- continue;
280
- }
281
- const prev = byTag.get(row.tag);
282
- byTag.set(row.tag, { ...(prev || {}), ...row, status: 'running' });
283
- }
284
- const workers = [...byTag.values()];
285
- return {
286
- workers,
287
- finishedJobs: finishedJobs.sort((a, b) => (b.finishedAtMs || 0) - (a.finishedAtMs || 0)),
288
- sessions: {
289
- roles: workers.filter((w) => w.status !== 'idle').map((w) => w.tag),
290
- workers,
291
- },
292
- };
293
- }
294
-
295
189
  /**
296
190
  * Render the L1/L2 statusline footer from CLI state.
297
191
  *
@@ -380,9 +274,23 @@ function renderNativeStatusline({
380
274
  const quotaStatus = usageReady
381
275
  ? mergeQuotaStatus(gatewayStatus, fallbackQuotaStatus({ provider, model }))
382
276
  : null;
383
- const quotaSegments = quotaStatus
277
+ let quotaSegments = quotaStatus
384
278
  ? formatGatewayLimitSegments(quotaStatus, { COLS: cols, D, R, GRN, YLW, RED, colourPct, epochMsToHHMM })
385
279
  : [];
280
+ // Only apply the hold to providers that actually armed the OAuth boot latch.
281
+ // oauthUsageSegmentReady() also returns true for non-OAuth providers (they
282
+ // are never gated), so gate the hold itself on _oauthUsageArmedProviders to
283
+ // keep non-OAuth empty/null behavior byte-for-byte unchanged.
284
+ const normalizedHoldProvider = String(provider || '').trim().toLowerCase();
285
+ if (usageReady && _oauthUsageArmedProviders.has(normalizedHoldProvider)) {
286
+ const holdKey = quotaSegmentsHoldKey({ provider, model, effort, fast, sessionId, clientHostPid });
287
+ if (quotaSegments.length) {
288
+ rememberNonEmptyQuotaSegments(holdKey, quotaSegments);
289
+ } else {
290
+ const held = _lastNonEmptyQuotaSegmentsByKey.get(holdKey);
291
+ if (held && held.length) quotaSegments = held;
292
+ }
293
+ }
386
294
  for (const seg of quotaSegments) addL1(seg);
387
295
 
388
296
  const agentPayload = agentStatuslinePayload([
@@ -425,20 +333,21 @@ function renderNativeStatusline({
425
333
  const label = `Running ${n} Shell${n === 1 ? '' : 's'}`;
426
334
  addL2(`${spin} ${B}${label}${R}${elapsedSuffix(shellStatus.elapsedLabel)}`);
427
335
  }
336
+ // Memory cycle segment — single unified "Memory" wording for all states:
337
+ // running -> "⠋ Memory · 12s"; backlog warning -> "⠋ Memory · backlog 512"
338
+ // (yellow). Nothing when idle (no L2 pollution).
339
+ const memStatus = memoryCycleStatus();
340
+ if (memStatus?.kind === 'running') {
341
+ const elapsed = formatElapsed(Date.now() - memStatus.startedAt);
342
+ addL2(`${spin} ${B}Memory${R}${elapsedSuffix(elapsed)}`);
343
+ } else if (memStatus?.kind === 'backlog') {
344
+ addL2(`${spin} ${B}Memory${R} ${D}·${R} ${YLW}backlog ${memStatus.pending}${R}`);
345
+ }
428
346
  const l1 = l1Parts.join(sep) || 'mixdog';
429
347
  const l2 = l2Parts.join(sep);
430
348
  return l2 ? `${l1}\n${l2}` : l1;
431
349
  }
432
350
 
433
- function terminalColumns() {
434
- const cols = Number(process.stdout?.columns);
435
- return Number.isFinite(cols) && cols > 0 ? Math.floor(cols) : 120;
436
- }
437
-
438
- function dataDir() {
439
- return process.env.MIXDOG_DATA_DIR || DEFAULT_STANDALONE_DATA_DIR;
440
- }
441
-
442
351
  function loadGatewayQuotaStatus({
443
352
  provider, model, effort, fast, contextWindow, rawContextWindow, autoCompactTokenLimit = 0,
444
353
  sessionId, activeContextTokens, clientHostPid,
@@ -605,278 +514,6 @@ function mergeQuotaStatus(primary, fallback) {
605
514
  };
606
515
  }
607
516
 
608
- function formatModelSegment({ provider, model, effort, fast, cols }) {
609
- const raw = String(model || '').trim();
610
- const meta = getModelMetadataSync(raw, provider) || {};
611
- const displayHint = String(meta.displayName || meta.display || meta.name || '').trim();
612
- const modelName = shortenModelName(displayModelName(raw, provider, displayHint), cols);
613
- const bits = [`${B}${modelName}${R}`];
614
- if (effort) bits.push(`${B}${String(effort).toUpperCase()}${R}`);
615
- if (fast === true) bits.push(`${B}FAST${R}`);
616
- return bits.join(` ${D}·${R} `);
617
- }
618
-
619
- /** Display label for context % (clamped to 100); raw pct still drives bar/color thresholds. */
620
- export function contextPctDisplayLabel(ctxPct) {
621
- const pct = Number(ctxPct);
622
- if (!Number.isFinite(pct) || pct <= 0) return '0';
623
- if (pct > 0 && pct < 1) return String(Math.round(pct * 10) / 10);
624
- return String(Math.floor(Math.min(100, pct)));
625
- }
626
-
627
- function formatContextSegment(ctxPct, cols) {
628
- const raw = Number(ctxPct);
629
- const pct = Number.isFinite(raw) ? Math.max(0, raw) : 0;
630
- const barPct = clampPct(pct);
631
- const fill = pct >= 90 ? RED : pct >= 70 ? YLW : GRN;
632
- const label = contextPctDisplayLabel(pct);
633
- // Keep a full-width bar wherever there is room for one. Below 80 cols the bar
634
- // is dropped (label-only) so the footer never overflows a narrow terminal;
635
- // at 80+ it stays a fixed 14 cells instead of shrinking to 8, which read as
636
- // too small/cramped on mid-width terminals.
637
- const cells = cols >= 80 ? 14 : 0;
638
- if (!cells) return `${fill}${label}%${R}`;
639
- const bar = makeBar(barPct, cells);
640
- const filled = bar.replace(/░/g, '');
641
- const empty = bar.replace(/▓/g, '');
642
- return `${fill}${filled}${R}${D}${empty}${R} ${label}%`;
643
- }
644
-
645
- function makeBar(pct, cells) {
646
- let filled = Math.floor((Number(pct) || 0) * cells / 100);
647
- if (filled < 0) filled = 0;
648
- if (filled > cells) filled = cells;
649
- if (pct >= 1 && filled === 0) filled = 1;
650
- return '▓'.repeat(filled) + '░'.repeat(cells - filled);
651
- }
652
-
653
- function colourPct(p) {
654
- if (p >= 90) return `${RED}${p}%${R}`;
655
- if (p >= 70) return `${YLW}${p}%${R}`;
656
- return `${GRN}${p}%${R}`;
657
- }
658
-
659
- function epochMsToHHMM(ms) {
660
- const d = new Date(Number(ms));
661
- if (!Number.isFinite(d.getTime())) return '';
662
- return d.toLocaleTimeString('sv-SE', { hour: '2-digit', minute: '2-digit', hour12: false });
663
- }
664
-
665
- function classifyAgentWorkers(workers = []) {
666
- const maintenance = [];
667
- const runningWorkers = [];
668
- const seenMaintenance = new Set();
669
- const seenRunning = new Set();
670
- for (const w of Array.isArray(workers) ? workers : []) {
671
- const tag = String(w?.tag || '').trim();
672
- if (!tag) continue;
673
- const maint = hiddenWorkerLabel(w);
674
- if (maint) {
675
- if (w.status !== 'idle' && !seenMaintenance.has(maint)) {
676
- seenMaintenance.add(maint);
677
- maintenance.push(`${GRN}↻${R} ${B}${maint}${R}`);
678
- }
679
- continue;
680
- }
681
- if (w.status !== 'idle' && !seenRunning.has(tag)) {
682
- seenRunning.add(tag);
683
- runningWorkers.push(w);
684
- }
685
- }
686
- return { maintenance, runningWorkers };
687
- }
688
-
689
- function hiddenWorkerLabel(worker = {}) {
690
- const agent = String(worker?.agent || '').trim();
691
- const tag = String(worker?.tag || '').trim();
692
- return maintenanceLabel(agent) || (!agent ? maintenanceLabel(tag) : '');
693
- }
694
-
695
- function hiddenStatuslineAgents() {
696
- if (_hiddenStatuslineAgents) return _hiddenStatuslineAgents;
697
- const agents = new Set(DEFAULT_HIDDEN_STATUSLINE_AGENTS);
698
- try {
699
- for (const agent of listHiddenAgentNames()) {
700
- const clean = String(agent || '').trim();
701
- if (clean) agents.add(clean);
702
- }
703
- } catch {}
704
- _hiddenStatuslineAgents = agents;
705
- return agents;
706
- }
707
-
708
- function isActiveHiddenStatus(statusText) {
709
- return /^(connecting|requesting|streaming|tool_running|running)$/i.test(String(statusText || '').trim());
710
- }
711
-
712
- function activeHiddenAgentWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
713
- const agents = hiddenStatuslineAgents();
714
- const ownerPid = positiveInt(clientHostPid);
715
- const ownerSessionId = String(sessionId || '').trim();
716
- const rows = [];
717
- try {
718
- for (const [runtimeSessionId, entry] of forEachSessionRuntime() || []) {
719
- if (!entry || entry.closed === true) continue;
720
- const session = entry.session || null;
721
- if (!session || session.closed === true) continue;
722
- const agent = String(session?.agent || '').trim();
723
- if (!agent || !agents.has(agent)) continue;
724
- const id = session?.id || runtimeSessionId || null;
725
- if (ownerSessionId && id === ownerSessionId) continue;
726
- const sessionOwnerId = String(session?.ownerSessionId || '').trim();
727
- if (sessionOwnerId && ownerSessionId && sessionOwnerId !== ownerSessionId) continue;
728
- const pid = positiveInt(session?.clientHostPid);
729
- if (ownerPid && pid && pid !== ownerPid) continue;
730
- const stage = String(entry.stage || session?.stage || session?.status || '').trim().toLowerCase();
731
- const status = String(session?.status || stage || '').trim().toLowerCase();
732
- if (!isActiveHiddenStatus(stage || status)) continue;
733
- rows.push({
734
- tag: String(session?.agentTag || `${agent}:${id || rows.length}`).trim(),
735
- agent,
736
- status: 'running',
737
- stage: stage || status || 'running',
738
- sessionId: id,
739
- provider: session?.provider || null,
740
- model: session?.model || null,
741
- });
742
- }
743
- } catch {}
744
- return rows;
745
- }
746
-
747
- function isTerminalBridgeStatus(statusText) {
748
- return /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/.test(String(statusText || '').toLowerCase());
749
- }
750
-
751
- function timeMs(value) {
752
- if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
753
- const n = Date.parse(String(value || ''));
754
- return Number.isFinite(n) ? n : 0;
755
- }
756
-
757
- function maintenanceLabel(tag) {
758
- switch (tag) {
759
- case 'cycle1-agent': return 'cycle1';
760
- case 'cycle2-agent': return 'cycle2';
761
- case 'cycle3-agent': return 'cycle3';
762
- case 'scheduler-task': return 'scheduler';
763
- case 'webhook-handler': return 'webhook';
764
- case 'explorer': return 'explorer';
765
- default: return '';
766
- }
767
- }
768
-
769
- function shellJobsStatus({ clientHostPid } = {}) {
770
- const ownerPid = positiveInt(clientHostPid);
771
- const empty = { count: 0, elapsedLabel: '' };
772
- if (!ownerPid) return empty;
773
- const now = Date.now();
774
- if (_shellJobsSegmentCache.ownerPid === ownerPid && now - _shellJobsSegmentCache.at < SHELL_JOBS_SEGMENT_CACHE_MS) {
775
- return _shellJobsSegmentCache.value || empty;
776
- }
777
- let value = empty;
778
- try {
779
- const dir = join(dataDir(), 'shell-jobs');
780
- if (!existsSync(dir)) {
781
- _shellJobsSegmentCache = { ownerPid, at: now, value };
782
- return value;
783
- }
784
- const names = readdirSync(dir);
785
- const done = new Set(names.filter((n) => n.endsWith('.done')).map((n) => n.slice(0, -5)));
786
- const ownerByJob = new Map();
787
- for (const n of names) {
788
- const i = n.lastIndexOf('.owner-');
789
- if (i > 0) {
790
- const pid = positiveInt(n.slice(i + 7));
791
- if (pid) ownerByJob.set(n.slice(0, i), pid);
792
- }
793
- }
794
- const ids = names
795
- .filter((n) => n.endsWith('.json'))
796
- .map((n) => n.slice(0, -5))
797
- .filter((id) => !done.has(id) && ownerByJob.get(id) === ownerPid)
798
- .sort((a, b) => jobStampMs(b) - jobStampMs(a))
799
- .slice(0, 30);
800
- let count = 0;
801
- let oldestMs = Infinity;
802
- for (const id of ids) {
803
- const p = join(dir, `${id}.json`);
804
- let detail;
805
- try { detail = JSON.parse(readFileSync(p, 'utf-8')); } catch { continue; }
806
- if (!isShellJobAlive(detail, p, dir, id)) continue;
807
- count++;
808
- try {
809
- const st = statSync(p);
810
- if (st.mtimeMs < oldestMs) oldestMs = st.mtimeMs;
811
- } catch {}
812
- }
813
- if (!count) {
814
- _shellJobsSegmentCache = { ownerPid, at: now, value };
815
- return value;
816
- }
817
- const elapsedLabel = Number.isFinite(oldestMs) ? formatElapsed(Date.now() - oldestMs) : '';
818
- value = { count, elapsedLabel };
819
- } catch {
820
- value = empty;
821
- }
822
- _shellJobsSegmentCache = { ownerPid, at: now, value };
823
- return value;
824
- }
825
-
826
- function isShellJobAlive(detail, detailPath, dir, id) {
827
- const pid = positiveInt(detail?.pid);
828
- if (!pid) return false;
829
- try {
830
- const st = statSync(detailPath);
831
- const timeoutMs = Number(detail?.timeoutMs);
832
- const enforced = detail?.timeoutEnforced === true || existsSync(join(dir, `${id}.enforced`));
833
- if (enforced && Number.isFinite(timeoutMs) && timeoutMs > 0 && Date.now() - st.mtimeMs > timeoutMs + 30 * 60_000) {
834
- return false;
835
- }
836
- } catch {
837
- return false;
838
- }
839
- try {
840
- process.kill(pid, 0);
841
- return true;
842
- } catch (e) {
843
- return e?.code === 'EPERM';
844
- }
845
- }
846
-
847
- function jobStampMs(id) {
848
- const m = /^job_(\d+)/.exec(String(id || ''));
849
- return m ? Number(m[1]) : 0;
850
- }
851
-
852
- function positiveInt(value) {
853
- const n = Number(value);
854
- return Number.isInteger(n) && n > 0 ? n : 0;
855
- }
856
-
857
- // Byte-identical replica of src/tui/time-format.mjs formatDuration() with
858
- // DEFAULT options (no mostSignificantOnly / hideTrailingZeros), wrapped to drop
859
- // sub-1s like formatElapsed there. Output shape: '' (<1s), `Xs`, `Xm Ys`,
860
- // `Xh Ym Zs`, `Xd Yh Zm`. statusline.mjs is a standalone UI module that should
861
- // not depend on the React/ink TUI tree, so the algorithm is replicated rather
862
- // than imported. Used for ALL L2 elapsed (Agents/Explore/Search/Shell).
863
- function formatElapsed(ms) {
864
- if (!Number.isFinite(Number(ms))) return '';
865
- const value = Math.max(0, Number(ms) || 0);
866
- if (value < 60_000) {
867
- if (value < 1_000) return '';
868
- return `${Math.floor(value / 1000)}s`;
869
- }
870
- const days = Math.floor(value / 86_400_000);
871
- const hours = Math.floor((value % 86_400_000) / 3_600_000);
872
- const minutes = Math.floor((value % 3_600_000) / 60_000);
873
- const seconds = Math.floor((value % 60_000) / 1000);
874
- if (days > 0) return `${days}d ${hours}h ${minutes}m`;
875
- if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
876
- if (minutes > 0) return `${minutes}m ${seconds}s`;
877
- return `${seconds}s`;
878
- }
879
-
880
517
  /** Minimal one-line footer used when the vendored renderer is unavailable. */
881
518
  export function fallbackStatusline({
882
519
  provider = '', model = '', effort = '', fast = false, cwd = '', stats, contextWindow = 0,
@@ -913,25 +550,3 @@ function fallbackLine({
913
550
  if (!parts.length) return statusSubtle('> mixdog');
914
551
  return parts.join(sep);
915
552
  }
916
-
917
- function num(v) {
918
- const n = Number(v);
919
- return Number.isFinite(n) ? n : 0;
920
- }
921
-
922
- function clampPct(v) {
923
- const n = Number(v);
924
- if (!Number.isFinite(n)) return 0;
925
- return Math.max(0, Math.min(100, n));
926
- }
927
-
928
- /** Human-friendly token count: 1234 -> 1.2k. */
929
- function fmt(n) {
930
- const v = Number(n) || 0;
931
- if (v < 1000) return String(Math.round(v));
932
- if (v < 1_000_000) {
933
- const k = Math.round(v / 1000);
934
- return k >= 1000 ? '1M' : `${k}k`;
935
- }
936
- return (v / 1_000_000).toFixed(1) + 'M';
937
- }
@@ -21,7 +21,6 @@ const SUMMARIZERS = {
21
21
  glob: (a) => joinMaybe(a.pattern) ?? a.path,
22
22
  grep: (a) => joinMaybe(a.pattern),
23
23
  shell: (a) => a.command,
24
- diagnostics: (a) => a.path,
25
24
  };
26
25
 
27
26
  /**
@@ -539,7 +539,14 @@ export function loadGatewayStatus(options = {}) {
539
539
  : true;
540
540
  const metricsOwnCurrentSession = !!(sessionStatus || gatewayCcSessionMatch || gatewayTranscriptMatch || activeTranscriptMatch);
541
541
  const metricsMatch = gatewayClientHostMatch && metricsOwnCurrentSession && routeMatchesConfigured;
542
- const lastUsage = gatewayClientHostMatch && (sessionStatus || gatewayCcSessionMatch || gatewayTranscriptMatch) && active.gateway_last_usage && typeof active.gateway_last_usage === 'object'
542
+ // Strong ownership = this session's own status file or an explicit session/
543
+ // transcript id match from the gateway advert. `activeTranscriptMatch` (the
544
+ // global active-instance transcript merely naming the current session) is NOT
545
+ // strong: right after /new the global record can already point at the new
546
+ // session while its stored gateway metrics (context pct, last usage) still
547
+ // belong to the previous one.
548
+ const strongSessionOwnership = !!(sessionStatus || gatewayCcSessionMatch || gatewayTranscriptMatch);
549
+ const lastUsage = gatewayClientHostMatch && strongSessionOwnership && active.gateway_last_usage && typeof active.gateway_last_usage === 'object'
543
550
  ? active.gateway_last_usage
544
551
  : null;
545
552
  const lastCompact = lastUsage?.compact && typeof lastUsage.compact === 'object'
@@ -593,7 +600,13 @@ export function loadGatewayStatus(options = {}) {
593
600
  rawContextWindow: configured?.rawContextWindow ?? num(active.gateway_raw_context_window, null),
594
601
  effectiveContextWindowPercent: configured?.effectiveContextWindowPercent ?? num(active.gateway_effective_context_window_percent, null),
595
602
  autoCompactTokenLimit: statusAutoCompactTokenLimit,
596
- contextUsedPct: metricsMatch ? recomputedContextUsedPct ?? num(active.gateway_context_used_pct, null) : null,
603
+ // Live recompute (from the caller's own context tokens) may use the weaker
604
+ // metricsMatch gate, but the STORED gateway_context_used_pct fallback needs
605
+ // strong ownership — otherwise a fresh /new session with a not-yet-updated
606
+ // active-instance record would keep showing the old session's context %.
607
+ contextUsedPct: metricsMatch
608
+ ? recomputedContextUsedPct ?? (strongSessionOwnership ? num(active.gateway_context_used_pct, null) : null)
609
+ : null,
597
610
  lastUsage,
598
611
  quotaWindows: activeQuotaWindows.length ? activeQuotaWindows : (configuredStatus?.quotaWindows || []),
599
612
  balance: metricsMatch && active.gateway_balance && typeof active.gateway_balance === 'object' ? active.gateway_balance : (configuredStatus?.balance || null),