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,213 @@
1
+ /**
2
+ * src/ui/statusline-agents.mjs — agent worker/job normalization for the L2 line.
3
+ *
4
+ * Extracted verbatim from statusline.mjs: normalize/merge REPL agent workers &
5
+ * bridge jobs into a statusline payload, classify running vs hidden-maintenance
6
+ * workers, and enumerate active hidden agents from live session runtimes. No
7
+ * behavior change.
8
+ */
9
+ import { forEachSessionRuntime } from '../runtime/agent/orchestrator/session/manager.mjs';
10
+ import { listHiddenAgentNames } from '../runtime/agent/orchestrator/internal-agents.mjs';
11
+ import { num, GRN, R, B } from './statusline-format.mjs';
12
+
13
+ const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
14
+ let _hiddenStatuslineAgents = null;
15
+
16
+ export function summarizeWorkerTags(workers, limit = 3) {
17
+ const cleanLabels = [...new Set((Array.isArray(workers) ? workers : [])
18
+ .map((worker) => String(worker?.tag || '').trim())
19
+ .filter(Boolean))];
20
+ if (cleanLabels.length <= limit) return cleanLabels.join(', ');
21
+ return `${cleanLabels.slice(0, limit).join(', ')}, +${cleanLabels.length - limit}`;
22
+ }
23
+
24
+ function normalizeAgentWorkerForStatusline(worker = {}) {
25
+ const tag = String(worker.tag || worker.agent || worker.name || '').trim();
26
+ if (!tag) return null;
27
+ const statusText = String(worker.stage || worker.status || '').toLowerCase();
28
+ const status = isTerminalBridgeStatus(statusText) ? 'idle' : 'running';
29
+ return {
30
+ tag,
31
+ status,
32
+ startedAtMs: timeMs(worker.startedAt || worker.startTime || worker.createdAt),
33
+ agent: worker.agent || null,
34
+ stage: worker.stage || worker.status || null,
35
+ sessionId: worker.sessionId || null,
36
+ provider: worker.provider || null,
37
+ model: worker.model || null,
38
+ };
39
+ }
40
+
41
+ function normalizeAgentJobForStatusline(job = {}) {
42
+ const statusText = String(job.status || job.stage || '').toLowerCase();
43
+ if (!statusText) return null;
44
+ const taskId = String(job.task_id || job.taskId || '').trim();
45
+ const tag = String(job.tag || job.agent || job.type || taskId || '').trim();
46
+ if (!tag && !taskId) return null;
47
+ const startedAtMs = timeMs(job.startedAt);
48
+ const finishedAtMs = timeMs(job.finishedAt);
49
+ if (isTerminalBridgeStatus(statusText) && finishedAtMs > 0) {
50
+ return {
51
+ tag,
52
+ taskId,
53
+ status: 'finished',
54
+ finalStatus: statusText,
55
+ startedAtMs,
56
+ finishedAtMs,
57
+ agent: job.agent || null,
58
+ stage: job.stage || job.workerStatus || job.status || null,
59
+ sessionId: job.sessionId || null,
60
+ provider: job.provider || null,
61
+ model: job.model || null,
62
+ };
63
+ }
64
+ if (!/running/.test(statusText)) return null;
65
+ return {
66
+ tag,
67
+ taskId,
68
+ status: 'running',
69
+ startedAtMs,
70
+ agent: job.agent || null,
71
+ stage: job.stage || job.workerStatus || job.status || null,
72
+ sessionId: job.sessionId || null,
73
+ provider: job.provider || null,
74
+ model: job.model || null,
75
+ };
76
+ }
77
+
78
+ export function agentStatuslinePayload(agentWorkers = [], agentJobs = []) {
79
+ const byTag = new Map();
80
+ const finishedJobs = [];
81
+ for (const worker of Array.isArray(agentWorkers) ? agentWorkers : []) {
82
+ const row = normalizeAgentWorkerForStatusline(worker);
83
+ if (row) byTag.set(row.tag, row);
84
+ }
85
+ for (const job of Array.isArray(agentJobs) ? agentJobs : []) {
86
+ const row = normalizeAgentJobForStatusline(job);
87
+ if (!row) continue;
88
+ if (row.status === 'finished') {
89
+ finishedJobs.push(row);
90
+ continue;
91
+ }
92
+ const prev = byTag.get(row.tag);
93
+ byTag.set(row.tag, { ...(prev || {}), ...row, status: 'running' });
94
+ }
95
+ const workers = [...byTag.values()];
96
+ return {
97
+ workers,
98
+ finishedJobs: finishedJobs.sort((a, b) => (b.finishedAtMs || 0) - (a.finishedAtMs || 0)),
99
+ sessions: {
100
+ roles: workers.filter((w) => w.status !== 'idle').map((w) => w.tag),
101
+ workers,
102
+ },
103
+ };
104
+ }
105
+
106
+ export function classifyAgentWorkers(workers = []) {
107
+ const maintenance = [];
108
+ const runningWorkers = [];
109
+ const seenMaintenance = new Set();
110
+ const seenRunning = new Set();
111
+ for (const w of Array.isArray(workers) ? workers : []) {
112
+ const tag = String(w?.tag || '').trim();
113
+ if (!tag) continue;
114
+ const maint = hiddenWorkerLabel(w);
115
+ if (maint) {
116
+ if (w.status !== 'idle' && !seenMaintenance.has(maint)) {
117
+ seenMaintenance.add(maint);
118
+ maintenance.push(`${GRN}↻${R} ${B}${maint}${R}`);
119
+ }
120
+ continue;
121
+ }
122
+ if (w.status !== 'idle' && !seenRunning.has(tag)) {
123
+ seenRunning.add(tag);
124
+ runningWorkers.push(w);
125
+ }
126
+ }
127
+ return { maintenance, runningWorkers };
128
+ }
129
+
130
+ function hiddenWorkerLabel(worker = {}) {
131
+ const agent = String(worker?.agent || '').trim();
132
+ const tag = String(worker?.tag || '').trim();
133
+ return maintenanceLabel(agent) || (!agent ? maintenanceLabel(tag) : '');
134
+ }
135
+
136
+ function hiddenStatuslineAgents() {
137
+ if (_hiddenStatuslineAgents) return _hiddenStatuslineAgents;
138
+ const agents = new Set(DEFAULT_HIDDEN_STATUSLINE_AGENTS);
139
+ try {
140
+ for (const agent of listHiddenAgentNames()) {
141
+ const clean = String(agent || '').trim();
142
+ if (clean) agents.add(clean);
143
+ }
144
+ } catch {}
145
+ _hiddenStatuslineAgents = agents;
146
+ return agents;
147
+ }
148
+
149
+ function isActiveHiddenStatus(statusText) {
150
+ return /^(connecting|requesting|streaming|tool_running|running)$/i.test(String(statusText || '').trim());
151
+ }
152
+
153
+ export function activeHiddenAgentWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
154
+ const agents = hiddenStatuslineAgents();
155
+ const ownerPid = positiveInt(clientHostPid);
156
+ const ownerSessionId = String(sessionId || '').trim();
157
+ const rows = [];
158
+ try {
159
+ for (const [runtimeSessionId, entry] of forEachSessionRuntime() || []) {
160
+ if (!entry || entry.closed === true) continue;
161
+ const session = entry.session || null;
162
+ if (!session || session.closed === true) continue;
163
+ const agent = String(session?.agent || '').trim();
164
+ if (!agent || !agents.has(agent)) continue;
165
+ const id = session?.id || runtimeSessionId || null;
166
+ if (ownerSessionId && id === ownerSessionId) continue;
167
+ const sessionOwnerId = String(session?.ownerSessionId || '').trim();
168
+ if (sessionOwnerId && ownerSessionId && sessionOwnerId !== ownerSessionId) continue;
169
+ const pid = positiveInt(session?.clientHostPid);
170
+ if (ownerPid && pid && pid !== ownerPid) continue;
171
+ const stage = String(entry.stage || session?.stage || session?.status || '').trim().toLowerCase();
172
+ const status = String(session?.status || stage || '').trim().toLowerCase();
173
+ if (!isActiveHiddenStatus(stage || status)) continue;
174
+ rows.push({
175
+ tag: String(session?.agentTag || `${agent}:${id || rows.length}`).trim(),
176
+ agent,
177
+ status: 'running',
178
+ stage: stage || status || 'running',
179
+ sessionId: id,
180
+ provider: session?.provider || null,
181
+ model: session?.model || null,
182
+ });
183
+ }
184
+ } catch {}
185
+ return rows;
186
+ }
187
+
188
+ function isTerminalBridgeStatus(statusText) {
189
+ return /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/.test(String(statusText || '').toLowerCase());
190
+ }
191
+
192
+ function timeMs(value) {
193
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
194
+ const n = Date.parse(String(value || ''));
195
+ return Number.isFinite(n) ? n : 0;
196
+ }
197
+
198
+ function maintenanceLabel(tag) {
199
+ switch (tag) {
200
+ case 'cycle1-agent': return 'cycle1';
201
+ case 'cycle2-agent': return 'cycle2';
202
+ case 'cycle3-agent': return 'cycle3';
203
+ case 'scheduler-task': return 'scheduler';
204
+ case 'webhook-handler': return 'webhook';
205
+ case 'explorer': return 'explorer';
206
+ default: return '';
207
+ }
208
+ }
209
+
210
+ function positiveInt(value) {
211
+ const n = Number(value);
212
+ return Number.isInteger(n) && n > 0 ? n : 0;
213
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * src/ui/statusline-format.mjs — pure formatting primitives for the footer.
3
+ *
4
+ * Extracted verbatim from statusline.mjs: ANSI SGR constants, the context%
5
+ * bar/segment formatters, small numeric helpers, and the TUI-independent
6
+ * formatElapsed() replica. No behavior change — statusline.mjs re-imports these.
7
+ */
8
+ import { colorEnabled, rgb } from './ansi.mjs';
9
+ import { displayModelName, shortenModelName } from './model-display.mjs';
10
+ import { getModelMetadataSync } from '../runtime/agent/orchestrator/providers/model-catalog.mjs';
11
+
12
+ // Token window used to compute a fallback context% from our own session usage.
13
+ // The live gateway (when up) overrides this with the real route's window. This
14
+ // is only the last resort for unknown local models.
15
+ export const FALLBACK_CONTEXT_WINDOW = 200000;
16
+ export const statusText = rgb(198, 198, 198);
17
+ export const statusSubtle = rgb(136, 136, 136);
18
+ export const statusAccent = rgb(215, 119, 87);
19
+
20
+ function sgr(code) {
21
+ return colorEnabled() ? `\x1b[${code}m` : '';
22
+ }
23
+
24
+ export const R = sgr('0');
25
+ export const B = sgr('1');
26
+ export const D = sgr('38;2;136;136;136');
27
+ export const GRN = sgr('38;2;0;170;75');
28
+ export const YLW = sgr('38;2;255;193;7');
29
+ export const RED = sgr('38;2;220;70;88');
30
+ export const CYN = sgr('38;2;136;136;136');
31
+ export const GREY = sgr('38;2;136;136;136');
32
+
33
+ export function terminalColumns() {
34
+ const cols = Number(process.stdout?.columns);
35
+ return Number.isFinite(cols) && cols > 0 ? Math.floor(cols) : 120;
36
+ }
37
+
38
+ export function modelContextWindow(provider, model, explicitContextWindow = 0) {
39
+ const explicit = num(explicitContextWindow);
40
+ if (explicit > 0) return explicit;
41
+ const metaWindow = num(getModelMetadataSync(model, provider)?.contextWindow);
42
+ if (metaWindow > 0) return metaWindow;
43
+ return FALLBACK_CONTEXT_WINDOW;
44
+ }
45
+
46
+ export function formatModelSegment({ provider, model, effort, fast, cols }) {
47
+ const raw = String(model || '').trim();
48
+ const meta = getModelMetadataSync(raw, provider) || {};
49
+ const displayHint = String(meta.displayName || meta.display || meta.name || '').trim();
50
+ const modelName = shortenModelName(displayModelName(raw, provider, displayHint), cols);
51
+ const bits = [`${B}${modelName}${R}`];
52
+ if (effort) bits.push(`${B}${String(effort).toUpperCase()}${R}`);
53
+ if (fast === true) bits.push(`${B}FAST${R}`);
54
+ return bits.join(` ${D}·${R} `);
55
+ }
56
+
57
+ /** Display label for context % (clamped to 100); raw pct still drives bar/color thresholds. */
58
+ export function contextPctDisplayLabel(ctxPct) {
59
+ const pct = Number(ctxPct);
60
+ if (!Number.isFinite(pct) || pct <= 0) return '0';
61
+ if (pct > 0 && pct < 1) return String(Math.round(pct * 10) / 10);
62
+ return String(Math.floor(Math.min(100, pct)));
63
+ }
64
+
65
+ export function formatContextSegment(ctxPct, cols) {
66
+ const raw = Number(ctxPct);
67
+ const pct = Number.isFinite(raw) ? Math.max(0, raw) : 0;
68
+ const barPct = clampPct(pct);
69
+ const fill = pct >= 90 ? RED : pct >= 70 ? YLW : GRN;
70
+ const label = contextPctDisplayLabel(pct);
71
+ // Keep a full-width bar wherever there is room for one. Below 80 cols the bar
72
+ // is dropped (label-only) so the footer never overflows a narrow terminal;
73
+ // at 80+ it stays a fixed 14 cells instead of shrinking to 8, which read as
74
+ // too small/cramped on mid-width terminals.
75
+ const cells = cols >= 80 ? 14 : 0;
76
+ if (!cells) return `${fill}${label}%${R}`;
77
+ const bar = makeBar(barPct, cells);
78
+ const filled = bar.replace(/░/g, '');
79
+ const empty = bar.replace(/▓/g, '');
80
+ return `${fill}${filled}${R}${D}${empty}${R} ${label}%`;
81
+ }
82
+
83
+ export function makeBar(pct, cells) {
84
+ let filled = Math.floor((Number(pct) || 0) * cells / 100);
85
+ if (filled < 0) filled = 0;
86
+ if (filled > cells) filled = cells;
87
+ if (pct >= 1 && filled === 0) filled = 1;
88
+ return '▓'.repeat(filled) + '░'.repeat(cells - filled);
89
+ }
90
+
91
+ export function colourPct(p) {
92
+ if (p >= 90) return `${RED}${p}%${R}`;
93
+ if (p >= 70) return `${YLW}${p}%${R}`;
94
+ return `${GRN}${p}%${R}`;
95
+ }
96
+
97
+ export function epochMsToHHMM(ms) {
98
+ const d = new Date(Number(ms));
99
+ if (!Number.isFinite(d.getTime())) return '';
100
+ return d.toLocaleTimeString('sv-SE', { hour: '2-digit', minute: '2-digit', hour12: false });
101
+ }
102
+
103
+ export function num(v) {
104
+ const n = Number(v);
105
+ return Number.isFinite(n) ? n : 0;
106
+ }
107
+
108
+ export function clampPct(v) {
109
+ const n = Number(v);
110
+ if (!Number.isFinite(n)) return 0;
111
+ return Math.max(0, Math.min(100, n));
112
+ }
113
+
114
+ /** Human-friendly token count: 1234 -> 1.2k. */
115
+ export function fmt(n) {
116
+ const v = Number(n) || 0;
117
+ if (v < 1000) return String(Math.round(v));
118
+ if (v < 1_000_000) {
119
+ const k = Math.round(v / 1000);
120
+ return k >= 1000 ? '1M' : `${k}k`;
121
+ }
122
+ return (v / 1_000_000).toFixed(1) + 'M';
123
+ }
124
+
125
+ // Byte-identical replica of src/tui/time-format.mjs formatDuration() with
126
+ // DEFAULT options (no mostSignificantOnly / hideTrailingZeros), wrapped to drop
127
+ // sub-1s like formatElapsed there. Output shape: '' (<1s), `Xs`, `Xm Ys`,
128
+ // `Xh Ym Zs`, `Xd Yh Zm`. statusline.mjs is a standalone UI module that should
129
+ // not depend on the React/ink TUI tree, so the algorithm is replicated rather
130
+ // than imported. Used for ALL L2 elapsed (Agents/Explore/Search/Shell).
131
+ export function formatElapsed(ms) {
132
+ if (!Number.isFinite(Number(ms))) return '';
133
+ const value = Math.max(0, Number(ms) || 0);
134
+ if (value < 60_000) {
135
+ if (value < 1_000) return '';
136
+ return `${Math.floor(value / 1000)}s`;
137
+ }
138
+ const days = Math.floor(value / 86_400_000);
139
+ const hours = Math.floor((value % 86_400_000) / 3_600_000);
140
+ const minutes = Math.floor((value % 3_600_000) / 60_000);
141
+ const seconds = Math.floor((value % 60_000) / 1000);
142
+ if (days > 0) return `${days}d ${hours}h ${minutes}m`;
143
+ if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
144
+ if (minutes > 0) return `${minutes}m ${seconds}s`;
145
+ return `${seconds}s`;
146
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * src/ui/statusline-segments.mjs — filesystem-backed L2 segment sources.
3
+ *
4
+ * Extracted verbatim from statusline.mjs: the shell-jobs segment (owner-scoped
5
+ * job scan + liveness) and the memory-cycle segment (daemon state file). Both
6
+ * keep their own 1s process-local caches. No behavior change.
7
+ */
8
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { formatElapsed } from './statusline-format.mjs';
12
+
13
+ const DEFAULT_MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
14
+ const DEFAULT_STANDALONE_DATA_DIR = join(DEFAULT_MIXDOG_HOME, 'data');
15
+ const SHELL_JOBS_SEGMENT_CACHE_MS = 1000;
16
+
17
+ let _shellJobsSegmentCache = { ownerPid: 0, at: 0, value: { count: 0, elapsedLabel: '' } };
18
+
19
+ function dataDir() {
20
+ return process.env.MIXDOG_DATA_DIR || DEFAULT_STANDALONE_DATA_DIR;
21
+ }
22
+
23
+ export function shellJobsStatus({ clientHostPid } = {}) {
24
+ return _shellJobsStatusImpl({ clientHostPid });
25
+ }
26
+
27
+ // Memory cycle L2 segment source. The memory daemon writes
28
+ // data/memory-cycle-state.json on every cycle start/finish (index.mjs
29
+ // _writeCycleStateFile) — no HTTP call from the statusline path. Cached at
30
+ // the same 1s cadence as the shell segment. Shows a single unified "Memory"
31
+ // segment: running (spinner + elapsed) or backlog warning (yellow count).
32
+ let _memoryCycleSegmentCache = { at: 0, value: null };
33
+ const MEMORY_CYCLE_SEGMENT_CACHE_MS = 1000;
34
+ const MEMORY_CYCLE_BACKLOG_WARN = 500;
35
+ export function memoryCycleStatus() {
36
+ const now = Date.now();
37
+ if (now - _memoryCycleSegmentCache.at < MEMORY_CYCLE_SEGMENT_CACHE_MS) {
38
+ return _memoryCycleSegmentCache.value;
39
+ }
40
+ let value = null;
41
+ try {
42
+ const p = join(dataDir(), 'memory-cycle-state.json');
43
+ if (existsSync(p)) {
44
+ const state = JSON.parse(readFileSync(p, 'utf-8'));
45
+ const running = state?.running || null;
46
+ const backlog = state?.backlog || {};
47
+ // Stale-file guard: a daemon that died mid-run leaves running set —
48
+ // ignore anything not refreshed in the last 10 minutes.
49
+ const fresh = Number(state?.updatedAt) > now - 10 * 60_000;
50
+ if (fresh && running?.cycle && Number(running.started_at) > 0) {
51
+ value = { kind: 'running', startedAt: Number(running.started_at) };
52
+ } else if (fresh) {
53
+ const pending = Math.max(Number(backlog?.unchunked) || 0, Number(backlog?.cycle2_pending) || 0);
54
+ if (pending > MEMORY_CYCLE_BACKLOG_WARN) value = { kind: 'backlog', pending };
55
+ }
56
+ }
57
+ } catch { value = null; }
58
+ _memoryCycleSegmentCache = { at: now, value };
59
+ return value;
60
+ }
61
+
62
+ function _shellJobsStatusImpl({ clientHostPid } = {}) {
63
+ const ownerPid = positiveInt(clientHostPid);
64
+ const empty = { count: 0, elapsedLabel: '' };
65
+ if (!ownerPid) return empty;
66
+ const now = Date.now();
67
+ if (_shellJobsSegmentCache.ownerPid === ownerPid && now - _shellJobsSegmentCache.at < SHELL_JOBS_SEGMENT_CACHE_MS) {
68
+ return _shellJobsSegmentCache.value || empty;
69
+ }
70
+ let value = empty;
71
+ try {
72
+ const dir = join(dataDir(), 'shell-jobs');
73
+ if (!existsSync(dir)) {
74
+ _shellJobsSegmentCache = { ownerPid, at: now, value };
75
+ return value;
76
+ }
77
+ const names = readdirSync(dir);
78
+ const done = new Set(names.filter((n) => n.endsWith('.done')).map((n) => n.slice(0, -5)));
79
+ const ownerByJob = new Map();
80
+ for (const n of names) {
81
+ const i = n.lastIndexOf('.owner-');
82
+ if (i > 0) {
83
+ const pid = positiveInt(n.slice(i + 7));
84
+ if (pid) ownerByJob.set(n.slice(0, i), pid);
85
+ }
86
+ }
87
+ const ids = names
88
+ .filter((n) => n.endsWith('.json'))
89
+ .map((n) => n.slice(0, -5))
90
+ .filter((id) => !done.has(id) && ownerByJob.get(id) === ownerPid)
91
+ .sort((a, b) => jobStampMs(b) - jobStampMs(a))
92
+ .slice(0, 30);
93
+ let count = 0;
94
+ let oldestMs = Infinity;
95
+ for (const id of ids) {
96
+ const p = join(dir, `${id}.json`);
97
+ let detail;
98
+ try { detail = JSON.parse(readFileSync(p, 'utf-8')); } catch { continue; }
99
+ if (!isShellJobAlive(detail, p, dir, id)) continue;
100
+ count++;
101
+ try {
102
+ const st = statSync(p);
103
+ if (st.mtimeMs < oldestMs) oldestMs = st.mtimeMs;
104
+ } catch {}
105
+ }
106
+ if (!count) {
107
+ _shellJobsSegmentCache = { ownerPid, at: now, value };
108
+ return value;
109
+ }
110
+ const elapsedLabel = Number.isFinite(oldestMs) ? formatElapsed(Date.now() - oldestMs) : '';
111
+ value = { count, elapsedLabel };
112
+ } catch {
113
+ value = empty;
114
+ }
115
+ _shellJobsSegmentCache = { ownerPid, at: now, value };
116
+ return value;
117
+ }
118
+
119
+ function isShellJobAlive(detail, detailPath, dir, id) {
120
+ const pid = positiveInt(detail?.pid);
121
+ if (!pid) return false;
122
+ try {
123
+ const st = statSync(detailPath);
124
+ const timeoutMs = Number(detail?.timeoutMs);
125
+ const enforced = detail?.timeoutEnforced === true || existsSync(join(dir, `${id}.enforced`));
126
+ if (enforced && Number.isFinite(timeoutMs) && timeoutMs > 0 && Date.now() - st.mtimeMs > timeoutMs + 30 * 60_000) {
127
+ return false;
128
+ }
129
+ } catch {
130
+ return false;
131
+ }
132
+ try {
133
+ process.kill(pid, 0);
134
+ return true;
135
+ } catch (e) {
136
+ return e?.code === 'EPERM';
137
+ }
138
+ }
139
+
140
+ function jobStampMs(id) {
141
+ const m = /^job_(\d+)/.exec(String(id || ''));
142
+ return m ? Number(m[1]) : 0;
143
+ }
144
+
145
+ function positiveInt(value) {
146
+ const n = Number(value);
147
+ return Number.isInteger(n) && n > 0 ? n : 0;
148
+ }