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,84 @@
1
+ // Standalone session tool definitions (wrapper tools surfaced by the runtime).
2
+ // Pure, self-contained schemas + the agent-hidden default helper. Extracted
3
+ // from mixdog-session-runtime.mjs; no runtime closure dependencies.
4
+
5
+ export const TOOL_SEARCH_TOOL = {
6
+ name: 'tool_search',
7
+ title: 'Tool Search',
8
+ annotations: {
9
+ title: 'Tool Search',
10
+ readOnlyHint: true,
11
+ destructiveHint: false,
12
+ idempotentHint: true,
13
+ openWorldHint: false,
14
+ agentHidden: true,
15
+ },
16
+ description: 'Search deferred tools; confident queries load matches.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ query: { type: 'string', description: 'Search text; confident hits load. select:a,b forces exact names.' },
21
+ select: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Force exact tool names.' },
22
+ limit: { type: 'number', description: 'Max matches.' },
23
+ },
24
+ additionalProperties: false,
25
+ },
26
+ };
27
+
28
+ export const CWD_TOOL = {
29
+ name: 'cwd',
30
+ title: 'Work Project',
31
+ annotations: {
32
+ title: 'Work Project',
33
+ readOnlyHint: false,
34
+ destructiveHint: false,
35
+ idempotentHint: false,
36
+ openWorldHint: false,
37
+ agentHidden: true,
38
+ },
39
+ description: 'Show or set the session work project for tool execution.',
40
+ inputSchema: {
41
+ type: 'object',
42
+ properties: {
43
+ action: { type: 'string', enum: ['get', 'set'], description: 'Default get.' },
44
+ path: { type: 'string', description: 'Project directory for set.' },
45
+ },
46
+ additionalProperties: false,
47
+ },
48
+ };
49
+
50
+ export const SKILL_TOOL = {
51
+ name: 'Skill',
52
+ title: 'Skill',
53
+ annotations: {
54
+ title: 'Skill',
55
+ readOnlyHint: true,
56
+ destructiveHint: false,
57
+ idempotentHint: true,
58
+ openWorldHint: false,
59
+ agentHidden: false,
60
+ },
61
+ description: 'Load a named SKILL.md into context.',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ name: { type: 'string', description: 'Skill name.' },
66
+ },
67
+ required: ['name'],
68
+ additionalProperties: false,
69
+ },
70
+ };
71
+
72
+ export const LEAD_DISALLOWED_TOOLS = Object.freeze([]);
73
+ export const AGENT_HIDDEN_WRAPPER_TOOLS = new Set([]);
74
+
75
+ export function applyStandaloneToolDefaults(tool) {
76
+ if (!tool || !AGENT_HIDDEN_WRAPPER_TOOLS.has(tool.name)) return tool;
77
+ return {
78
+ ...tool,
79
+ annotations: {
80
+ ...(tool.annotations || {}),
81
+ agentHidden: true,
82
+ },
83
+ };
84
+ }
@@ -0,0 +1,201 @@
1
+ // Background warmup/refresh schedulers, extracted from
2
+ // mixdog-session-runtime.mjs. Dependency-injected factory: the timer handles
3
+ // live in a caller-owned `timers` object (so the facade's clearTimeout teardown
4
+ // still sees them) and all route/config/state reads go through supplied
5
+ // accessors. Returns the schedule* functions plus a clearAll() teardown helper.
6
+ import { performance } from 'node:perf_hooks';
7
+ import { clean } from './session-text.mjs';
8
+
9
+ export function createWarmupSchedulers({
10
+ timers,
11
+ bootProfile,
12
+ getRoute,
13
+ getConfig,
14
+ isCloseRequested,
15
+ getActiveTurnCount,
16
+ getSessionCreatePromise,
17
+ getProviderModelsCache,
18
+ getProviderModelsPromise,
19
+ reloadFullConfig,
20
+ ensureConfigForRouteProvider,
21
+ ensureProvidersReady,
22
+ ensureProviderEnabled,
23
+ refreshStatuslineUsageSnapshot,
24
+ warmProviderModelCache,
25
+ cachedProviderSetup,
26
+ warmCatalogsInBackground,
27
+ isFirstTurnCompleted,
28
+ envFlag,
29
+ delays,
30
+ flags,
31
+ }) {
32
+ const {
33
+ providerWarmupDelayMs,
34
+ providerSetupWarmupDelayMs,
35
+ providerModelWarmupDelayMs,
36
+ modelCatalogWarmupDelayMs,
37
+ statuslineUsageWarmupDelayMs,
38
+ statuslineUsageRefreshDelayMs,
39
+ backgroundBusyRetryMs,
40
+ } = delays;
41
+ const {
42
+ providerWarmupEnabled,
43
+ modelPrefetchEnabled,
44
+ modelCatalogWarmupEnabled,
45
+ } = flags;
46
+
47
+ function scheduleProviderWarmup(delayMs = providerWarmupDelayMs) {
48
+ if (!providerWarmupEnabled) {
49
+ bootProfile('providers:warm-skipped');
50
+ return;
51
+ }
52
+ if (timers.providerWarmupTimer || isCloseRequested()) return;
53
+ timers.providerWarmupTimer = setTimeout(() => {
54
+ timers.providerWarmupTimer = null;
55
+ if (isCloseRequested()) return;
56
+ if (!isFirstTurnCompleted() && !envFlag('MIXDOG_PROVIDER_WARMUP_BEFORE_FIRST_TURN')) {
57
+ bootProfile('providers:warm-deferred', { reason: 'first-turn-pending' });
58
+ return;
59
+ }
60
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
61
+ bootProfile('providers:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
62
+ scheduleProviderWarmup(backgroundBusyRetryMs);
63
+ return;
64
+ }
65
+ const providersStartedAt = performance.now();
66
+ try {
67
+ reloadFullConfig();
68
+ } catch (error) {
69
+ bootProfile('config:full-failed', { error: error?.message || String(error) });
70
+ }
71
+ void ensureProvidersReady(getConfig().providers || {})
72
+ .then(() => {
73
+ bootProfile('providers:init:ready', { ms: (performance.now() - providersStartedAt).toFixed(1) });
74
+ if (isCloseRequested()) return null;
75
+ return true;
76
+ })
77
+ .catch((error) => bootProfile('providers:warm-failed', { error: error?.message || String(error) }));
78
+ }, delayMs);
79
+ timers.providerWarmupTimer.unref?.();
80
+ }
81
+
82
+ function scheduleProviderSetupWarmup(delayMs = providerSetupWarmupDelayMs) {
83
+ if (timers.providerSetupWarmupTimer || isCloseRequested()) return;
84
+ timers.providerSetupWarmupTimer = setTimeout(() => {
85
+ timers.providerSetupWarmupTimer = null;
86
+ if (isCloseRequested()) return;
87
+ void cachedProviderSetup()
88
+ .then(() => bootProfile('provider-setup:warm-ready'))
89
+ .catch((error) => bootProfile('provider-setup:warm-failed', { error: error?.message || String(error) }));
90
+ }, delayMs);
91
+ timers.providerSetupWarmupTimer.unref?.();
92
+ }
93
+
94
+ function scheduleProviderModelWarmup(delayMs = providerModelWarmupDelayMs) {
95
+ if (!modelPrefetchEnabled) return;
96
+ if (timers.providerModelWarmupTimer || isCloseRequested()) return;
97
+ timers.providerModelWarmupTimer = setTimeout(() => {
98
+ timers.providerModelWarmupTimer = null;
99
+ if (isCloseRequested() || Array.isArray(getProviderModelsCache().models) || getProviderModelsPromise()) return;
100
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
101
+ bootProfile('provider-models:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
102
+ scheduleProviderModelWarmup(backgroundBusyRetryMs);
103
+ return;
104
+ }
105
+ warmProviderModelCache();
106
+ }, delayMs);
107
+ timers.providerModelWarmupTimer.unref?.();
108
+ }
109
+
110
+ function scheduleModelCatalogWarmup(delayMs = modelCatalogWarmupDelayMs) {
111
+ if (!modelCatalogWarmupEnabled) {
112
+ bootProfile('model-catalog:warm-skipped', { reason: 'disabled' });
113
+ return;
114
+ }
115
+ if (timers.modelCatalogWarmupTimer || isCloseRequested()) return;
116
+ timers.modelCatalogWarmupTimer = setTimeout(() => {
117
+ timers.modelCatalogWarmupTimer = null;
118
+ if (isCloseRequested()) return;
119
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
120
+ bootProfile('model-catalog:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
121
+ scheduleModelCatalogWarmup(backgroundBusyRetryMs);
122
+ return;
123
+ }
124
+ void warmCatalogsInBackground()
125
+ .then(() => bootProfile('model-catalog:warm-ready'))
126
+ .catch((error) => bootProfile('model-catalog:warm-failed', { error: error?.message || String(error) }));
127
+ }, delayMs);
128
+ timers.modelCatalogWarmupTimer.unref?.();
129
+ }
130
+
131
+ function scheduleStatuslineUsageWarmup(delayMs = statuslineUsageWarmupDelayMs) {
132
+ const route = getRoute();
133
+ const providerId = clean(route?.provider);
134
+ if (!providerId || !providerId.includes('oauth')) {
135
+ bootProfile('statusline-usage:warm-skipped', { provider: providerId || null });
136
+ return;
137
+ }
138
+ if (timers.statuslineUsageWarmupTimer || isCloseRequested()) return;
139
+ timers.statuslineUsageWarmupTimer = setTimeout(async () => {
140
+ timers.statuslineUsageWarmupTimer = null;
141
+ if (isCloseRequested()) return;
142
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
143
+ bootProfile('statusline-usage:warm-deferred', { reason: getActiveTurnCount() > 0 ? 'turn-active' : 'session-create' });
144
+ scheduleStatuslineUsageWarmup(backgroundBusyRetryMs);
145
+ return;
146
+ }
147
+ try {
148
+ ensureConfigForRouteProvider();
149
+ await ensureProvidersReady(ensureProviderEnabled(getConfig(), getRoute().provider));
150
+ if (isCloseRequested()) return;
151
+ refreshStatuslineUsageSnapshot(getRoute());
152
+ bootProfile('statusline-usage:warm-ready', { provider: clean(getRoute()?.provider) });
153
+ } catch (error) {
154
+ bootProfile('statusline-usage:warm-failed', { error: error?.message || String(error) });
155
+ } finally {
156
+ scheduleStatuslineUsageRefresh();
157
+ }
158
+ }, delayMs);
159
+ timers.statuslineUsageWarmupTimer.unref?.();
160
+ }
161
+
162
+ // Idle keep-alive loop: periodically re-fetch the OAuth usage snapshot so its
163
+ // cachedAt stays "live-fresh" and the statusline usage segment does not vanish
164
+ // after LIVE_USAGE_SNAPSHOT_MAX_AGE_MS while the session is idle. Turn-driven
165
+ // refreshes (recordStandaloneStatusTelemetry) already cover active sessions.
166
+ function scheduleStatuslineUsageRefresh(delayMs = statuslineUsageRefreshDelayMs) {
167
+ const route = getRoute();
168
+ const providerId = clean(route?.provider);
169
+ if (!providerId || !providerId.includes('oauth')) return;
170
+ if (timers.statuslineUsageRefreshTimer || isCloseRequested()) return;
171
+ timers.statuslineUsageRefreshTimer = setTimeout(async () => {
172
+ timers.statuslineUsageRefreshTimer = null;
173
+ if (isCloseRequested()) return;
174
+ if (getActiveTurnCount() > 0 || getSessionCreatePromise()) {
175
+ // Active turns refresh usage on their own; just re-arm the idle loop.
176
+ scheduleStatuslineUsageRefresh();
177
+ return;
178
+ }
179
+ try {
180
+ ensureConfigForRouteProvider();
181
+ await ensureProvidersReady(ensureProviderEnabled(getConfig(), getRoute().provider));
182
+ if (isCloseRequested()) return;
183
+ refreshStatuslineUsageSnapshot(getRoute());
184
+ } catch {
185
+ // Usage display must never affect the session runtime.
186
+ } finally {
187
+ scheduleStatuslineUsageRefresh();
188
+ }
189
+ }, delayMs);
190
+ timers.statuslineUsageRefreshTimer.unref?.();
191
+ }
192
+
193
+ return {
194
+ scheduleProviderWarmup,
195
+ scheduleProviderSetupWarmup,
196
+ scheduleProviderModelWarmup,
197
+ scheduleModelCatalogWarmup,
198
+ scheduleStatuslineUsageWarmup,
199
+ scheduleStatuslineUsageRefresh,
200
+ };
201
+ }
@@ -0,0 +1,358 @@
1
+ // Workflow/agent pack loading + route resolution, and search-route
2
+ // normalization. Roots/dataDir and config-dependent helpers are injected to
3
+ // keep this module free of the runtime's path/provider constants.
4
+ import { basename, join } from 'node:path';
5
+ import { existsSync, readdirSync } from 'node:fs';
6
+ import { clean } from './session-text.mjs';
7
+ import { normalizeEffortInput } from './effort.mjs';
8
+ import { isLikelyRawModelId } from './config-helpers.mjs';
9
+ import { readTextSafe, readJsonSafe } from './fs-utils.mjs';
10
+
11
+ export const WORKFLOW_ROUTE_SLOTS = ['lead', 'agent', 'explorer', 'memory'];
12
+ export const FIXED_AGENT_SLOTS = Object.freeze([
13
+ { id: 'explore', label: 'Explore', description: 'Broad repository exploration', workflowSlot: 'explorer' },
14
+ { id: 'maintainer', label: 'Maintainer', description: 'Background memory and upkeep', workflowSlot: 'memory' },
15
+ { id: 'worker', label: 'Worker', description: 'Scoped implementation' },
16
+ { id: 'heavy-worker', label: 'Heavy Worker', description: 'Broad or multi-file implementation' },
17
+ { id: 'reviewer', label: 'Reviewer', description: 'Diff review and risk checks' },
18
+ { id: 'debugger', label: 'Debugger', description: 'Root-cause analysis and failure tracing' },
19
+ ]);
20
+ const AGENT_ROLE_IDS = new Set(FIXED_AGENT_SLOTS.map((agent) => agent.id));
21
+ const DEFAULT_WORKFLOW_ID = 'default';
22
+
23
+ const SEARCH_CAPABLE_PROVIDERS = new Set([
24
+ 'openai-oauth', 'openai', 'grok-oauth', 'xai', 'gemini', 'anthropic', 'anthropic-oauth',
25
+ ]);
26
+ export const SEARCH_DEFAULT_PROVIDER = 'default';
27
+ export const SEARCH_DEFAULT_MODEL = 'default';
28
+ const SEARCH_PROVIDER_ALIASES = Object.freeze({
29
+ 'openai-api': 'openai',
30
+ 'xai-api': 'xai',
31
+ 'gemini-api': 'gemini',
32
+ 'anthropic-api': 'anthropic',
33
+ });
34
+
35
+ const agentDefinitionCache = new Map();
36
+ const AGENT_DEFINITION_CACHE_LIMIT = 64;
37
+ function setAgentDefinitionCache(key, value) {
38
+ if (!agentDefinitionCache.has(key) && agentDefinitionCache.size >= AGENT_DEFINITION_CACHE_LIMIT) {
39
+ const oldestKey = agentDefinitionCache.keys().next().value;
40
+ agentDefinitionCache.delete(oldestKey);
41
+ }
42
+ agentDefinitionCache.set(key, value);
43
+ }
44
+
45
+ export function workflowPresetId(slot) {
46
+ return `workflow-${slot}`;
47
+ }
48
+
49
+ function workflowPresetName(slot) {
50
+ return `WORKFLOW ${String(slot || '').toUpperCase()}`;
51
+ }
52
+
53
+ export function agentPresetSlot(agentId) {
54
+ return `agent-${String(agentId || '').replace(/[^a-z0-9_.-]+/gi, '-').toLowerCase()}`;
55
+ }
56
+
57
+ export function normalizeAgentId(value) {
58
+ const id = clean(value).toLowerCase().replace(/[\s_]+/g, '-');
59
+ if (id === 'explorer') return 'explore';
60
+ if (id === 'maint' || id === 'maintenance' || id === 'memory') return 'maintainer';
61
+ if (id === 'heavy' || id === 'heavyworker') return 'heavy-worker';
62
+ if (id === 'review') return 'reviewer';
63
+ if (id === 'debug') return 'debugger';
64
+ return AGENT_ROLE_IDS.has(id) ? id : '';
65
+ }
66
+
67
+ export function normalizeWorkflowId(value, fallback = '') {
68
+ const id = clean(value).toLowerCase().replace(/[\s_]+/g, '-');
69
+ return /^[a-z0-9][a-z0-9_.-]*$/.test(id) ? id : fallback;
70
+ }
71
+
72
+ // A workflow/agent pack loader is created per data/root layout via
73
+ // createWorkflowHelpers, and the config-aware route helpers via
74
+ // createWorkflowRouteHelpers.
75
+ export function createWorkflowHelpers({ rootDir, dataDir, readMarkdownDocument, normalizeAgentPermissionOrNone }) {
76
+ function workflowSourceDirs(dir) {
77
+ return [
78
+ { root: join(rootDir, 'workflows'), source: 'built-in' },
79
+ { root: join(dir || dataDir, 'workflows'), source: 'user' },
80
+ ];
81
+ }
82
+
83
+ function agentSourceDirs(dir, id) {
84
+ return [
85
+ join(dir || dataDir, 'agents', id),
86
+ join(rootDir, 'agents', id),
87
+ ];
88
+ }
89
+
90
+ function readWorkflowPackFromDir(dir, source = 'built-in', dirName = '') {
91
+ const entry = 'WORKFLOW.md';
92
+ const doc = readMarkdownDocument(readTextSafe(join(dir, entry)));
93
+ const body = doc.body;
94
+ if (!body) return null;
95
+ const fm = doc.frontmatter || {};
96
+ const id = normalizeWorkflowId(clean(fm.id) || dirName || basename(dir));
97
+ if (!id) return null;
98
+ const agentsConfigured = Object.prototype.hasOwnProperty.call(fm, 'agents');
99
+ return {
100
+ id,
101
+ name: clean(fm.name) || id,
102
+ description: clean(fm.description),
103
+ entry,
104
+ agentsConfigured,
105
+ agents: agentsConfigured
106
+ ? String(fm.agents || '')
107
+ .split(',')
108
+ .map((agent) => normalizeAgentId(agent) || normalizeWorkflowId(agent))
109
+ .filter(Boolean)
110
+ : [],
111
+ body,
112
+ source,
113
+ };
114
+ }
115
+
116
+ function listWorkflowPacks(dir) {
117
+ const byId = new Map();
118
+ for (const { root, source } of workflowSourceDirs(dir)) {
119
+ if (!existsSync(root)) continue;
120
+ let entries = [];
121
+ try { entries = readdirSync(root, { withFileTypes: true }); } catch { entries = []; }
122
+ for (const entry of entries) {
123
+ if (!entry.isDirectory()) continue;
124
+ const d = join(root, entry.name);
125
+ if (!existsSync(join(d, 'WORKFLOW.md'))) continue;
126
+ const pack = readWorkflowPackFromDir(d, source, entry.name);
127
+ if (pack) byId.set(pack.id, pack);
128
+ }
129
+ }
130
+ return [...byId.values()].sort((a, b) => {
131
+ if (a.id === DEFAULT_WORKFLOW_ID) return -1;
132
+ if (b.id === DEFAULT_WORKFLOW_ID) return 1;
133
+ return a.name.localeCompare(b.name);
134
+ });
135
+ }
136
+
137
+ function activeWorkflowId(config) {
138
+ return normalizeWorkflowId(config?.workflow?.active, DEFAULT_WORKFLOW_ID);
139
+ }
140
+
141
+ function loadWorkflowPack(dir, id) {
142
+ const wanted = normalizeWorkflowId(id, DEFAULT_WORKFLOW_ID);
143
+ for (const { root, source } of workflowSourceDirs(dir).reverse()) {
144
+ const pack = readWorkflowPackFromDir(join(root, wanted), source, wanted);
145
+ if (pack) return pack;
146
+ }
147
+ return readWorkflowPackFromDir(join(rootDir, 'workflows', DEFAULT_WORKFLOW_ID), 'built-in', DEFAULT_WORKFLOW_ID);
148
+ }
149
+
150
+ function workflowSummary(pack) {
151
+ const id = normalizeWorkflowId(pack?.id, DEFAULT_WORKFLOW_ID);
152
+ return {
153
+ id,
154
+ name: clean(pack?.name) || (id === DEFAULT_WORKFLOW_ID ? 'Default' : id),
155
+ description: clean(pack?.description),
156
+ source: clean(pack?.source),
157
+ };
158
+ }
159
+
160
+ function activeWorkflowSummary(config, dir) {
161
+ return workflowSummary(loadWorkflowPack(dir, activeWorkflowId(config)));
162
+ }
163
+
164
+ function loadAgentDefinition(dir, id) {
165
+ const agentId = normalizeAgentId(id) || normalizeWorkflowId(id);
166
+ if (!agentId) return null;
167
+ const cacheKey = `${dir || dataDir}\n${agentId}`;
168
+ if (agentDefinitionCache.has(cacheKey)) return agentDefinitionCache.get(cacheKey);
169
+ for (const d of agentSourceDirs(dir, agentId)) {
170
+ const manifest = readJsonSafe(join(d, 'agent.json')) || {};
171
+ const entry = clean(manifest.entry) || 'AGENT.md';
172
+ const doc = readMarkdownDocument(readTextSafe(join(d, entry)));
173
+ const body = doc.body;
174
+ if (!body) continue;
175
+ const definition = {
176
+ id: agentId,
177
+ name: clean(manifest.name) || FIXED_AGENT_SLOTS.find((agent) => agent.id === agentId)?.label || agentId,
178
+ description: clean(manifest.description) || FIXED_AGENT_SLOTS.find((agent) => agent.id === agentId)?.description || '',
179
+ permission: normalizeAgentPermissionOrNone(doc.frontmatter.permission),
180
+ frontmatter: doc.frontmatter,
181
+ body,
182
+ };
183
+ setAgentDefinitionCache(cacheKey, definition);
184
+ return definition;
185
+ }
186
+ const legacyDoc = readMarkdownDocument(readTextSafe(join(rootDir, 'agents', `${agentId}.md`)));
187
+ if (!legacyDoc.body) {
188
+ setAgentDefinitionCache(cacheKey, null);
189
+ return null;
190
+ }
191
+ const definition = {
192
+ id: agentId,
193
+ name: FIXED_AGENT_SLOTS.find((agent) => agent.id === agentId)?.label || agentId,
194
+ description: '',
195
+ permission: normalizeAgentPermissionOrNone(legacyDoc.frontmatter.permission),
196
+ frontmatter: legacyDoc.frontmatter,
197
+ body: legacyDoc.body,
198
+ };
199
+ setAgentDefinitionCache(cacheKey, definition);
200
+ return definition;
201
+ }
202
+
203
+ function workflowContextBlock(config, dir) {
204
+ const pack = loadWorkflowPack(dir, activeWorkflowId(config));
205
+ if (!pack) return '';
206
+ const lines = [`# Active Workflow: ${pack.name}`];
207
+ if (pack.description) lines.push(pack.description);
208
+ lines.push(pack.body);
209
+ const agentIds = pack.agentsConfigured ? pack.agents : FIXED_AGENT_SLOTS.map((agent) => agent.id);
210
+ const agentBlocks = agentIds.map((id) => loadAgentDefinition(dir, id)).filter(Boolean);
211
+ if (agentBlocks.length) {
212
+ lines.push('# Available Agents');
213
+ for (const agent of agentBlocks) {
214
+ lines.push(`## ${agent.name} (${agent.id})`);
215
+ if (agent.description) lines.push(agent.description);
216
+ lines.push(agent.body);
217
+ }
218
+ }
219
+ return lines.join('\n\n');
220
+ }
221
+
222
+ return {
223
+ listWorkflowPacks,
224
+ activeWorkflowId,
225
+ loadWorkflowPack,
226
+ workflowSummary,
227
+ activeWorkflowSummary,
228
+ loadAgentDefinition,
229
+ workflowContextBlock,
230
+ };
231
+ }
232
+
233
+ export function normalizeSearchProviderId(provider) {
234
+ const id = clean(provider);
235
+ return SEARCH_PROVIDER_ALIASES[id] || id;
236
+ }
237
+
238
+ export function isDefaultSearchRouteConfig(routeLike = {}) {
239
+ return normalizeSearchProviderId(routeLike?.provider) === SEARCH_DEFAULT_PROVIDER
240
+ && clean(routeLike?.model).toLowerCase() === SEARCH_DEFAULT_MODEL;
241
+ }
242
+
243
+ export function isSearchCapableProvider(provider) {
244
+ return SEARCH_CAPABLE_PROVIDERS.has(normalizeSearchProviderId(provider));
245
+ }
246
+
247
+ export function normalizeSearchRouteConfig(routeLike, fallback = {}) {
248
+ const provider = normalizeSearchProviderId(routeLike?.provider || fallback.provider);
249
+ const model = clean(routeLike?.model || fallback.model);
250
+ if (!provider || !model) return null;
251
+ let effort = null;
252
+ try {
253
+ effort = normalizeEffortInput(routeLike?.effort ?? fallback.effort);
254
+ } catch {
255
+ effort = null;
256
+ }
257
+ const fast = routeLike?.fast ?? fallback.fast;
258
+ const toolType = clean(routeLike?.toolType || fallback.toolType);
259
+ return {
260
+ provider,
261
+ model,
262
+ ...(effort ? { effort } : {}),
263
+ ...(fast === true ? { fast: true } : {}),
264
+ ...(toolType ? { toolType } : {}),
265
+ };
266
+ }
267
+
268
+ export function normalizeWorkflowRoute(routeLike, fallback = {}) {
269
+ const provider = clean(routeLike?.provider) || clean(fallback.provider);
270
+ const model = clean(routeLike?.model) || clean(fallback.model);
271
+ if (!provider || !model) return null;
272
+ // Defensive: a workflow/agent route must carry a real model id. Reject values
273
+ // that are obviously free-form text (whitespace, prose) so a bad string can
274
+ // never be persisted as a preset/workflow route.
275
+ if (!isLikelyRawModelId(model)) return null;
276
+ const effort = normalizeEffortInput(routeLike?.effort ?? fallback.effort);
277
+ const fast = routeLike?.fast ?? fallback.fast;
278
+ return {
279
+ provider,
280
+ model,
281
+ ...(effort ? { effort } : {}),
282
+ ...(fast === true ? { fast: true } : {}),
283
+ };
284
+ }
285
+
286
+ export function upsertWorkflowPreset(presets, slot, routeLike) {
287
+ const route = normalizeWorkflowRoute(routeLike);
288
+ if (!route) return presets;
289
+ const id = workflowPresetId(slot);
290
+ const preset = {
291
+ id,
292
+ name: workflowPresetName(slot),
293
+ type: 'agent',
294
+ provider: route.provider,
295
+ model: route.model,
296
+ ...(route.effort ? { effort: route.effort } : {}),
297
+ ...(route.fast === true ? { fast: true } : {}),
298
+ tools: 'full',
299
+ };
300
+ const next = (Array.isArray(presets) ? presets : []).filter((p) => clean(p?.id) !== id && clean(p?.name) !== preset.name);
301
+ next.push(preset);
302
+ return next;
303
+ }
304
+
305
+ // Config-aware route resolvers need resolveDefaultProvider + findPreset from
306
+ // the runtime; created via this factory.
307
+ export function createWorkflowRouteHelpers({ resolveDefaultProvider, findPreset }) {
308
+ function summarizeWorkflowRoutes(config) {
309
+ const routes = config?.workflowRoutes && typeof config.workflowRoutes === 'object' ? config.workflowRoutes : {};
310
+ const fallbackProvider = resolveDefaultProvider(config);
311
+ const out = {};
312
+ for (const slot of WORKFLOW_ROUTE_SLOTS) {
313
+ const route = routes[slot];
314
+ // Read/interpret path: a route with a model but no provider falls back to
315
+ // config.defaultProvider (then DEFAULT_PROVIDER).
316
+ if (route?.model && (route?.provider || fallbackProvider)) {
317
+ out[slot] = normalizeWorkflowRoute(route, { provider: fallbackProvider });
318
+ }
319
+ }
320
+ return out;
321
+ }
322
+
323
+ function routeFromPreset(config, slotValue) {
324
+ // Maintenance slots now store a direct {provider, model} route. Accept that
325
+ // shape first; fall back to the legacy preset-NAME string lookup so configs
326
+ // written before the route migration still resolve.
327
+ if (slotValue && typeof slotValue === 'object' && !Array.isArray(slotValue)) {
328
+ const direct = normalizeWorkflowRoute(slotValue);
329
+ if (direct) return direct;
330
+ }
331
+ const preset = findPreset(config, slotValue);
332
+ return preset ? normalizeWorkflowRoute(preset) : null;
333
+ }
334
+
335
+ function agentRouteFromConfig(config, agentId, _dataDir) {
336
+ const id = normalizeAgentId(agentId);
337
+ if (!id) return null;
338
+ // Read/interpret path: inject config.defaultProvider (then DEFAULT_PROVIDER)
339
+ // when a stored route omits its provider.
340
+ const fallback = { provider: resolveDefaultProvider(config) };
341
+ const explicit = normalizeWorkflowRoute(config?.agents?.[id], fallback)
342
+ || (id === 'maintainer' ? normalizeWorkflowRoute(config?.agents?.maintenance, fallback) : null);
343
+ if (explicit) return explicit;
344
+
345
+ const agent = FIXED_AGENT_SLOTS.find((item) => item.id === id);
346
+ if (agent?.workflowSlot) {
347
+ const workflowRoute = normalizeWorkflowRoute(config?.workflowRoutes?.[agent.workflowSlot], fallback);
348
+ if (workflowRoute) return workflowRoute;
349
+ }
350
+
351
+ if (id === 'explore') return routeFromPreset(config, config?.maintenance?.explore);
352
+ if (id === 'maintainer') return routeFromPreset(config, config?.maintenance?.memory);
353
+
354
+ return null;
355
+ }
356
+
357
+ return { summarizeWorkflowRoutes, routeFromPreset, agentRouteFromConfig };
358
+ }