mixdog 0.9.1 → 0.9.3

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 (301) hide show
  1. package/package.json +9 -1
  2. package/scripts/_bench-cwc.json +20 -0
  3. package/scripts/agent-loop-policy-test.mjs +37 -0
  4. package/scripts/agent-parallel-smoke.mjs +54 -10
  5. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  6. package/scripts/background-task-meta-smoke.mjs +1 -1
  7. package/scripts/bench-run.mjs +262 -0
  8. package/scripts/build-tui.mjs +13 -1
  9. package/scripts/compact-smoke.mjs +12 -0
  10. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  11. package/scripts/explore-bench.mjs +124 -0
  12. package/scripts/hook-bus-test.mjs +191 -0
  13. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  14. package/scripts/internal-comms-bench.mjs +727 -0
  15. package/scripts/internal-comms-smoke.mjs +75 -0
  16. package/scripts/lead-workflow-smoke.mjs +4 -4
  17. package/scripts/live-worker-smoke.mjs +9 -9
  18. package/scripts/output-style-bench.mjs +285 -0
  19. package/scripts/output-style-smoke.mjs +13 -10
  20. package/scripts/patch-replay.mjs +90 -0
  21. package/scripts/path-suffix-test.mjs +57 -0
  22. package/scripts/provider-stream-stall-test.mjs +276 -0
  23. package/scripts/provider-toolcall-test.mjs +599 -1
  24. package/scripts/recall-bench.mjs +207 -0
  25. package/scripts/routing-corpus.mjs +281 -0
  26. package/scripts/session-bench.mjs +1526 -0
  27. package/scripts/session-diag.mjs +595 -0
  28. package/scripts/task-bench.mjs +207 -0
  29. package/scripts/tool-failures.mjs +6 -6
  30. package/scripts/tool-smoke.mjs +310 -67
  31. package/scripts/toolcall-args-test.mjs +81 -0
  32. package/src/agents/debugger/AGENT.md +4 -4
  33. package/src/agents/heavy-worker/AGENT.md +20 -9
  34. package/src/agents/reviewer/AGENT.md +4 -4
  35. package/src/agents/worker/AGENT.md +17 -9
  36. package/src/app.mjs +10 -6
  37. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  38. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  39. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  40. package/src/headless-role.mjs +14 -14
  41. package/src/help.mjs +1 -0
  42. package/src/lib/rules-builder.cjs +32 -54
  43. package/src/mixdog-session-runtime.mjs +1040 -2036
  44. package/src/output-styles/default.md +12 -7
  45. package/src/output-styles/minimal.md +25 -0
  46. package/src/output-styles/oneline.md +21 -0
  47. package/src/output-styles/simple.md +10 -9
  48. package/src/repl.mjs +17 -7
  49. package/src/rules/agent/00-common.md +7 -5
  50. package/src/rules/agent/30-explorer.md +8 -12
  51. package/src/rules/lead/01-general.md +3 -1
  52. package/src/rules/lead/lead-tool.md +13 -2
  53. package/src/rules/shared/01-tool.md +23 -12
  54. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  55. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  59. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  60. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/context/collect.mjs +182 -67
  62. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  63. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +100 -18
  65. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +313 -84
  70. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +104 -38
  71. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +28 -33
  72. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  73. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  74. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  75. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  76. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +227 -31
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +83 -6
  79. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +229 -115
  80. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +213 -33
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  82. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +49 -17
  83. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +22 -17
  85. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  86. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  87. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  88. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
  89. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  90. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  91. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
  93. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  94. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  95. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  97. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  98. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  99. package/src/runtime/agent/orchestrator/session/loop.mjs +513 -1000
  100. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
  101. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  102. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
  103. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  104. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  105. package/src/runtime/agent/orchestrator/session/manager.mjs +232 -1152
  106. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  107. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  108. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  109. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  110. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
  111. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  113. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  118. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  119. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  121. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  122. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +14 -5
  123. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  124. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  125. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +23 -2
  126. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
  127. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  128. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  129. package/src/runtime/agent/orchestrator/tools/builtin.mjs +70 -1
  130. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  131. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  132. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  133. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
  134. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  135. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  136. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  137. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  138. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  139. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
  140. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  141. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  142. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  143. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  144. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  145. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +39 -4189
  146. package/src/runtime/agent/orchestrator/tools/patch.mjs +119 -5
  147. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
  148. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
  149. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  150. package/src/runtime/channels/backends/discord.mjs +99 -9
  151. package/src/runtime/channels/backends/telegram.mjs +501 -0
  152. package/src/runtime/channels/index.mjs +437 -1439
  153. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  154. package/src/runtime/channels/lib/config.mjs +54 -2
  155. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  156. package/src/runtime/channels/lib/format.mjs +4 -2
  157. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  158. package/src/runtime/channels/lib/output-forwarder.mjs +88 -67
  159. package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
  160. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  161. package/src/runtime/channels/lib/telegram-format.mjs +280 -0
  162. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  163. package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
  164. package/src/runtime/channels/lib/webhook.mjs +59 -31
  165. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  166. package/src/runtime/channels/tool-defs.mjs +1 -1
  167. package/src/runtime/memory/index.mjs +465 -345
  168. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  169. package/src/runtime/memory/lib/core-memory-store.mjs +352 -2
  170. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  171. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  172. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  173. package/src/runtime/memory/lib/memory-cycle2.mjs +65 -9
  174. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  175. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  176. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  177. package/src/runtime/memory/lib/memory.mjs +121 -4
  178. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  179. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  180. package/src/runtime/memory/lib/recall-format.mjs +183 -0
  181. package/src/runtime/memory/lib/session-ingest.mjs +107 -0
  182. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  183. package/src/runtime/memory/tool-defs.mjs +10 -7
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/background-tasks.mjs +2 -3
  186. package/src/runtime/shared/buffered-appender.mjs +149 -0
  187. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  188. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  189. package/src/runtime/shared/config.mjs +9 -0
  190. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  191. package/src/runtime/shared/schedules-store.mjs +21 -19
  192. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  193. package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
  194. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  195. package/src/runtime/shared/tool-surface.mjs +98 -13
  196. package/src/runtime/shared/transcript-writer.mjs +156 -0
  197. package/src/runtime/shared/update-checker.mjs +214 -0
  198. package/src/session-runtime/config-helpers.mjs +209 -0
  199. package/src/session-runtime/effort.mjs +128 -0
  200. package/src/session-runtime/fs-utils.mjs +10 -0
  201. package/src/session-runtime/model-capabilities.mjs +130 -0
  202. package/src/session-runtime/output-styles.mjs +124 -0
  203. package/src/session-runtime/plugin-mcp.mjs +114 -0
  204. package/src/session-runtime/session-text.mjs +100 -0
  205. package/src/session-runtime/statusline-route.mjs +35 -0
  206. package/src/session-runtime/tool-catalog.mjs +720 -0
  207. package/src/session-runtime/workflow.mjs +358 -0
  208. package/src/standalone/agent-tool.mjs +302 -117
  209. package/src/standalone/channel-admin.mjs +133 -40
  210. package/src/standalone/channel-worker.mjs +10 -292
  211. package/src/standalone/explore-tool.mjs +12 -5
  212. package/src/standalone/hook-bus.mjs +165 -8
  213. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  214. package/src/standalone/opencode-go-login.mjs +121 -0
  215. package/src/standalone/provider-admin.mjs +25 -3
  216. package/src/standalone/usage-dashboard.mjs +1 -1
  217. package/src/tui/App.jsx +2883 -778
  218. package/src/tui/components/ConfirmBar.jsx +47 -0
  219. package/src/tui/components/ContextPanel.jsx +5 -3
  220. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  221. package/src/tui/components/Markdown.jsx +22 -98
  222. package/src/tui/components/Message.jsx +14 -35
  223. package/src/tui/components/Picker.jsx +87 -12
  224. package/src/tui/components/PromptInput.jsx +355 -22
  225. package/src/tui/components/QueuedCommands.jsx +1 -1
  226. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  227. package/src/tui/components/Spinner.jsx +7 -7
  228. package/src/tui/components/StatusLine.jsx +40 -21
  229. package/src/tui/components/TextEntryPanel.jsx +51 -7
  230. package/src/tui/components/ToolExecution.jsx +183 -101
  231. package/src/tui/components/TurnDone.jsx +4 -4
  232. package/src/tui/components/UsagePanel.jsx +1 -1
  233. package/src/tui/components/tool-output-format.mjs +161 -23
  234. package/src/tui/components/tool-output-format.test.mjs +87 -0
  235. package/src/tui/display-width.mjs +69 -0
  236. package/src/tui/display-width.test.mjs +35 -0
  237. package/src/tui/dist/index.mjs +6731 -2333
  238. package/src/tui/engine/agent-envelope.mjs +296 -0
  239. package/src/tui/engine/boot-profile.mjs +21 -0
  240. package/src/tui/engine/labels.mjs +67 -0
  241. package/src/tui/engine/notice-text.mjs +112 -0
  242. package/src/tui/engine/queue-helpers.mjs +161 -0
  243. package/src/tui/engine/session-stats.mjs +46 -0
  244. package/src/tui/engine/tool-call-fields.mjs +23 -0
  245. package/src/tui/engine/tool-result-text.mjs +126 -0
  246. package/src/tui/engine.mjs +569 -948
  247. package/src/tui/index.jsx +117 -7
  248. package/src/tui/input-editing.mjs +58 -8
  249. package/src/tui/input-editing.selection.test.mjs +75 -0
  250. package/src/tui/keyboard-protocol.mjs +42 -0
  251. package/src/tui/lib/voice-recorder.mjs +469 -0
  252. package/src/tui/markdown/format-token.mjs +128 -76
  253. package/src/tui/markdown/format-token.test.mjs +61 -19
  254. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  255. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  256. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  257. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  258. package/src/tui/markdown/table-layout.mjs +9 -9
  259. package/src/tui/paste-attachments.mjs +36 -9
  260. package/src/tui/paste-fix.test.mjs +119 -0
  261. package/src/tui/prompt-history-store.mjs +129 -0
  262. package/src/tui/prompt-history-store.test.mjs +52 -0
  263. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  264. package/src/tui/theme.mjs +41 -657
  265. package/src/tui/themes/base.mjs +86 -0
  266. package/src/tui/themes/basic.mjs +85 -0
  267. package/src/tui/themes/catppuccin.mjs +72 -0
  268. package/src/tui/themes/dracula.mjs +70 -0
  269. package/src/tui/themes/everforest.mjs +71 -0
  270. package/src/tui/themes/gruvbox.mjs +71 -0
  271. package/src/tui/themes/index.mjs +71 -0
  272. package/src/tui/themes/indigo.mjs +78 -0
  273. package/src/tui/themes/kanagawa.mjs +80 -0
  274. package/src/tui/themes/light.mjs +81 -0
  275. package/src/tui/themes/nord.mjs +72 -0
  276. package/src/tui/themes/onedark.mjs +16 -0
  277. package/src/tui/themes/rosepine.mjs +70 -0
  278. package/src/tui/themes/teal.mjs +80 -0
  279. package/src/tui/themes/tokyonight.mjs +79 -0
  280. package/src/tui/themes/utils.mjs +106 -0
  281. package/src/tui/themes/warm.mjs +79 -0
  282. package/src/tui/transcript-tool-failures.mjs +13 -2
  283. package/src/ui/markdown.mjs +1 -1
  284. package/src/ui/model-display.mjs +2 -2
  285. package/src/ui/statusline.mjs +75 -27
  286. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  287. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  288. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  289. package/src/workflows/default/WORKFLOW.md +46 -12
  290. package/src/workflows/sequential/WORKFLOW.md +51 -0
  291. package/src/workflows/solo/WORKFLOW.md +12 -1
  292. package/vendor/ink/build/display-width.js +62 -0
  293. package/vendor/ink/build/ink.js +100 -12
  294. package/vendor/ink/build/measure-text.js +4 -1
  295. package/vendor/ink/build/output.js +115 -9
  296. package/vendor/ink/build/render-node-to-output.js +4 -1
  297. package/vendor/ink/build/render.js +4 -0
  298. package/src/output-styles/extreme-simple.md +0 -20
  299. package/src/rules/lead/04-workflow.md +0 -51
  300. package/src/workflows/default/workflow.json +0 -13
  301. package/src/workflows/solo/workflow.json +0 -7
@@ -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
+ }