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,400 @@
1
+ /**
2
+ * onboarding-steps.mjs — the first-run onboarding wizard step cluster.
3
+ *
4
+ * Extracted from App.jsx behavior-preservingly as a dependency-injection
5
+ * factory. Every function body is the original App logic verbatim, with closure
6
+ * identifiers threaded through the factory argument. Cross-references between
7
+ * steps stay inside this factory; deps pointing at later-defined App fns
8
+ * (openProviderSetupPicker, openThemePicker, openOutputStylePicker) thread as
9
+ * lazy getter wrappers so they resolve the live opener at call time.
10
+ */
11
+ import { theme } from '../theme.mjs';
12
+ import { SEARCH_DEFAULT_ROUTE, isSearchDefaultRoute } from './app-format.mjs';
13
+ import {
14
+ normalizeModelOptions,
15
+ modelDescription,
16
+ agentModelParts,
17
+ routeFromModel,
18
+ } from './model-options.mjs';
19
+
20
+ export function createOnboardingSteps({
21
+ store,
22
+ setPicker,
23
+ setProviderPrompt,
24
+ setChannelPrompt,
25
+ setHookPrompt,
26
+ setSettingsPrompt,
27
+ setOnboardingActive,
28
+ onboardingRef,
29
+ providerModelsCacheRef,
30
+ onboardingPrefetchSeqRef,
31
+ openProviderSetupPicker,
32
+ openThemePicker,
33
+ openOutputStylePicker,
34
+ }) {
35
+ // First-run onboarding is a 4-step wizard. Each step's ROOT screen carries a
36
+ // ConfirmBar (Back/Next, Finish on the last step); the Picker owns the bar
37
+ // focus and only fires onConfirm from the bar. Nested depths (API-key entry,
38
+ // model route picker) render without a ConfirmBar so
39
+ // their own key semantics are untouched and step-switching is disabled there.
40
+ // Esc/cancel during onboarding = confirm skip. Mark onboarding complete
41
+ // (routes/agents/provider untouched) so it does not reopen next launch;
42
+ // `mixdog --onboarding` (forceOnboarding) still reopens regardless.
43
+ const onboardingWarnReopen = () => {
44
+ setOnboardingActive(false);
45
+ try {
46
+ store.skipOnboarding?.();
47
+ store.pushNotice('Setup skipped. Run `mixdog --onboarding` to set up later.', 'info');
48
+ } catch (e) {
49
+ store.pushNotice(`Couldn’t save skip: ${e?.message || e}`, 'error');
50
+ }
51
+ };
52
+
53
+ // Warm the Step 2 data (provider models + agent roster) in the background as
54
+ // soon as Step 1 opens, so advancing to Step 2 renders instantly instead of
55
+ // flashing an empty panel while the async load runs.
56
+ const prefetchOnboardingStep2 = () => {
57
+ if (!Array.isArray(onboardingRef.current.providerModels) || onboardingRef.current.providerModels.length === 0) {
58
+ const seq = onboardingPrefetchSeqRef.current;
59
+ void Promise.resolve(store.listProviderModels?.())
60
+ .then((models) => {
61
+ // Drop a stale result: if the provider cache was invalidated (auth
62
+ // change) while this load was in flight, its generation moved on.
63
+ if (seq !== onboardingPrefetchSeqRef.current) return;
64
+ if (Array.isArray(models) && models.length) {
65
+ onboardingRef.current.providerModels = models;
66
+ providerModelsCacheRef.current = { models, at: Date.now() };
67
+ }
68
+ })
69
+ .catch(() => { /* Step 2 falls back to its own load on entry. */ });
70
+ }
71
+ if (!Array.isArray(onboardingRef.current.agents) || onboardingRef.current.agents.length === 0) {
72
+ try {
73
+ const roster = (store.listAgents?.() || []).map((a) => ({ id: a.id, label: a.label || a.id, description: a.description || '' }));
74
+ if (roster.length) onboardingRef.current.agents = roster;
75
+ } catch { /* Step 2 retries on entry. */ }
76
+ }
77
+ };
78
+
79
+ const openOnboardingAuthStep = async () => {
80
+ prefetchOnboardingStep2();
81
+ // Load the provider setup BEFORE opening the picker so Step 1 renders the
82
+ // real list in one frame instead of flashing the "Checking Providers"
83
+ // placeholder (that swap is what looked like a jump on entry). On failure,
84
+ // fall back to the picker's own in-panel loading path.
85
+ let preloadedSetup = null;
86
+ try {
87
+ preloadedSetup = await store.getProviderSetup?.();
88
+ } catch { /* openProviderSetupPicker will show its loading frame + error. */ }
89
+ void openProviderSetupPicker({
90
+ title: 'First Run · Step 1/4 · Provider Auth',
91
+ returnTo: () => openOnboardingAuthStep(),
92
+ preloadedSetup,
93
+ confirmBar: {
94
+ buttons: [{ value: 'next', label: 'Next ▶' }],
95
+ // Keep Step 1 visible while Step 2's async model load runs; the next
96
+ // step replaces the picker itself, so no blank frame in between.
97
+ onConfirm: () => { void openOnboardingWorkflowStep(); },
98
+ },
99
+ onCancel: onboardingWarnReopen,
100
+ });
101
+ };
102
+
103
+ const openOnboardingThemeStep = () => {
104
+ openThemePicker({
105
+ onboarding: {
106
+ onAdvance: () => openOnboardingOutputStyleStep(),
107
+ onBack: () => void openOnboardingWorkflowStep(),
108
+ onCancel: onboardingWarnReopen,
109
+ },
110
+ });
111
+ };
112
+
113
+ const openOnboardingOutputStyleStep = () => {
114
+ openOutputStylePicker({
115
+ onboarding: {
116
+ isLastStep: true,
117
+ onAdvance: () => finishOnboarding(),
118
+ onBack: () => openOnboardingThemeStep(),
119
+ onCancel: onboardingWarnReopen,
120
+ },
121
+ });
122
+ };
123
+
124
+ const finishOnboarding = () => {
125
+ const defaultRoute = onboardingRef.current.defaultRoute;
126
+ const searchRoute = onboardingRef.current.searchRoute || null;
127
+ const overrides = onboardingRef.current.agentRoutes || {};
128
+ const hasOverrides = Object.keys(overrides).length > 0;
129
+ setPicker(null);
130
+ setOnboardingActive(false);
131
+ const done = () => store.pushNotice('First-run setup complete.', 'info');
132
+ const failed = (e) => store.pushNotice(`Couldn’t save setup: ${e?.message || e}`, 'error');
133
+ // Branch 1 — Main Model set: full persist. Agents without an explicit
134
+ // override are sent; untouched agents are left out so the backend never
135
+ // overwrites them (they follow the Main Model dynamically at runtime).
136
+ if (defaultRoute) {
137
+ void store.completeOnboarding?.({
138
+ defaultRoute,
139
+ defaultProvider: defaultRoute.provider,
140
+ ...(hasOverrides ? { agentRoutes: { ...overrides } } : {}),
141
+ ...(searchRoute ? { searchRoute } : {}),
142
+ }).then(done).catch(failed);
143
+ return;
144
+ }
145
+ // Branch 2 — Main unset but some Search/agent picks exist: partial persist.
146
+ // Only the explicit overrides are sent (no defaultRoute/defaultProvider); the
147
+ // backend skips agents lacking a route and marks onboarding complete.
148
+ if (hasOverrides || searchRoute) {
149
+ void store.completeOnboarding?.({
150
+ ...(hasOverrides ? { agentRoutes: { ...overrides } } : {}),
151
+ ...(searchRoute ? { searchRoute } : {}),
152
+ }).then(done).catch(failed);
153
+ return;
154
+ }
155
+ // Branch 3 — nothing configured: mark done only, leave config untouched.
156
+ try {
157
+ store.skipOnboarding?.();
158
+ } catch (e) {
159
+ failed(e);
160
+ return;
161
+ }
162
+ done();
163
+ };
164
+
165
+ // Onboarding Step 2 per-target model picker. `target` is either the pseudo
166
+ // slot 'lead' (Main Model) or a real agent id from listAgents(). No
167
+ // recommendation logic: the current effective route is pre-highlighted, and
168
+ // the plain model list is shown. Selecting Main Model updates defaultRoute;
169
+ // agents that have no explicit override keep inheriting it.
170
+ const openOnboardingRoleModelPicker = async (target) => {
171
+ const isLead = target === 'lead';
172
+ const isSearch = target === 'search';
173
+ // Search uses the search-capable model list; lead/agent use provider models.
174
+ let models;
175
+ if (isSearch) {
176
+ let searchModels = [];
177
+ try {
178
+ searchModels = await Promise.resolve(store.listSearchModels?.() || []);
179
+ } catch (e) {
180
+ store.pushNotice(`could not list search models: ${e?.message || e}`, 'warn');
181
+ }
182
+ models = normalizeModelOptions(searchModels || []);
183
+ if (models.length === 0) {
184
+ store.pushNotice('no native search models available; connect OpenAI, Grok, Gemini, or Anthropic', 'warn');
185
+ void openOnboardingWorkflowStep();
186
+ return;
187
+ }
188
+ } else {
189
+ models = normalizeModelOptions(onboardingRef.current.providerModels || []);
190
+ if (models.length === 0) {
191
+ store.pushNotice('no provider models available; open /providers to sign in', 'warn');
192
+ openOnboardingAuthStep();
193
+ return;
194
+ }
195
+ }
196
+ const overrides = onboardingRef.current.agentRoutes || {};
197
+ // Current effective route for pre-marking: Main/Search show their own stored
198
+ // route (or none); agents show their explicit override only (unset = none,
199
+ // so we don't falsely mark the Main Model row on an untouched agent).
200
+ const currentRoute = isLead
201
+ ? (onboardingRef.current.defaultRoute || null)
202
+ : isSearch
203
+ ? (onboardingRef.current.searchRoute || null)
204
+ : (overrides[target] || null);
205
+ const routeMatchesModel = (route, m) => route?.provider === m.provider && route?.model === m.id;
206
+ // Non-lead targets get a leading "Default" row that makes the target follow
207
+ // the Main Model at runtime. For agents this clears the override (null);
208
+ // for search this stores the SEARCH_DEFAULT marker route. "Default" is the
209
+ // pre-marked row when the target is unset (agent) or on the marker (search).
210
+ const isDefaultSelected = isSearch
211
+ ? (!currentRoute || isSearchDefaultRoute(currentRoute))
212
+ : !currentRoute;
213
+ const isUnset = isDefaultSelected;
214
+ const modelItems = models.map((m) => ({
215
+ value: `${m.provider}:${m.id}`,
216
+ label: m.display || m.id,
217
+ marker: routeMatchesModel(currentRoute, m) ? '✓' : '',
218
+ markerColor: theme.success,
219
+ description: modelDescription(m),
220
+ _model: m,
221
+ }));
222
+ const items = isLead
223
+ ? modelItems
224
+ : [
225
+ {
226
+ value: '__default__',
227
+ label: 'Default',
228
+ marker: isUnset ? '✓' : '',
229
+ markerColor: theme.success,
230
+ description: 'follows Main Model',
231
+ _default: true,
232
+ },
233
+ ...modelItems,
234
+ ];
235
+ const matchIdx = models.findIndex((m) => routeMatchesModel(currentRoute, m));
236
+ const initialIndex = isLead
237
+ ? Math.max(0, matchIdx)
238
+ : (isUnset || matchIdx < 0 ? 0 : matchIdx + 1);
239
+ const label = isLead
240
+ ? 'Main'
241
+ : isSearch
242
+ ? 'Search'
243
+ : (onboardingRef.current.agents || []).find((a) => a.id === target)?.label || target;
244
+ setPicker({
245
+ title: `First Run · ${label}`,
246
+ description: isLead
247
+ ? 'Pick the main model. Agents inherit this unless individually changed.'
248
+ : isSearch
249
+ ? 'Pick the native web-search model, or Default to follow the Main Model.'
250
+ : `Pick the model for ${label}, or Default to follow the Main Model.`,
251
+ initialIndex,
252
+ items,
253
+ onSelect: (_value, item) => {
254
+ // "Default" → clear the override so this target follows the Main Model.
255
+ if (item?._default) {
256
+ if (isSearch) {
257
+ // Store the SEARCH_DEFAULT marker so finish persists it and the
258
+ // runtime follows the Main Model (not a null that drops the field).
259
+ onboardingRef.current.searchRoute = { ...SEARCH_DEFAULT_ROUTE };
260
+ } else {
261
+ const nextOverrides = { ...(onboardingRef.current.agentRoutes || {}) };
262
+ delete nextOverrides[target];
263
+ onboardingRef.current.agentRoutes = nextOverrides;
264
+ }
265
+ void openOnboardingWorkflowStep();
266
+ return;
267
+ }
268
+ const next = item?._model ? routeFromModel(item._model) : null;
269
+ if (!next) {
270
+ store.pushNotice('select a provider model first', 'warn');
271
+ void openOnboardingWorkflowStep();
272
+ return;
273
+ }
274
+ if (isLead) {
275
+ onboardingRef.current.defaultRoute = next;
276
+ } else if (isSearch) {
277
+ onboardingRef.current.searchRoute = next;
278
+ } else {
279
+ onboardingRef.current.agentRoutes = {
280
+ ...(onboardingRef.current.agentRoutes || {}),
281
+ [target]: next,
282
+ };
283
+ }
284
+ void openOnboardingWorkflowStep();
285
+ },
286
+ onCancel: () => {
287
+ void openOnboardingWorkflowStep();
288
+ },
289
+ });
290
+ };
291
+
292
+ const openOnboardingWorkflowStep = async () => {
293
+ if (!Array.isArray(onboardingRef.current.providerModels) || onboardingRef.current.providerModels.length === 0) {
294
+ try {
295
+ onboardingRef.current.providerModels = await store.listProviderModels();
296
+ providerModelsCacheRef.current = { models: onboardingRef.current.providerModels, at: Date.now() };
297
+ } catch (e) {
298
+ onboardingRef.current.providerModels = [];
299
+ store.pushNotice(`could not list models: ${e?.message || e}`, 'warn');
300
+ }
301
+ }
302
+ const models = onboardingRef.current.providerModels || [];
303
+ if (models.length === 0) {
304
+ onboardingRef.current.defaultRoute = null;
305
+ onboardingRef.current.agentRoutes = {};
306
+ store.pushNotice('no provider models available; open /providers to sign in', 'warn');
307
+ openOnboardingAuthStep();
308
+ return;
309
+ }
310
+ // Main Model stays unset until the user picks one; no auto-recommendation.
311
+ // Load the real agent roster once (explore/maintainer/worker/heavy-worker/
312
+ // reviewer/debugger). Each agent defaults to the Main Model unless the user
313
+ // set an explicit override in agentRoutes.
314
+ if (!Array.isArray(onboardingRef.current.agents) || onboardingRef.current.agents.length === 0) {
315
+ try {
316
+ onboardingRef.current.agents = (store.listAgents?.() || []).map((a) => ({ id: a.id, label: a.label || a.id, description: a.description || '' }));
317
+ } catch (e) {
318
+ onboardingRef.current.agents = [];
319
+ store.pushNotice(`could not list agents: ${e?.message || e}`, 'warn');
320
+ }
321
+ }
322
+ const defaultRoute = onboardingRef.current.defaultRoute;
323
+ const searchRoute = onboardingRef.current.searchRoute || null;
324
+ const overrides = onboardingRef.current.agentRoutes || {};
325
+ const agents = onboardingRef.current.agents || [];
326
+ setProviderPrompt(null);
327
+ setChannelPrompt(null);
328
+ setHookPrompt(null);
329
+ setSettingsPrompt(null);
330
+ setPicker({
331
+ title: 'First Run · Step 2/4 · Models',
332
+ description: 'Set the Main Model; each agent inherits it unless changed.',
333
+ indexMode: 'always',
334
+ labelWidth: 18,
335
+ metaWidth: 33,
336
+ items: [
337
+ {
338
+ value: 'main-model',
339
+ label: 'Main',
340
+ metaParts: agentModelParts(defaultRoute),
341
+ description: 'main chat, planning, and agent default',
342
+ _action: 'slot',
343
+ _target: 'lead',
344
+ },
345
+ {
346
+ value: 'search-model',
347
+ label: 'Search',
348
+ // Marker route = follow Main Model → show a hint, not 'default/default'.
349
+ metaParts: isSearchDefaultRoute(searchRoute)
350
+ ? [{ text: '(follows main)', width: 17 }, { text: '', width: 6 }, { text: '', width: 4 }]
351
+ : agentModelParts(searchRoute),
352
+ description: 'native search model',
353
+ _action: 'slot',
354
+ _target: 'search',
355
+ },
356
+ ...agents.map((agent) => ({
357
+ value: `agent:${agent.id}`,
358
+ label: agent.label,
359
+ metaParts: agentModelParts(overrides[agent.id] || null),
360
+ description: agent.description || '',
361
+ _action: 'slot',
362
+ _target: agent.id,
363
+ })),
364
+ ],
365
+ confirmBar: {
366
+ buttons: [
367
+ { value: 'back', label: '◀ Back' },
368
+ { value: 'next', label: 'Next ▶' },
369
+ ],
370
+ onConfirm: (button) => {
371
+ // Both neighbors are async (Step 1 preloads provider setup; theme
372
+ // list is sync but keep symmetric) — leave Step 2 visible until the
373
+ // next picker replaces it to avoid a blank frame.
374
+ if (button.value === 'back') openOnboardingAuthStep();
375
+ else openOnboardingThemeStep();
376
+ },
377
+ },
378
+ onSelect: (_value, item) => {
379
+ if (item._action === 'slot') {
380
+ openOnboardingRoleModelPicker(item._target);
381
+ }
382
+ },
383
+ onCancel: () => {
384
+ setPicker(null);
385
+ onboardingWarnReopen();
386
+ },
387
+ });
388
+ };
389
+
390
+ return {
391
+ onboardingWarnReopen,
392
+ prefetchOnboardingStep2,
393
+ openOnboardingAuthStep,
394
+ openOnboardingThemeStep,
395
+ openOnboardingOutputStyleStep,
396
+ finishOnboarding,
397
+ openOnboardingRoleModelPicker,
398
+ openOnboardingWorkflowStep,
399
+ };
400
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * project-picker.mjs — the project selector / create / rename / enter cluster.
3
+ *
4
+ * Extracted from App.jsx behavior-preservingly. This cluster is ref/state
5
+ * coupled (it drives setPicker + a fan of prompt setters), so it's delivered as
6
+ * a dependency-injection factory rather than pure functions: App calls
7
+ * createProjectPicker({...}) once and destructures the returned builders. Every
8
+ * function body is the original App logic, verbatim, with closure identifiers
9
+ * (state, store, setPicker, the prompt setters, and the projects.mjs helpers)
10
+ * threaded in through the factory argument.
11
+ */
12
+ export function createProjectPicker({
13
+ state,
14
+ store,
15
+ setPicker,
16
+ setProviderPrompt,
17
+ setChannelPrompt,
18
+ setHookPrompt,
19
+ setSettingsPrompt,
20
+ setContextPanel,
21
+ closeUsagePanel,
22
+ listProjects,
23
+ addProject,
24
+ touchProjectSelected,
25
+ resolveProjectPath,
26
+ projectNameFromPath,
27
+ pickFolder,
28
+ }) {
29
+ const buildProjectPickerState = ({ initialEntry = false } = {}) => {
30
+ let projects = [];
31
+ try {
32
+ projects = listProjects() || [];
33
+ } catch {
34
+ projects = [];
35
+ }
36
+ const currentPath = String(state.cwd || process.cwd() || '');
37
+ const items = [];
38
+ // Registered projects (store order via listProjects).
39
+ for (const project of projects) {
40
+ if (!project?.path) continue;
41
+ items.push({
42
+ value: project.path,
43
+ label: project.name || project.path,
44
+ meta: project.path,
45
+ _project: project,
46
+ });
47
+ }
48
+ // Last row: implicit current-directory shortcut (not persisted).
49
+ items.push({
50
+ value: '__use_current__',
51
+ label: 'Current Path',
52
+ meta: currentPath,
53
+ _action: 'current',
54
+ });
55
+ return {
56
+ kind: 'project',
57
+ title: 'Project',
58
+ description: 'Choose a project.',
59
+ help: initialEntry
60
+ ? '↑/↓ Select · Enter Open · c Create · r Rename'
61
+ : '↑/↓ Select · Enter Open · c Create · r Rename · Esc Back',
62
+ indexMode: 'always',
63
+ labelWidth: 18,
64
+ metaWidth: 40,
65
+ items,
66
+ onSelect: (_value, item) => {
67
+ if (item?._action === 'new') {
68
+ beginNewProject();
69
+ return;
70
+ }
71
+ if (item?._action === 'current') {
72
+ setPicker(null);
73
+ try {
74
+ store.setCwd?.(currentPath, {
75
+ notice: !initialEntry,
76
+ message: `Project set: ${projectNameFromPath(currentPath)}`,
77
+ });
78
+ } catch (e) {
79
+ store.pushNotice(`project switch failed: ${e?.message || e}`, 'error');
80
+ }
81
+ return;
82
+ }
83
+ setPicker(null);
84
+ const project = item?._project;
85
+ if (project?.path) enterProject(project.path, { notice: !initialEntry });
86
+ },
87
+ onKey: (input, _key, item) => {
88
+ if (input === 'c' || input === 'C') {
89
+ beginNewProject();
90
+ return;
91
+ }
92
+ // 'r' renames the highlighted registered project (not the current-dir
93
+ // shortcut or the create row).
94
+ if ((input === 'r' || input === 'R') && item?._project?.path) {
95
+ beginRenameProject(item._project);
96
+ }
97
+ },
98
+ onCancel: () => {
99
+ setPicker(null);
100
+ },
101
+ };
102
+ };
103
+
104
+ // Open the manual path-entry flow. The user types a directory path; on submit
105
+ // we register it (and offer to create it if missing). Used as a
106
+ // fallback when no native folder dialog is available.
107
+ const beginNewProjectManual = () => {
108
+ setPicker(null);
109
+ setProviderPrompt(null);
110
+ setChannelPrompt(null);
111
+ setHookPrompt(null);
112
+ setContextPanel(null);
113
+ closeUsagePanel();
114
+ setSettingsPrompt({
115
+ kind: 'project-new',
116
+ label: 'New project · Path',
117
+ hint: 'Type a directory path. The folder name becomes the project name.',
118
+ });
119
+ };
120
+
121
+ // Begin "create project": open the OS-native folder picker. The project picker
122
+ // stays mounted (swapped to a non-interactive "Opening folder picker…" panel)
123
+ // while the native dialog is open, so the welcome banner/layout stay put and
124
+ // the prompt remains disabled (input is gated on `!!picker`). On a chosen
125
+ // folder we register; on cancel we return to the project picker;
126
+ // when no dialog tool exists we fall back to manual path typing.
127
+ const beginNewProject = () => {
128
+ setProviderPrompt(null);
129
+ setChannelPrompt(null);
130
+ setHookPrompt(null);
131
+ setContextPanel(null);
132
+ closeUsagePanel();
133
+ // Keep an overlay up (kind:'project' so the banner/height stay reserved) but
134
+ // make it inert: no selectable items, navigation is a no-op until resolve.
135
+ setPicker({
136
+ kind: 'project',
137
+ title: 'Project',
138
+ description: 'Opening folder picker… choose a folder in the dialog window.',
139
+ help: 'Waiting for the system folder dialog…',
140
+ indexMode: 'never',
141
+ items: [],
142
+ onSelect: () => {},
143
+ onCancel: () => {},
144
+ });
145
+ void pickFolder({
146
+ title: 'Select a project folder',
147
+ initialPath: String(state.cwd || process.cwd() || ''),
148
+ })
149
+ .then((result) => {
150
+ if (!result || result.available === false) {
151
+ // No native dialog on this system → manual typing.
152
+ beginNewProjectManual();
153
+ return;
154
+ }
155
+ if (!result.path) {
156
+ // User cancelled the dialog → back to the project list.
157
+ openProjectPicker();
158
+ return;
159
+ }
160
+ registerProject(result.path);
161
+ })
162
+ .catch(() => {
163
+ beginNewProjectManual();
164
+ });
165
+ };
166
+
167
+ // Register a project in the picker list without switching this session's cwd.
168
+ const registerProject = (rawPath) => {
169
+ const path = resolveProjectPath(rawPath);
170
+ if (!path) {
171
+ store.pushNotice('project path is required', 'warn');
172
+ return;
173
+ }
174
+ try {
175
+ const project = addProject(path);
176
+ if (project?.name) store.pushNotice(`project added: ${project.name}`, 'info');
177
+ openProjectPicker();
178
+ } catch (e) {
179
+ store.pushNotice(`project add failed: ${e?.message || e}`, 'error');
180
+ }
181
+ };
182
+
183
+ // Switch the active working directory to a registered/created project path.
184
+ const enterProject = (rawPath, options = {}) => {
185
+ const path = resolveProjectPath(rawPath);
186
+ if (!path) {
187
+ store.pushNotice('project path is required', 'warn');
188
+ return;
189
+ }
190
+ try {
191
+ // Switch cwd first; only persist the project once the runtime accepts it,
192
+ // so an invalid/missing path can never be written to projects.json.
193
+ store.setCwd?.(path, {
194
+ notice: options?.notice !== false,
195
+ message: `Project set: ${projectNameFromPath(path)}`,
196
+ });
197
+ addProject(path);
198
+ touchProjectSelected(path);
199
+ } catch (e) {
200
+ store.pushNotice(`project switch failed: ${e?.message || e}`, 'error');
201
+ }
202
+ };
203
+
204
+ // Begin renaming a registered project's display name. Opens a text prompt
205
+ // seeded with the current name; submitting persists via renameProject and
206
+ // returns to the project picker. The path is never changed.
207
+ const beginRenameProject = (project) => {
208
+ if (!project?.path) return;
209
+ setPicker(null);
210
+ setProviderPrompt(null);
211
+ setChannelPrompt(null);
212
+ setHookPrompt(null);
213
+ setContextPanel(null);
214
+ closeUsagePanel();
215
+ setSettingsPrompt({
216
+ kind: 'project-rename',
217
+ label: 'Rename project',
218
+ hint: 'Set a display name. Leave blank to reset to the folder name.',
219
+ projectPath: project.path,
220
+ initialValue: project.name || '',
221
+ });
222
+ };
223
+
224
+ // Open the project selector, styled like the Model picker: numbered rows with
225
+ // a Name column + Path column. The list always opens (even when empty) and
226
+ // lists registered projects first, then a trailing "Current Path" shortcut.
227
+ // Creating a new project is available via the picker-level c shortcut.
228
+ const openProjectPicker = () => {
229
+ setProviderPrompt(null);
230
+ setChannelPrompt(null);
231
+ setHookPrompt(null);
232
+ setSettingsPrompt(null);
233
+ setContextPanel(null);
234
+ closeUsagePanel();
235
+ setPicker(buildProjectPickerState());
236
+ };
237
+
238
+ return {
239
+ buildProjectPickerState,
240
+ beginNewProjectManual,
241
+ beginNewProject,
242
+ registerProject,
243
+ enterProject,
244
+ beginRenameProject,
245
+ openProjectPicker,
246
+ };
247
+ }