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
@@ -1,159 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import {
4
- remapCanonicalStatuslineTruecolor,
5
- normalizeStatuslineAnsi,
6
- statuslineFooterCacheKey,
7
- statuslineFooterIdentityChanged,
8
- isResetStatsState,
9
- STATUSLINE_CANONICAL_TRUECOLOR,
10
- } from './statusline-ansi-bridge.mjs';
11
- import { theme, setThemeSetting } from './theme.mjs';
12
-
13
- function sgr38(r, g, b) {
14
- return `\x1b[38;2;${r};${g};${b}m`;
15
- }
16
-
17
- function statusColorsFromTheme() {
18
- const ansiRgb = (value, fallback) => {
19
- const match = /^rgb\((\d+),(\d+),(\d+)\)$/.exec(String(value || '').replace(/\s+/g, ''));
20
- if (!match) return fallback;
21
- return sgr38(match[1], match[2], match[3]);
22
- };
23
- return {
24
- STATUS: ansiRgb(theme.statusText, sgr38(198, 198, 198)),
25
- SUBTLE: ansiRgb(theme.statusSubtle, sgr38(136, 136, 136)),
26
- SUCCESS: ansiRgb(theme.success, sgr38(0, 170, 75)),
27
- WARNING: ansiRgb(theme.warning, sgr38(255, 193, 7)),
28
- ERROR: ansiRgb(theme.error, sgr38(220, 70, 88)),
29
- };
30
- }
31
-
32
- test('remaps canonical truecolor sequences from ui/statusline.mjs', () => {
33
- const colors = statusColorsFromTheme();
34
- const subtle = sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle);
35
- const success = sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.success);
36
- const raw = `${subtle}│\x1b[0m ${success}42%`;
37
- const out = remapCanonicalStatuslineTruecolor(raw, colors);
38
- assert.ok(!out.includes('38;2;136;136;136'), 'subtle canonical RGB should be replaced');
39
- assert.ok(!out.includes('38;2;0;170;75'), 'success canonical RGB should be replaced');
40
- assert.ok(out.includes(colors.SUBTLE));
41
- assert.ok(out.includes(colors.SUCCESS));
42
- });
43
-
44
- test('normalizeStatuslineAnsi maps bold and legacy SGR plus truecolor', () => {
45
- const colors = statusColorsFromTheme();
46
- const raw = `\x1b[1mmodel\x1b[0m \x1b[33m5H\x1b[0m ${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.error)}quota`;
47
- const out = normalizeStatuslineAnsi(raw, colors);
48
- assert.ok(out.includes(colors.STATUS));
49
- assert.ok(out.includes(colors.WARNING));
50
- assert.ok(out.includes(colors.ERROR));
51
- assert.ok(!out.includes('\x1b[33m'));
52
- assert.ok(!out.includes('38;2;220;70;88'));
53
- });
54
-
55
- test('normalizeStatuslineAnsi preserves leading diamond glyph', () => {
56
- const colors = statusColorsFromTheme();
57
- const subtle = sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle);
58
- const raw = `${subtle}◆\x1b[0m \x1b[1mgpt-4o\x1b[0m`;
59
- const out = normalizeStatuslineAnsi(raw, colors);
60
- assert.ok(out.includes('◆'), 'visible ◆ must remain');
61
- assert.ok(out.includes('gpt-4o'));
62
- assert.ok(out.indexOf('◆') < out.indexOf('gpt-4o'));
63
- });
64
-
65
- test('retimes bare values after reset without clobbering explicit segment colors', () => {
66
- const colors = statusColorsFromTheme();
67
- const subtle = colors.SUBTLE;
68
- const success = colors.SUCCESS;
69
- const warning = colors.WARNING;
70
- const reset = '\x1b[0m';
71
- const creditSeg = `${subtle}Credit${reset} $12.34`;
72
- const outCredit = normalizeStatuslineAnsi(creditSeg, colors, { reset });
73
- assert.ok(outCredit.includes('$12.34'));
74
- const moneyIdx = outCredit.indexOf('$12.34');
75
- assert.ok(moneyIdx > 0);
76
- assert.ok(outCredit.slice(0, moneyIdx).includes(colors.STATUS), 'bare credit amount uses status text');
77
-
78
- const usedLimit = `${subtle}5H${reset} $3.00/$10.00`;
79
- const outUsed = normalizeStatuslineAnsi(usedLimit, colors, { reset });
80
- assert.ok(outUsed.includes('$3.00/$10.00'));
81
- assert.ok(outUsed.slice(0, outUsed.indexOf('$3.00')).includes(colors.STATUS));
82
-
83
- const colored = `${subtle}5H${reset} ${warning}est $5.00${reset}`;
84
- const outColored = normalizeStatuslineAnsi(colored, colors, { reset });
85
- assert.ok(outColored.includes(warning), 'warning color on value must remain');
86
- const estIdx = outColored.indexOf('est');
87
- assert.ok(estIdx > 0);
88
- assert.ok(outColored.slice(0, estIdx).includes(warning));
89
- });
90
-
91
- test('statuslineFooterCacheKey changes when session or route identity changes', () => {
92
- const a = statuslineFooterCacheKey({ sessionId: 's1', provider: 'p', model: 'm1' });
93
- const b = statuslineFooterCacheKey({ sessionId: 's2', provider: 'p', model: 'm1' });
94
- const c = statuslineFooterCacheKey({ sessionId: 's1', provider: 'p', model: 'm2' });
95
- assert.notEqual(a, b);
96
- assert.notEqual(a, c);
97
- });
98
-
99
- test('statusline footer identity tracks context display boundary fields', () => {
100
- const base = {
101
- sessionId: 's1',
102
- provider: 'p',
103
- model: 'm',
104
- contextWindow: 1_000_000,
105
- displayContextWindow: 900_000,
106
- rawContextWindow: 1_000_000,
107
- compactBoundaryTokens: 900_000,
108
- autoCompactTokenLimit: 810_000,
109
- stats: { currentEstimatedContextTokens: 10_000 },
110
- };
111
- const changedDisplay = { ...base, displayContextWindow: 800_000 };
112
- const changedBoundary = { ...base, compactBoundaryTokens: 800_000 };
113
- const changedTrigger = { ...base, autoCompactTokenLimit: 720_000 };
114
-
115
- assert.notEqual(statuslineFooterCacheKey(base), statuslineFooterCacheKey(changedDisplay));
116
- assert.notEqual(statuslineFooterCacheKey(base), statuslineFooterCacheKey(changedBoundary));
117
- assert.notEqual(statuslineFooterCacheKey(base), statuslineFooterCacheKey(changedTrigger));
118
- assert.equal(statuslineFooterIdentityChanged(changedDisplay, base), true);
119
- assert.equal(statuslineFooterIdentityChanged(changedBoundary, base), true);
120
- assert.equal(statuslineFooterIdentityChanged(changedTrigger, base), true);
121
- });
122
-
123
- test('stats reset transition is footer identity change (cache must not reuse on /theme)', () => {
124
- const activeStats = {
125
- currentContextTokens: 1200,
126
- inputTokens: 800,
127
- turns: 2,
128
- };
129
- const resetStats = {
130
- currentContextTokens: 0,
131
- currentEstimatedContextTokens: 0,
132
- inputTokens: 0,
133
- latestInputTokens: 0,
134
- promptTokens: 0,
135
- turns: 0,
136
- };
137
- assert.equal(isResetStatsState(resetStats), true);
138
- assert.equal(isResetStatsState(activeStats), false);
139
- const sameRoute = { sessionId: 's1', provider: 'p', model: 'm', stats: resetStats };
140
- const lastRoute = { sessionId: 's1', provider: 'p', model: 'm', stats: activeStats };
141
- assert.equal(statuslineFooterCacheKey(sameRoute), statuslineFooterCacheKey(lastRoute));
142
- assert.equal(statuslineFooterIdentityChanged(sameRoute, lastRoute), true);
143
- });
144
-
145
- test('theme switch changes normalized output when raw line is canonical', () => {
146
- setThemeSetting('basic', { persist: false });
147
- const colorsA = statusColorsFromTheme();
148
- const raw = `${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.success)}▓▓\x1b[0m ${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle)}░░`;
149
- const tonedA = normalizeStatuslineAnsi(raw, colorsA);
150
-
151
- setThemeSetting('teal', { persist: false });
152
- const colorsB = statusColorsFromTheme();
153
- const tonedB = normalizeStatuslineAnsi(raw, colorsB);
154
-
155
- assert.notEqual(tonedA, tonedB);
156
- assert.ok(tonedB.includes(colorsB.SUCCESS));
157
- assert.ok(tonedB.includes(colorsB.SUBTLE));
158
- setThemeSetting('basic', { persist: false });
159
- });
@@ -1,111 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import {
4
- formatHookDenialDetail,
5
- hasUsefulFailedToolResultBody,
6
- isFullyFailedToolBatch,
7
- isHookApprovalDenialToolItem,
8
- isHookApprovalDenialToolResult,
9
- shouldSuppressFullyFailedToolItem,
10
- } from './transcript-tool-failures.mjs';
11
-
12
- const hookDeniedItem = {
13
- kind: 'tool',
14
- name: 'shell',
15
- args: { command: 'rm -rf /' },
16
- result: 'Error: tool "shell" denied by hook: approval required but no approval UI is available (policy)',
17
- isError: true,
18
- count: 1,
19
- completedCount: 1,
20
- };
21
-
22
- const noisyFailedItem = {
23
- kind: 'tool',
24
- name: 'grep',
25
- args: { pattern: 'foo' },
26
- result: 'no matches',
27
- isError: true,
28
- count: 1,
29
- completedCount: 1,
30
- };
31
-
32
- const emptyFailedItem = {
33
- kind: 'tool',
34
- name: 'grep',
35
- args: { pattern: 'foo' },
36
- result: null,
37
- rawResult: null,
38
- isError: true,
39
- count: 1,
40
- completedCount: 1,
41
- };
42
-
43
- const failedTaskJsonStringArgs = {
44
- kind: 'tool',
45
- name: 'shell',
46
- args: '{"task_id":"t1","status":"failed","error":"boom"}',
47
- result: null,
48
- rawResult: null,
49
- isError: true,
50
- count: 1,
51
- completedCount: 1,
52
- };
53
-
54
- const failedTaskInputWrapperArgs = {
55
- kind: 'tool',
56
- name: 'shell',
57
- args: { input: { task_id: 't2', status: 'timeout' } },
58
- result: null,
59
- rawResult: null,
60
- isError: true,
61
- count: 1,
62
- completedCount: 1,
63
- };
64
-
65
- test('detects hook denial result text', () => {
66
- assert.equal(
67
- isHookApprovalDenialToolResult('Error: tool "read" denied by hook: blocked'),
68
- true,
69
- );
70
- assert.equal(
71
- isHookApprovalDenialToolResult('Error: tool "shell" denied by hook: approval required but no approval UI is available'),
72
- true,
73
- );
74
- assert.equal(isHookApprovalDenialToolResult('grep: no matches'), false);
75
- });
76
-
77
- test('fully-failed hook denials are not suppressed', () => {
78
- assert.equal(isFullyFailedToolBatch(hookDeniedItem), true);
79
- assert.equal(isHookApprovalDenialToolItem(hookDeniedItem), true);
80
- assert.equal(shouldSuppressFullyFailedToolItem(hookDeniedItem), false);
81
- });
82
-
83
- test('fully-failed non-hook errors with visible body stay visible', () => {
84
- assert.equal(isFullyFailedToolBatch(noisyFailedItem), true);
85
- assert.equal(hasUsefulFailedToolResultBody(noisyFailedItem), true);
86
- assert.equal(shouldSuppressFullyFailedToolItem(noisyFailedItem), false);
87
- });
88
-
89
- test('fully-failed non-hook errors without useful body stay suppressed', () => {
90
- assert.equal(isFullyFailedToolBatch(emptyFailedItem), true);
91
- assert.equal(hasUsefulFailedToolResultBody(emptyFailedItem), false);
92
- assert.equal(shouldSuppressFullyFailedToolItem(emptyFailedItem), true);
93
- });
94
-
95
- test('failed background task with JSON-string args is not suppressed', () => {
96
- assert.equal(isFullyFailedToolBatch(failedTaskJsonStringArgs), false);
97
- assert.equal(shouldSuppressFullyFailedToolItem(failedTaskJsonStringArgs), false);
98
- });
99
-
100
- test('failed background task with input-wrapped args is not suppressed', () => {
101
- assert.equal(isFullyFailedToolBatch(failedTaskInputWrapperArgs), false);
102
- assert.equal(shouldSuppressFullyFailedToolItem(failedTaskInputWrapperArgs), false);
103
- });
104
-
105
- test('formatHookDenialDetail strips tool prefix', () => {
106
- assert.equal(
107
- formatHookDenialDetail(hookDeniedItem.result),
108
- 'denied by hook: approval required but no approval UI is available (policy)',
109
- );
110
- });
111
-
@@ -1,70 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { env } from 'node:process';
4
- import { renderMarkdown } from './markdown.mjs';
5
- import { refreshColorSupport, stripAnsi } from './ansi.mjs';
6
-
7
- test.before(() => {
8
- env.FORCE_COLOR = '1';
9
- refreshColorSupport();
10
- });
11
-
12
- test('renderMarkdown never throws on garbage input', () => {
13
- assert.doesNotThrow(() => renderMarkdown('**unclosed `fence\n> quote\n'));
14
- assert.equal(renderMarkdown(null), '');
15
- });
16
-
17
- test('diff fenced blocks color hunks and +/- lines', () => {
18
- const src = [
19
- '```diff',
20
- '--- a/file.mjs',
21
- '+++ b/file.mjs',
22
- '@@ -1,2 +1,2 @@',
23
- '-old',
24
- '+new',
25
- '```',
26
- ].join('\n');
27
- const out = renderMarkdown(src);
28
- assert.match(out, /\x1b\[38;2;204;157;44/);
29
- assert.match(out, /\x1b\[38;2;0;170;75/);
30
- assert.match(out, /\x1b\[38;2;220;70;88/);
31
- assert.ok(stripAnsi(out).includes('diff'));
32
- });
33
-
34
- test('inline code and headings use Mixdog accent colors', () => {
35
- const out = renderMarkdown('# Title\nUse `code` here.');
36
- assert.match(out, /\x1b\[38;2;215;119;87/);
37
- assert.match(out, /\x1b\[38;2;138;190;183/);
38
- });
39
-
40
- test('longer fence opener does not close on shorter backtick line inside block', () => {
41
- const src = ['````', '```', 'still inside', '````'].join('\n');
42
- const plain = stripAnsi(renderMarkdown(src));
43
- assert.ok(plain.includes('still inside'));
44
- assert.ok(!plain.includes('```\nstill'));
45
- });
46
-
47
- test('mixed backtick-tilde closer does not end backtick fence', () => {
48
- const src = ['```', '``~', 'still inside', '```'].join('\n');
49
- const plain = stripAnsi(renderMarkdown(src));
50
- assert.ok(plain.includes('still inside'));
51
- assert.ok(plain.includes('``~'));
52
- });
53
-
54
- test('fenced code block body rows include right border', () => {
55
- const out = stripAnsi(renderMarkdown('```js\nx\n```'));
56
- const rows = out.split('\n').filter((line) => line.startsWith('│ '));
57
- assert.ok(rows.length >= 1);
58
- for (const row of rows) {
59
- assert.match(row, /│\s*$/);
60
- }
61
- });
62
-
63
- test('inline code inside link labels is preserved', () => {
64
- const out = renderMarkdown('[see `foo`](https://example.com)');
65
- const plain = stripAnsi(out);
66
- assert.ok(plain.includes('foo'));
67
- assert.ok(plain.includes('https://example.com'));
68
- assert.match(out, /\x1b\[38;2;138;190;183/);
69
- });
70
-
@@ -1,15 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { contextPctDisplayLabel } from './statusline.mjs';
4
-
5
- test('contextPctDisplayLabel clamps displayed percent at 100', () => {
6
- assert.equal(contextPctDisplayLabel(105), '100');
7
- assert.equal(contextPctDisplayLabel(100.9), '100');
8
- assert.equal(contextPctDisplayLabel(99.2), '99');
9
- });
10
-
11
- test('contextPctDisplayLabel keeps sub-1% decimal display', () => {
12
- assert.equal(contextPctDisplayLabel(0.45), '0.5');
13
- assert.equal(contextPctDisplayLabel(0.04), '0');
14
- });
15
-
@@ -1,186 +0,0 @@
1
- // mixdog statusline renderer — function form for in-daemon reuse.
2
- //
3
- // Mirrors `bin/statusline.mjs` exactly, but takes the CC stdin JSON as an
4
- // argument and returns the rendered status text instead of reading fd 0 and
5
- // writing to process.stdout. This allows the hook-pipe daemon to compute the
6
- // statusline without paying the ~100ms bun cold-start on every refresh tick.
7
-
8
- import fs from 'fs';
9
- import os from 'os';
10
- import http from 'node:http';
11
- import path from 'path';
12
- import { formatGatewayLimitSegments, loadGatewayStatus } from './statusline-route.mjs';
13
- import {
14
- isClaudeNativeModelSelection,
15
- isMixdogModelSelection,
16
- writeClaudeCodeCurrentSnapshot,
17
- } from '../src/gateway/claude-current.mjs';
18
- import {
19
- ANTHROPIC_OFFICIAL_BASE_URL,
20
- syncAnthropicBaseUrl,
21
- syncAutoCompactWindow,
22
- } from '../scripts/lib/gateway-settings.mjs';
23
- import {
24
- readGatewaySessionRoute,
25
- readLatestGatewayHostRoute,
26
- } from '../src/gateway/session-routes.mjs';
27
-
28
- function claudeConfigDir() {
29
- return process.env.MIXDOG_CONFIG_DIR || path.join(os.homedir(), '.mixdog');
30
- }
31
-
32
- function pluginDataDir() {
33
- return process.env.MIXDOG_DATA_DIR || path.join(process.env.MIXDOG_HOME || path.join(os.homedir(), '.mixdog'), 'data');
34
- }
35
-
36
- function writeFileIfChangedSync(file, content) {
37
- try {
38
- if (fs.existsSync(file) && fs.readFileSync(file, 'utf8') === content) return true;
39
- fs.mkdirSync(path.dirname(file), { recursive: true });
40
- fs.writeFileSync(file, content);
41
- return true;
42
- } catch {
43
- return false;
44
- }
45
- }
46
-
47
- function gatewayPort() {
48
- const envPort = Number(process.env.MIXDOG_GATEWAY_PORT);
49
- if (Number.isFinite(envPort) && envPort > 0) return Math.floor(envPort);
50
- try {
51
- const cfg = JSON.parse(fs.readFileSync(path.join(pluginDataDir(), 'mixdog-config.json'), 'utf8'));
52
- const port = Number(cfg?.gateway?.port);
53
- if (Number.isFinite(port) && port > 0) return Math.floor(port);
54
- } catch {}
55
- return 3468;
56
- }
57
-
58
- function readGatewayConfig() {
59
- try {
60
- return JSON.parse(fs.readFileSync(path.join(pluginDataDir(), 'mixdog-config.json'), 'utf8'));
61
- } catch {
62
- return null;
63
- }
64
- }
65
-
66
- function gatewayOwnsMainBaseUrl() {
67
- const cfg = readGatewayConfig();
68
- if (cfg?.modules?.gateway?.enabled !== true) return false;
69
- return cfg?.gateway?.mode === 'claude-current';
70
- }
71
-
72
- function settingsModelIsMixdog() {
73
- try {
74
- const settings = JSON.parse(fs.readFileSync(path.join(claudeConfigDir(), 'settings.json'), 'utf8'));
75
- return isMixdogModelSelection(settings?.model);
76
- } catch {
77
- return false;
78
- }
79
- }
80
-
81
- function routeUsesNonClaudeGateway(route) {
82
- if (!route || typeof route !== 'object') return false;
83
- const provider = String(route.defaultProvider || route.provider || '').trim();
84
- const model = String(route.defaultModel || route.model || '').trim();
85
- if (!provider || !model) return false;
86
- return provider !== 'anthropic-oauth' && provider !== 'anthropic';
87
- }
88
-
89
- function storedRouteNeedsGatewayStatus(sessionId, clientHostPid) {
90
- try {
91
- if (!(Number(clientHostPid) > 0)) return false;
92
- const route = readGatewaySessionRoute(sessionId, { clientHostPid })
93
- || readLatestGatewayHostRoute(clientHostPid, { excludeSessionId: sessionId });
94
- return routeUsesNonClaudeGateway(route);
95
- } catch {
96
- return false;
97
- }
98
- }
99
-
100
- function shouldLoadGatewayStatus(currentRoute, sessionId, clientHostPid) {
101
- if (process.env.MIXDOG_STATUSLINE_STANDALONE === '1') return false;
102
- if (process.env.MIXDOG_STANDALONE === '1') return true;
103
- if (!isClaudeNativeModelSelection(currentRoute)) return true;
104
- // Native model selection, but the gateway still owns the main base URL in
105
- // claude-current mode: every request flows through 3468, so the gateway's
106
- // oauth usage cache (5H/7D) is the only quota source — CC's stdin rate_limits
107
- // arrive empty on a gateway-proxied native route. Read the gateway status so
108
- // the usage segment renders instead of going blank.
109
- return gatewayOwnsMainBaseUrl()
110
- || storedRouteNeedsGatewayStatus(sessionId, clientHostPid)
111
- || settingsModelIsMixdog();
112
- }
113
-
114
- function traceStatusline(message) {
115
- if (!process.env.MIXDOG_STATUSLINE_TRACE) return;
116
- try {
117
- fs.appendFileSync(path.join(pluginDataDir(), 'statusline-trace.log'), `${new Date().toISOString()} ${message}\n`);
118
- } catch {}
119
- }
120
-
121
- function syncBaseUrlForCurrentModel(route) {
122
- if (!route) return;
123
- const target = isMixdogModelSelection(route) || gatewayOwnsMainBaseUrl() || settingsModelIsMixdog()
124
- ? `http://127.0.0.1:${gatewayPort()}`
125
- : isClaudeNativeModelSelection(route)
126
- ? ANTHROPIC_OFFICIAL_BASE_URL
127
- : '';
128
- if (!target) return;
129
- const result = syncAnthropicBaseUrl(target, { requireExisting: true });
130
- if (!result.ok) traceStatusline(`[base-url-sync] ${result.error || 'failed'}`);
131
- }
132
-
133
- function syncBaseUrlForGatewayStatus(status) {
134
- if (!status?.provider || !status?.model) return;
135
- const result = syncAnthropicBaseUrl(`http://127.0.0.1:${gatewayPort()}`, { requireExisting: true });
136
- if (!result.ok) traceStatusline(`[base-url-sync:gateway-status] ${result.error || 'failed'}`);
137
- }
138
-
139
- function syncCompactWindowValue(win, label) {
140
- if (!(Number.isFinite(Number(win)) && Number(win) > 0)) return;
141
- const result = syncAutoCompactWindow(Math.floor(Number(win)));
142
- if (!result.ok) traceStatusline(`[compact-window-sync:${label}] ${result.error || 'failed'}`);
143
- }
144
-
145
- function compactWindowForRouteLike(route) {
146
- if (!route || typeof route !== 'object') return null;
147
- const num = (v) => {
148
- const n = Number(v);
149
- return Number.isFinite(n) && n > 0 ? n : 0;
150
- };
151
- const explicit = num(route.autoCompactTokenLimit ?? route.auto_compact_token_limit);
152
- const raw = num(route.rawContextWindow ?? route.raw_context_window ?? route.contextWindow ?? route.context_window);
153
- const context = num(route.contextWindow ?? route.context_window);
154
- const pct = num(route.effectiveContextWindowPercent ?? route.effective_context_window_percent) || 90;
155
- const effective = context || (raw > 0 ? Math.max(1, Math.floor(raw * Math.min(100, pct) / 100)) : 0);
156
- if (explicit > 0 && effective > 0) return Math.min(explicit, effective);
157
- if (explicit > 0) return explicit;
158
- return effective > 0 ? effective : null;
159
- }
160
-
161
- function syncCompactWindowForCurrentModel(route) {
162
- if (!isClaudeNativeModelSelection(route)) return;
163
- try {
164
- syncCompactWindowValue(compactWindowForRouteLike(route), 'current');
165
- } catch (e) {
166
- traceStatusline(`[compact-window-sync:current] ${e?.message || e}`);
167
- }
168
- }
169
-
170
- function syncCompactWindowForGatewayStatus(status) {
171
- if (!status?.provider || !status?.model) return;
172
- syncCompactWindowValue(compactWindowForRouteLike(status), 'gateway-status');
173
- }
174
-
175
- function writeStatuslineLastSnapshot(json) {
176
- if (!json) return;
177
- try { JSON.parse(json); } catch { return; }
178
- try {
179
- const file = path.join(claudeConfigDir(), 'cc-statusline-last.json');
180
- const tmp = `${file}.${process.pid}.tmp`;
181
- if (fs.existsSync(file) && fs.readFileSync(file, 'utf8') === json) return;
182
- fs.mkdirSync(path.dirname(file), { recursive: true });
183
- fs.writeFileSync(tmp, json);
184
- fs.renameSync(tmp, file);
185
- } catch {}
186
- }
@@ -1,80 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { formatGatewayLimitSegments } from './statusline-route.mjs';
4
-
5
- test('formatGatewayLimitSegments hides OpenAI OAuth credit balance on L1', () => {
6
- const segments = formatGatewayLimitSegments({
7
- provider: 'openai-oauth',
8
- providerKind: 'oauth',
9
- source: 'openai-codex-wham',
10
- quotaWindows: [
11
- { label: '5H', source: 'openai-codex-oauth', usedPct: 42 },
12
- { label: '7D', source: 'openai-codex-oauth', usedPct: 5 },
13
- ],
14
- balance: {
15
- source: 'openai-codex-credits',
16
- remainingUsd: 12.34,
17
- },
18
- }, { COLS: 120 });
19
-
20
- assert.deepEqual(segments, ['5H 42%', '7D 5%']);
21
- assert.equal(segments.some((segment) => /Credit|\$12\.34/.test(segment)), false);
22
- });
23
-
24
- test('formatGatewayLimitSegments falls back to percent for OpenAI OAuth credit windows', () => {
25
- const segments = formatGatewayLimitSegments({
26
- provider: 'openai-oauth',
27
- providerKind: 'oauth',
28
- quotaWindows: [
29
- {
30
- label: '5H',
31
- source: 'openai-codex-oauth',
32
- usedPct: 55,
33
- remainingUsd: 4,
34
- usedUsd: 6,
35
- limitUsd: 10,
36
- remainingCredits: 40,
37
- usedCredits: 60,
38
- limitCredits: 100,
39
- },
40
- ],
41
- }, { COLS: 120 });
42
-
43
- assert.deepEqual(segments, ['5H 55%']);
44
- });
45
-
46
- test('formatGatewayLimitSegments keeps non-OpenAI OAuth credit balance on L1', () => {
47
- const segments = formatGatewayLimitSegments({
48
- provider: 'anthropic-oauth',
49
- providerKind: 'oauth',
50
- source: 'anthropic-oauth',
51
- quotaWindows: [
52
- { label: '5H', source: 'anthropic-oauth', usedPct: 20 },
53
- ],
54
- balance: {
55
- source: 'anthropic-oauth-extra',
56
- remainingUsd: 7.5,
57
- },
58
- }, { COLS: 120 });
59
-
60
- assert.ok(segments.includes('Credit $7.50'));
61
- });
62
-
63
- test('formatGatewayLimitSegments suppresses OpenAI OAuth credit via providerId even without a source hint', () => {
64
- // Mirrors the merged quotaStatus shape (mergeQuotaStatus) where `source` may
65
- // be absent and `balance.source` is generic: the providerId === openai-oauth
66
- // branch alone must still suppress any Credit/$ leak on L1.
67
- const segments = formatGatewayLimitSegments({
68
- provider: 'openai-oauth',
69
- providerKind: 'oauth',
70
- quotaWindows: [
71
- { label: '5H', source: 'openai-codex-oauth', usedPct: 30 },
72
- ],
73
- balance: {
74
- remainingUsd: 99.99,
75
- },
76
- }, { COLS: 120 });
77
-
78
- assert.deepEqual(segments, ['5H 30%']);
79
- assert.equal(segments.some((segment) => /Credit|\$/.test(segment)), false);
80
- });