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,427 @@
1
+ /**
2
+ * slash-dispatch.mjs — the runSlashCommand slash-command dispatcher.
3
+ *
4
+ * Extracted from App.jsx behavior-preservingly as a dependency-injection
5
+ * factory: the switch body reads live store/state and drives many pickers +
6
+ * openers, so it can't be pure. The switch body below is the original App
7
+ * logic verbatim (case ordering + fallthrough semantics byte-identical), with
8
+ * every closure identifier threaded in through the factory argument. Openers
9
+ * defined later in the App factory zone are passed as lazy getters so their
10
+ * live binding is used at call time.
11
+ */
12
+ export function createSlashDispatch({
13
+ state,
14
+ store,
15
+ normalizeSlashCommandName,
16
+ setContextPanel,
17
+ closeUsagePanel,
18
+ openModelPicker,
19
+ modelSwitchNotice,
20
+ toggleVoice,
21
+ setVoiceEnabled,
22
+ isVoiceEnabled,
23
+ openSearchPicker,
24
+ openAgentsPicker,
25
+ openWorkflowPicker,
26
+ workflowSwitchNotice,
27
+ openOutputStylePicker,
28
+ outputStyleNotice,
29
+ openThemePicker,
30
+ themeNotice,
31
+ openEffortPicker,
32
+ projectNameFromPath,
33
+ enterProject,
34
+ openProjectPicker,
35
+ openToolsPicker,
36
+ openMcpPicker,
37
+ openSkillsPicker,
38
+ openPluginsPicker,
39
+ openHooksPicker,
40
+ openProviderSetupPicker,
41
+ openChannelSetupPicker,
42
+ openMemoryCorePicker,
43
+ parseMemoryCommand,
44
+ openSettingsPicker,
45
+ openAutoClearPicker,
46
+ formatDuration,
47
+ openResumePicker,
48
+ openUsagePanel,
49
+ openContextPicker,
50
+ openProfilePicker,
51
+ openUpdatePicker,
52
+ requestExit,
53
+ }) {
54
+ const runSlashCommand = (cmd, arg = '') => {
55
+ const rawName = String(cmd || '').toLowerCase();
56
+ cmd = normalizeSlashCommandName(cmd);
57
+ if (cmd !== 'context') setContextPanel(null);
58
+ if (cmd !== 'usage') closeUsagePanel();
59
+ switch (cmd) {
60
+ case 'clear':
61
+ if (state.busy) {
62
+ store.pushNotice('wait for the current turn to finish before /clear', 'warn');
63
+ return false;
64
+ }
65
+ if (rawName === 'new') {
66
+ void store.newSession().then(() => {}).catch((e) => store.pushNotice(`new session failed: ${e?.message || e}`, 'error'));
67
+ } else {
68
+ void store.clear().then(() => {}).catch((e) => store.pushNotice(`clear failed: ${e?.message || e}`, 'error'));
69
+ }
70
+ return true;
71
+ case 'model':
72
+ if (!arg) {
73
+ openModelPicker();
74
+ return true;
75
+ }
76
+ void store.setModel(arg)
77
+ .then(ok => store.pushNotice(ok ? modelSwitchNotice() : 'Model switch is already running.', ok ? 'info' : 'warn'))
78
+ .catch((e) => store.pushNotice(`Couldn’t switch model: ${e?.message || e}`, 'error'));
79
+ return true;
80
+ case 'remote': {
81
+ // /remote = force-claim, not toggle: always turns remote ON for THIS
82
+ // session and steals the seat from any other session (which flips
83
+ // itself OFF via the superseded notification). Turn off via /channels.
84
+ const enabled = store.claimRemote?.() === true;
85
+ store.pushNotice(enabled ? 'Remote mode ON — this session owns remote now.' : 'Remote mode unavailable.', 'info');
86
+ return true;
87
+ }
88
+ case 'voice': {
89
+ // Step1 only: toggleVoice() owns config persistence (voice.enabled) +
90
+ // the missing-component install sequence + its own notices (OFF/ON/
91
+ // progress/failure). We don't push a redundant notice here; a null
92
+ // return means "already running" or "failed", both already noticed.
93
+ void toggleVoice({ pushNotice: store.pushNotice, setProgressHint: store.setProgressHint })
94
+ .then((result) => {
95
+ // true/false = new enabled state; null = failed/busy (unchanged).
96
+ if (result === true || result === false) setVoiceEnabled(result);
97
+ else if (result && typeof result === 'object') setVoiceEnabled(isVoiceEnabled());
98
+ });
99
+ return true;
100
+ }
101
+ case 'search':
102
+ if (state.busy) {
103
+ store.pushNotice('wait for the current turn to finish before /search', 'warn');
104
+ return false;
105
+ }
106
+ if (arg) store.pushNotice('/search sets the search provider/model; the search tool uses that model when called.', 'warn');
107
+ openSearchPicker();
108
+ return true;
109
+ case 'agents':
110
+ openAgentsPicker();
111
+ return true;
112
+ case 'workflow':
113
+ if (!arg) {
114
+ openWorkflowPicker();
115
+ return true;
116
+ }
117
+ void store.setWorkflow?.(arg.trim())
118
+ .then((result) => {
119
+ if (!result) {
120
+ store.pushNotice('Workflow switch is already running.', 'warn');
121
+ return;
122
+ }
123
+ store.pushNotice(workflowSwitchNotice(result), 'info');
124
+ })
125
+ .catch((e) => store.pushNotice(`Couldn’t switch workflow: ${e?.message || e}`, 'error'));
126
+ return true;
127
+ case 'outputstyle': {
128
+ if (state.busy) {
129
+ store.pushNotice('wait for the current turn to finish before /OutputStyle', 'warn');
130
+ return false;
131
+ }
132
+ const value = arg.trim();
133
+ const lower = value.toLowerCase();
134
+ if (!value) {
135
+ openOutputStylePicker();
136
+ return true;
137
+ }
138
+ if (lower === 'status' || lower === 'current' || lower === 'show') {
139
+ try {
140
+ const status = store.getOutputStyle?.();
141
+ const label = status?.current?.label || status?.current?.id || status?.configured || 'Default';
142
+ store.pushNotice(`Output style: ${label}`, 'info');
143
+ } catch (e) {
144
+ store.pushNotice(`Couldn’t read output style: ${e?.message || e}`, 'error');
145
+ }
146
+ return true;
147
+ }
148
+ void store.setOutputStyle?.(value)
149
+ .then((result) => {
150
+ if (!result) {
151
+ store.pushNotice('Output style switch is already running.', 'warn');
152
+ return;
153
+ }
154
+ store.pushNotice(outputStyleNotice(result), 'info');
155
+ })
156
+ .catch((e) => store.pushNotice(`Couldn’t switch output style: ${e?.message || e}`, 'error'));
157
+ return true;
158
+ }
159
+ case 'theme': {
160
+ const value = arg.trim();
161
+ const lower = value.toLowerCase();
162
+ if (!value) {
163
+ openThemePicker();
164
+ return true;
165
+ }
166
+ let themes = [];
167
+ try { themes = store.listThemes?.() || []; } catch (e) {
168
+ store.pushNotice(`could not list themes: ${e?.message || e}`, 'error');
169
+ return true;
170
+ }
171
+ if (lower === 'status' || lower === 'current' || lower === 'show') {
172
+ const id = store.getTheme?.();
173
+ const entry = themes.find((t) => t.id === id);
174
+ store.pushNotice(`Theme: ${entry?.label || id || 'default'}`, 'info');
175
+ return true;
176
+ }
177
+ const match = themes.find((t) => t.id.toLowerCase() === lower)
178
+ || themes.find((t) => String(t.label || '').toLowerCase() === lower);
179
+ if (!match) {
180
+ const ids = themes.map((t) => t.id).join(', ');
181
+ store.pushNotice(`usage: /theme [id]. Available: ${ids}`, 'warn');
182
+ return true;
183
+ }
184
+ try {
185
+ const applied = store.setTheme?.(match.id, { persist: true });
186
+ store.pushNotice(themeNotice(applied || match), 'info');
187
+ } catch (e) {
188
+ store.pushNotice(`Couldn’t set theme: ${e?.message || e}`, 'error');
189
+ }
190
+ return true;
191
+ }
192
+ case 'effort':
193
+ if (state.busy) {
194
+ store.pushNotice('wait for the current turn to finish before /effort', 'warn');
195
+ return false;
196
+ }
197
+ if (!arg) {
198
+ openEffortPicker();
199
+ return true;
200
+ }
201
+ void store.setEffort(arg)
202
+ .then(result => store.pushNotice(result ? `Effort set to ${result}` : 'Effort switch is already running.', result ? 'info' : 'warn'))
203
+ .catch((e) => store.pushNotice(`Couldn’t switch effort: ${e?.message || e}`, 'error'));
204
+ return true;
205
+ case 'fast': {
206
+ if (state.busy) {
207
+ store.pushNotice('wait for the current turn to finish before /fast', 'warn');
208
+ return false;
209
+ }
210
+ const value = String(arg || '').trim().toLowerCase();
211
+ const setTo = value
212
+ ? ['1', 'true', 'yes', 'on', 'enable', 'enabled'].includes(value)
213
+ ? true
214
+ : ['0', 'false', 'no', 'off', 'disable', 'disabled'].includes(value)
215
+ ? false
216
+ : null
217
+ : undefined;
218
+ if (setTo === null) {
219
+ store.pushNotice('usage: /fast [on|off]', 'warn');
220
+ return true;
221
+ }
222
+ const action = setTo === undefined ? store.toggleFast?.() : store.setFast?.(setTo);
223
+ void Promise.resolve(action)
224
+ .then((enabled) => {
225
+ if (enabled === null || enabled === undefined) {
226
+ store.pushNotice('Fast mode switch is already running.', 'warn');
227
+ return;
228
+ }
229
+ store.pushNotice(`Fast mode ${enabled ? 'on' : 'off'} for ${state.provider}/${state.model}`, 'info');
230
+ })
231
+ .catch((e) => store.pushNotice(`Couldn’t update fast mode: ${e?.message || e}`, 'error'));
232
+ return true;
233
+ }
234
+ case 'cwd': {
235
+ const nextPath = arg.trim();
236
+ if (!nextPath) {
237
+ store.pushNotice(`Project path: ${state.cwd}`, 'info');
238
+ return true;
239
+ }
240
+ try {
241
+ store.setCwd?.(nextPath, { message: `Project set: ${projectNameFromPath(nextPath)}` });
242
+ } catch (e) {
243
+ store.pushNotice(`project switch failed: ${e?.message || e}`, 'error');
244
+ }
245
+ return true;
246
+ }
247
+ case 'project': {
248
+ const target = arg.trim();
249
+ if (target) {
250
+ enterProject(target);
251
+ return true;
252
+ }
253
+ openProjectPicker();
254
+ return true;
255
+ }
256
+ case 'tools':
257
+ openToolsPicker(arg.trim());
258
+ return true;
259
+ case 'mcp':
260
+ openMcpPicker();
261
+ return true;
262
+ case 'skills':
263
+ openSkillsPicker();
264
+ return true;
265
+ case 'plugins':
266
+ openPluginsPicker();
267
+ return true;
268
+ case 'hooks':
269
+ openHooksPicker();
270
+ return true;
271
+ case 'providers':
272
+ void openProviderSetupPicker();
273
+ return true;
274
+ case 'channels':
275
+ void openChannelSetupPicker('all');
276
+ return true;
277
+ case 'schedules':
278
+ void openChannelSetupPicker('schedules');
279
+ return true;
280
+ case 'webhooks':
281
+ void openChannelSetupPicker('webhooks');
282
+ return true;
283
+ case 'auth':
284
+ store.pushNotice('/auth moved to /providers', 'info');
285
+ void openProviderSetupPicker();
286
+ return true;
287
+ case 'auth-forget': {
288
+ const providerId = arg.trim();
289
+ if (!providerId) {
290
+ store.pushNotice('usage: /auth-forget <provider>', 'warn');
291
+ return true;
292
+ }
293
+ try {
294
+ store.forgetProviderAuth(providerId);
295
+ } catch (e) {
296
+ store.pushNotice(`auth-forget failed: ${e?.message || e}`, 'error');
297
+ }
298
+ return true;
299
+ }
300
+ case 'memory': {
301
+ if (!arg.trim()) {
302
+ openMemoryCorePicker({ returnTo: null });
303
+ return true;
304
+ }
305
+ void store.memoryControl?.(parseMemoryCommand(arg))
306
+ .catch((e) => store.pushNotice(`memory failed: ${e?.message || e}`, 'error'));
307
+ return true;
308
+ }
309
+ case 'recap': {
310
+ const value = arg.trim().toLowerCase();
311
+ if (value === 'on' || value === 'off') {
312
+ void Promise.resolve(store.setMemoryEnabled?.(value === 'on'))
313
+ .then((next) => {
314
+ if (!next) store.pushNotice('recap setting is busy', 'warn');
315
+ else store.pushNotice(`Recap ${next.enabled ? 'on' : 'off'}`, 'info');
316
+ })
317
+ .catch((e) => store.pushNotice(`recap failed: ${e?.message || e}`, 'error'));
318
+ return true;
319
+ }
320
+ // Bare /recap opens the Memory picker: its top row is the same
321
+ // background-cycles toggle, plus the curated entries below.
322
+ openMemoryCorePicker({ returnTo: null });
323
+ return true;
324
+ }
325
+ case 'recall': {
326
+ const query = arg.trim();
327
+ if (!query) {
328
+ store.pushNotice('usage: /recall <query>', 'warn');
329
+ return true;
330
+ }
331
+ void store.recall?.(query)
332
+ .catch((e) => store.pushNotice(`recall failed: ${e?.message || e}`, 'error'));
333
+ return true;
334
+ }
335
+ case 'autoclear': {
336
+ const value = arg.trim().toLowerCase();
337
+ if (!value) {
338
+ openAutoClearPicker();
339
+ return true;
340
+ }
341
+ try {
342
+ let next;
343
+ if (value === 'status') {
344
+ next = store.getAutoClear?.();
345
+ } else if (value === 'on' || value === 'enable' || value === 'enabled') {
346
+ next = store.setAutoClear?.({ enabled: true });
347
+ } else if (value === 'off' || value === 'disable' || value === 'disabled') {
348
+ next = store.setAutoClear?.({ enabled: false });
349
+ } else {
350
+ next = store.setAutoClear?.({ duration: value });
351
+ }
352
+ if (!next) {
353
+ store.pushNotice('autoclear unavailable', 'warn');
354
+ return true;
355
+ }
356
+ store.pushNotice(`autoclear ${next.enabled ? 'on' : 'off'} · idle ${formatDuration(next.idleMs)}`, 'info');
357
+ } catch (e) {
358
+ store.pushNotice(`autoclear failed: ${e?.message || e}`, 'error');
359
+ }
360
+ return true;
361
+ }
362
+ case 'compact':
363
+ if (state.busy) {
364
+ store.pushNotice('wait for the current turn to finish before /compact', 'warn');
365
+ return false;
366
+ }
367
+ void store.compact()
368
+ .then((r) => {
369
+ if (!r) {
370
+ store.pushNotice('Compact failed.', 'warn');
371
+ return;
372
+ }
373
+ if (r.error) {
374
+ store.pushNotice('Compact failed.', 'error');
375
+ return;
376
+ }
377
+ if (r.changed === false && r.reason) {
378
+ store.pushNotice(r.reason, 'warn');
379
+ return;
380
+ }
381
+ if (r.changed === false) {
382
+ store.pushNotice('nothing to compact', 'warn');
383
+ return;
384
+ }
385
+ store.pushNotice('Compact done.', 'info');
386
+ })
387
+ .catch(() => store.pushNotice('Compact failed.', 'error'));
388
+ return true;
389
+ case 'resume':
390
+ if (state.busy) {
391
+ store.pushNotice('wait for the current turn to finish before /resume', 'warn');
392
+ return false;
393
+ }
394
+ if (arg) {
395
+ void store.resume(arg)
396
+ .then(ok => store.pushNotice(ok ? `Resumed ${arg}` : 'Couldn’t resume chat.', ok ? 'info' : 'warn'))
397
+ .catch((e) => store.pushNotice(`Couldn’t resume chat: ${e?.message || e}`, 'error'));
398
+ } else {
399
+ openResumePicker();
400
+ }
401
+ return true;
402
+ case 'usage':
403
+ openUsagePanel(arg);
404
+ return true;
405
+ case 'context':
406
+ openContextPicker();
407
+ return true;
408
+ case 'settings':
409
+ case 'config':
410
+ openSettingsPicker();
411
+ return true;
412
+ case 'profile':
413
+ openProfilePicker();
414
+ return true;
415
+ case 'update':
416
+ openUpdatePicker();
417
+ return true;
418
+ case 'quit':
419
+ requestExit();
420
+ return true;
421
+ default:
422
+ store.pushNotice(`unknown command: /${cmd}`, 'warn');
423
+ return true;
424
+ }
425
+ };
426
+ return { runSlashCommand };
427
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * text-layout.mjs — prompt-box row-reservation helpers extracted verbatim from
3
+ * App.jsx. Pure grapheme wrap math (displayWidth policy); no React, no App state.
4
+ */
5
+ import { displayWidth } from '../display-width.mjs';
6
+
7
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
8
+
9
+ export function wrappedTextRows(value, width) {
10
+ const w = Math.max(1, Math.floor(Number(width) || 1));
11
+ let row = 0;
12
+ let col = 0;
13
+ const segments = [...graphemeSegmenter.segment(String(value ?? ''))];
14
+ for (let i = 0; i < segments.length; i += 1) {
15
+ const { segment } = segments[i];
16
+ if (segment === '\n') {
17
+ row += 1;
18
+ col = 0;
19
+ continue;
20
+ }
21
+ const segmentWidth = displayWidth(segment);
22
+ if (segmentWidth === 0) continue;
23
+ // Same wrap-ansi hard/wordWrap:false wrapping as caretPosition — keep the
24
+ // reserved row count in lock-step with the caret math so the prompt box
25
+ // height matches ink's actual wrap for wide-char lines.
26
+ if (col > 0 && col + segmentWidth > w) {
27
+ row += 1;
28
+ col = 0;
29
+ }
30
+ col += segmentWidth;
31
+ if (col === w && i < segments.length - 1) {
32
+ row += 1;
33
+ col = 0;
34
+ }
35
+ }
36
+ return row + 1;
37
+ }
38
+
39
+ export function promptContentRows(value, contentColumns) {
40
+ // PromptInput appends a blank trailing cell when the caret is at end-of-input
41
+ // so the native cursor always has a rendered cell. App does not own the cursor
42
+ // offset, so reserve for the worst common case: caret at the end. This can
43
+ // over-reserve by one row at exact wrap boundaries, but never under-reserves
44
+ // and therefore prevents transcript rows from bleeding into the prompt box.
45
+ return wrappedTextRows(`${String(value ?? '')} `, contentColumns);
46
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * theme-effort-pickers.mjs — the Theme picker + Effort picker cluster.
3
+ *
4
+ * Extracted from App.jsx behavior-preservingly as a dependency-injection
5
+ * factory (these openers drive setPicker + the prompt/pane setters and read
6
+ * live App state, so they can't be pure). Every function body is the original
7
+ * App logic verbatim, with closure identifiers threaded through the factory
8
+ * argument. themeNotice moves here alongside its sole external callers being
9
+ * re-exported so the runSlashCommand /theme-by-id path keeps using it.
10
+ */
11
+ import { theme } from '../theme.mjs';
12
+
13
+ export const themeNotice = (applied) => `Theme set to ${applied?.label || applied?.id || 'default'}`;
14
+
15
+ export function createThemeEffortPickers({
16
+ state,
17
+ store,
18
+ setPicker,
19
+ setProviderPrompt,
20
+ setChannelPrompt,
21
+ setHookPrompt,
22
+ setSettingsPrompt,
23
+ setContextPanel,
24
+ closeUsagePanel,
25
+ clean,
26
+ }) {
27
+ const openThemePicker = (options = {}) => {
28
+ const returnTo = typeof options.returnTo === 'function' ? options.returnTo : null;
29
+ const onboarding = options.onboarding || null;
30
+ let themes = [];
31
+ try {
32
+ themes = store.listThemes?.() || [];
33
+ } catch (e) {
34
+ store.pushNotice(`could not list themes: ${e?.message || e}`, 'error');
35
+ return;
36
+ }
37
+ if (!themes.length) {
38
+ store.pushNotice('no themes available', 'warn');
39
+ return;
40
+ }
41
+ const currentId = store.getTheme?.() || themes.find((t) => t.current)?.id || themes[0]?.id;
42
+ let highlightedThemeId = currentId;
43
+ const items = themes.map((entry) => ({
44
+ value: entry.id,
45
+ label: entry.label || entry.id,
46
+ marker: entry.id === currentId ? '✓' : '',
47
+ description: entry.description || 'color theme',
48
+ _theme: entry,
49
+ }));
50
+ setProviderPrompt(null);
51
+ setChannelPrompt(null);
52
+ setHookPrompt(null);
53
+ setSettingsPrompt(null);
54
+ setContextPanel(null);
55
+ closeUsagePanel();
56
+ const applyTheme = (id, { persist = true } = {}) => {
57
+ try {
58
+ return store.setTheme?.(id, { persist });
59
+ } catch (e) {
60
+ store.pushNotice(`Couldn’t set theme: ${e?.message || e}`, 'error');
61
+ return null;
62
+ }
63
+ };
64
+ // Onboarding: Enter (row) and ConfirmBar Next both persist the highlighted
65
+ // theme, then advance; Back restores the original palette then steps back.
66
+ const saveTheme = (id, { advance = false } = {}) => {
67
+ setPicker(null);
68
+ const applied = applyTheme(id, { persist: true });
69
+ store.pushNotice(themeNotice(applied || { id }), 'info');
70
+ if (advance && onboarding) onboarding.onAdvance?.();
71
+ else if (returnTo) returnTo();
72
+ };
73
+ setPicker({
74
+ title: 'Theme',
75
+ description: 'Choose the color theme that looks best with your terminal.',
76
+ help: onboarding ? undefined : (returnTo ? '↑/↓ Preview · Enter Choose · Esc Settings' : '↑/↓ Preview · Enter Choose · Esc Back'),
77
+ labelWidth: 22,
78
+ initialIndex: Math.max(0, items.findIndex((item) => item.value === currentId)),
79
+ items,
80
+ confirmBar: onboarding ? {
81
+ buttons: [
82
+ { value: 'back', label: '◀ Back' },
83
+ { value: 'next', label: 'Next ▶' },
84
+ ],
85
+ onConfirm: (button) => {
86
+ if (button.value === 'back') {
87
+ // Restore the palette active before the picker opened, then step back.
88
+ if (currentId) applyTheme(currentId, { persist: false });
89
+ onboarding.onBack?.();
90
+ return;
91
+ }
92
+ saveTheme(highlightedThemeId, { advance: true });
93
+ },
94
+ } : (options.confirmBar || null),
95
+ // Live preview while moving: apply (no persist) so the surface re-tones
96
+ // as the selection moves. Enter persists; Esc restores the original.
97
+ onHighlight: (_value, item) => {
98
+ if (item?._theme?.id) {
99
+ highlightedThemeId = item._theme.id;
100
+ applyTheme(item._theme.id, { persist: false });
101
+ }
102
+ },
103
+ onSelect: (_value, item) => {
104
+ const entry = item?._theme;
105
+ if (!entry) return;
106
+ saveTheme(entry.id, { advance: Boolean(onboarding) });
107
+ },
108
+ onCancel: () => {
109
+ setPicker(null);
110
+ // Restore the theme that was active before the picker opened.
111
+ if (currentId) applyTheme(currentId, { persist: false });
112
+ if (onboarding) onboarding.onCancel?.();
113
+ else if (returnTo) returnTo();
114
+ },
115
+ });
116
+ };
117
+
118
+ const openEffortPicker = () => {
119
+ setProviderPrompt(null);
120
+ setChannelPrompt(null);
121
+ setHookPrompt(null);
122
+ setSettingsPrompt(null);
123
+ const items = Array.isArray(state.effortOptions) && state.effortOptions.length > 0
124
+ ? state.effortOptions
125
+ : [];
126
+ if (!items.length) {
127
+ store.pushNotice('Current model has no effort levels.', 'warn');
128
+ return;
129
+ }
130
+ const current = state.effort || items[0]?.value || '';
131
+ const pickerItems = items.map((item) => ({
132
+ ...item,
133
+ marker: item?.value === current ? '✓' : '',
134
+ markerColor: theme.success,
135
+ description: clean(item?.description).toLowerCase() === 'current' ? '' : item?.description,
136
+ }));
137
+ setPicker({
138
+ title: 'Effort',
139
+ description: 'Reasoning effort for the current model.',
140
+ items: pickerItems,
141
+ onSelect: (value) => {
142
+ setPicker(null);
143
+ void store.setEffort(value)
144
+ .then(result => store.pushNotice(result ? `Effort set to ${result}` : 'Effort switch is already running.', result ? 'info' : 'warn'))
145
+ .catch((e) => store.pushNotice(`Couldn’t switch effort: ${e?.message || e}`, 'error'));
146
+ },
147
+ onCancel: () => {
148
+ setPicker(null);
149
+ },
150
+ });
151
+ };
152
+
153
+ return { openThemePicker, openEffortPicker };
154
+ }