mixdog 0.7.18 → 0.8.1

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 (847) hide show
  1. package/README.md +37 -331
  2. package/package.json +67 -99
  3. package/scripts/boot-smoke.mjs +94 -0
  4. package/scripts/build-tui.mjs +52 -0
  5. package/scripts/compact-smoke.mjs +199 -0
  6. package/scripts/lead-workflow-smoke.mjs +598 -0
  7. package/scripts/live-worker-smoke.mjs +239 -0
  8. package/scripts/output-style-smoke.mjs +101 -0
  9. package/scripts/session-context-bench.mjs +172 -0
  10. package/scripts/smoke-loop-report.mjs +221 -0
  11. package/scripts/smoke-loop.mjs +201 -0
  12. package/scripts/smoke.mjs +113 -0
  13. package/scripts/tool-failures.mjs +143 -0
  14. package/scripts/tool-smoke.mjs +452 -0
  15. package/src/agents/debugger/AGENT.md +3 -0
  16. package/src/agents/debugger/agent.json +6 -0
  17. package/src/agents/explore/AGENT.md +4 -0
  18. package/src/agents/explore/agent.json +6 -0
  19. package/src/agents/heavy-worker/AGENT.md +3 -0
  20. package/src/agents/heavy-worker/agent.json +6 -0
  21. package/src/agents/maintainer/AGENT.md +3 -0
  22. package/src/agents/maintainer/agent.json +6 -0
  23. package/src/agents/reviewer/AGENT.md +3 -0
  24. package/src/agents/reviewer/agent.json +6 -0
  25. package/src/agents/scheduler-task.md +3 -0
  26. package/src/agents/web-researcher/AGENT.md +3 -0
  27. package/src/agents/web-researcher/agent.json +6 -0
  28. package/src/agents/webhook-handler.md +3 -0
  29. package/src/agents/worker/AGENT.md +3 -0
  30. package/src/agents/worker/agent.json +6 -0
  31. package/src/app.mjs +90 -0
  32. package/src/cli.mjs +11 -0
  33. package/src/defaults/hidden-roles.json +72 -0
  34. package/src/defaults/mixdog-config.template.json +15 -0
  35. package/src/hooks/lib/permission-evaluator.cjs +488 -0
  36. package/src/hooks/lib/settings-loader.cjs +112 -0
  37. package/src/lib/keychain-cjs.cjs +332 -0
  38. package/src/lib/plugin-paths.cjs +28 -0
  39. package/src/lib/rules-builder.cjs +315 -0
  40. package/src/mixdog-session-runtime.mjs +3813 -0
  41. package/src/output-styles/default.md +38 -0
  42. package/src/output-styles/extreme-simple.md +17 -0
  43. package/src/output-styles/simple.md +17 -0
  44. package/src/repl.mjs +330 -0
  45. package/src/rules/bridge/00-common.md +5 -0
  46. package/src/rules/bridge/20-skip-protocol.md +11 -0
  47. package/src/rules/bridge/30-explorer.md +4 -0
  48. package/src/rules/bridge/40-cycle1-agent.md +28 -0
  49. package/src/rules/bridge/41-cycle2-agent.md +59 -0
  50. package/src/rules/lead/00-tool-lead.md +5 -0
  51. package/src/rules/lead/01-general.md +5 -0
  52. package/src/rules/lead/02-channels.md +3 -0
  53. package/src/rules/lead/04-workflow.md +12 -0
  54. package/src/rules/shared/00-language.md +3 -0
  55. package/src/rules/shared/01-tool.md +3 -0
  56. package/src/runtime/agent/orchestrator/bridge-trace.mjs +814 -0
  57. package/src/runtime/agent/orchestrator/cache-mtime.mjs +60 -0
  58. package/src/runtime/agent/orchestrator/config.mjs +446 -0
  59. package/src/runtime/agent/orchestrator/context/collect.mjs +796 -0
  60. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +417 -0
  61. package/src/runtime/agent/orchestrator/internal-roles.mjs +188 -0
  62. package/src/runtime/agent/orchestrator/internal-tools.mjs +88 -0
  63. package/src/runtime/agent/orchestrator/mcp/client.mjs +345 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2104 -0
  65. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +784 -0
  66. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +341 -0
  67. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1679 -0
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +959 -0
  69. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +213 -0
  70. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +38 -0
  71. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +471 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +615 -0
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +808 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +1719 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2587 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1953 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +136 -0
  78. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +317 -0
  79. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +109 -0
  80. package/src/runtime/agent/orchestrator/providers/registry.mjs +247 -0
  81. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +332 -0
  82. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +11 -0
  83. package/src/runtime/agent/orchestrator/providers/trace-utils.mjs +50 -0
  84. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  85. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  86. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +318 -0
  87. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +367 -0
  88. package/src/runtime/agent/orchestrator/session/compact.mjs +882 -0
  89. package/src/runtime/agent/orchestrator/session/context-utils.mjs +233 -0
  90. package/src/runtime/agent/orchestrator/session/loop.mjs +2269 -0
  91. package/src/runtime/agent/orchestrator/session/manager.mjs +2972 -0
  92. package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
  93. package/src/runtime/agent/orchestrator/session/store.mjs +870 -0
  94. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  95. package/src/runtime/agent/orchestrator/smart-bridge/bridge-llm.mjs +339 -0
  96. package/src/runtime/agent/orchestrator/smart-bridge/cache-strategy.mjs +419 -0
  97. package/src/runtime/agent/orchestrator/stall-policy.mjs +227 -0
  98. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +235 -0
  99. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +723 -0
  100. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +389 -0
  102. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +637 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +165 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +104 -0
  105. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +194 -0
  106. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +596 -0
  107. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +110 -0
  108. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +153 -0
  109. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  110. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +189 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +731 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +168 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +465 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +160 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +982 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1087 -0
  118. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +231 -0
  119. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +223 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin.mjs +478 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  122. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4102 -0
  123. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +154 -0
  125. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +26 -0
  126. package/src/runtime/agent/orchestrator/tools/patch-binary-fetcher.mjs +143 -0
  127. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +18 -0
  128. package/src/runtime/agent/orchestrator/tools/patch.mjs +2772 -0
  129. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +114 -0
  130. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +880 -0
  131. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +312 -0
  132. package/src/runtime/channels/backends/discord.mjs +781 -0
  133. package/src/runtime/channels/data/voice-runtime-manifest.json +138 -0
  134. package/src/runtime/channels/index.mjs +3309 -0
  135. package/src/runtime/channels/lib/config.mjs +285 -0
  136. package/src/runtime/channels/lib/drop-trace.mjs +71 -0
  137. package/src/runtime/channels/lib/event-pipeline.mjs +81 -0
  138. package/src/runtime/channels/lib/holidays.mjs +138 -0
  139. package/src/runtime/channels/lib/hook-pipe-server.mjs +671 -0
  140. package/src/runtime/channels/lib/output-forwarder.mjs +765 -0
  141. package/src/runtime/channels/lib/runtime-paths.mjs +497 -0
  142. package/src/runtime/channels/lib/scheduler.mjs +710 -0
  143. package/src/runtime/channels/lib/session-discovery.mjs +102 -0
  144. package/src/runtime/channels/lib/state-file.mjs +68 -0
  145. package/src/runtime/channels/lib/status-snapshot.mjs +224 -0
  146. package/src/runtime/channels/lib/tool-format.mjs +122 -0
  147. package/src/runtime/channels/lib/transcript-discovery.mjs +195 -0
  148. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +734 -0
  149. package/src/runtime/channels/lib/webhook.mjs +1288 -0
  150. package/src/runtime/channels/tool-defs.mjs +177 -0
  151. package/src/runtime/lib/keychain-cjs.cjs +289 -0
  152. package/src/runtime/memory/data/runtime-manifest.json +40 -0
  153. package/src/runtime/memory/index.mjs +3706 -0
  154. package/src/runtime/memory/lib/core-memory-store.mjs +336 -0
  155. package/src/runtime/memory/lib/embedding-provider.mjs +275 -0
  156. package/src/runtime/memory/lib/embedding-worker.mjs +331 -0
  157. package/src/runtime/memory/lib/memory-cycle-requests.mjs +276 -0
  158. package/src/runtime/memory/lib/memory-cycle1.mjs +783 -0
  159. package/src/runtime/memory/lib/memory-cycle2.mjs +1389 -0
  160. package/src/runtime/memory/lib/memory-cycle3.mjs +646 -0
  161. package/src/runtime/memory/lib/memory-embed.mjs +300 -0
  162. package/src/runtime/memory/lib/memory-ops-policy.mjs +149 -0
  163. package/src/runtime/memory/lib/memory-recall-store.mjs +644 -0
  164. package/src/runtime/memory/lib/memory.mjs +418 -0
  165. package/src/runtime/memory/lib/pg/adapter.mjs +328 -0
  166. package/src/runtime/memory/lib/pg/process.mjs +366 -0
  167. package/src/runtime/memory/lib/pg/supervisor.mjs +495 -0
  168. package/src/runtime/memory/lib/runtime-fetcher.mjs +464 -0
  169. package/src/runtime/memory/lib/trace-store.mjs +734 -0
  170. package/src/runtime/memory/tool-defs.mjs +79 -0
  171. package/src/runtime/search/index.mjs +925 -0
  172. package/src/runtime/search/lib/config.mjs +61 -0
  173. package/src/runtime/search/lib/web-tools.mjs +1278 -0
  174. package/src/runtime/search/tool-defs.mjs +64 -0
  175. package/src/runtime/shared/atomic-file.mjs +435 -0
  176. package/src/runtime/shared/background-tasks.mjs +376 -0
  177. package/src/runtime/shared/child-guardian.mjs +98 -0
  178. package/src/runtime/shared/config.mjs +393 -0
  179. package/src/runtime/shared/err-text.mjs +121 -0
  180. package/src/runtime/shared/launcher-control.mjs +259 -0
  181. package/src/runtime/shared/llm/http-agent.mjs +129 -0
  182. package/src/runtime/shared/open-url.mjs +37 -0
  183. package/src/runtime/shared/plugin-paths.mjs +25 -0
  184. package/src/runtime/shared/process-shutdown.mjs +147 -0
  185. package/src/runtime/shared/schedules-store.mjs +70 -0
  186. package/src/runtime/shared/tool-execution-contract.mjs +104 -0
  187. package/src/runtime/shared/tool-surface.mjs +947 -0
  188. package/src/runtime/shared/user-cwd.mjs +221 -0
  189. package/src/runtime/shared/user-data-guard.mjs +232 -0
  190. package/src/runtime/shared/workspace-router.mjs +259 -0
  191. package/src/standalone/bridge-tool.mjs +1414 -0
  192. package/src/standalone/channel-admin.mjs +366 -0
  193. package/src/standalone/channel-worker-preload.cjs +3 -0
  194. package/src/standalone/channel-worker.mjs +353 -0
  195. package/src/standalone/explore-tool.mjs +233 -0
  196. package/src/standalone/hook-bus.mjs +246 -0
  197. package/src/standalone/plugin-admin.mjs +247 -0
  198. package/src/standalone/provider-admin.mjs +338 -0
  199. package/src/standalone/seeds.mjs +94 -0
  200. package/src/standalone/usage-dashboard.mjs +510 -0
  201. package/src/tui/App.jsx +5446 -0
  202. package/src/tui/components/AnsiText.jsx +199 -0
  203. package/src/tui/components/ContextPanel.jsx +265 -0
  204. package/src/tui/components/Markdown.jsx +205 -0
  205. package/src/tui/components/MarkdownTable.jsx +204 -0
  206. package/src/tui/components/Message.jsx +103 -0
  207. package/src/tui/components/Picker.jsx +317 -0
  208. package/src/tui/components/PromptInput.jsx +584 -0
  209. package/src/tui/components/QueuedCommands.jsx +47 -0
  210. package/src/tui/components/SlashCommandPalette.jsx +114 -0
  211. package/src/tui/components/Spinner.jsx +317 -0
  212. package/src/tui/components/StatusLine.jsx +87 -0
  213. package/src/tui/components/TextEntryPanel.jsx +323 -0
  214. package/src/tui/components/ToolExecution.jsx +791 -0
  215. package/src/tui/components/TurnDone.jsx +78 -0
  216. package/src/tui/components/UsagePanel.jsx +331 -0
  217. package/src/tui/dist/index.mjs +12420 -0
  218. package/src/tui/engine.mjs +2410 -0
  219. package/src/tui/figures.mjs +50 -0
  220. package/src/tui/hooks/useEngine.mjs +16 -0
  221. package/src/tui/index.jsx +254 -0
  222. package/src/tui/input-editing.mjs +242 -0
  223. package/src/tui/markdown/format-token.mjs +194 -0
  224. package/src/tui/paste-attachments.mjs +198 -0
  225. package/src/tui/runtime/shared/process-shutdown.mjs +1 -0
  226. package/src/tui/spinner-verbs.mjs +45 -0
  227. package/src/tui/theme.mjs +67 -0
  228. package/src/tui/time-format.mjs +53 -0
  229. package/src/ui/ansi.mjs +115 -0
  230. package/src/ui/markdown.mjs +195 -0
  231. package/src/ui/statusline.mjs +730 -0
  232. package/src/ui/tool-card.mjs +99 -0
  233. package/src/vendor/statusline/bin/statusline-lib.mjs +805 -0
  234. package/src/vendor/statusline/bin/statusline-route.mjs +596 -0
  235. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +285 -0
  236. package/src/vendor/statusline/src/gateway/claude-current.mjs +320 -0
  237. package/src/vendor/statusline/src/gateway/route-meta.mjs +753 -0
  238. package/src/vendor/statusline/src/gateway/session-routes.mjs +244 -0
  239. package/src/workflows/default/WORKFLOW.md +7 -0
  240. package/src/workflows/default/workflow.json +14 -0
  241. package/vendor/ink/build/ansi-tokenizer.d.ts +38 -0
  242. package/vendor/ink/build/ansi-tokenizer.js +316 -0
  243. package/vendor/ink/build/ansi-tokenizer.js.map +1 -0
  244. package/vendor/ink/build/colorize.d.ts +3 -0
  245. package/vendor/ink/build/colorize.js +48 -0
  246. package/vendor/ink/build/colorize.js.map +1 -0
  247. package/vendor/ink/build/components/AccessibilityContext.d.ts +3 -0
  248. package/vendor/ink/build/components/AccessibilityContext.js +5 -0
  249. package/vendor/ink/build/components/AccessibilityContext.js.map +1 -0
  250. package/vendor/ink/build/components/AnimationContext.d.ts +9 -0
  251. package/vendor/ink/build/components/AnimationContext.js +13 -0
  252. package/vendor/ink/build/components/AnimationContext.js.map +1 -0
  253. package/vendor/ink/build/components/App.d.ts +24 -0
  254. package/vendor/ink/build/components/App.js +554 -0
  255. package/vendor/ink/build/components/App.js.map +1 -0
  256. package/vendor/ink/build/components/AppContext.d.ts +80 -0
  257. package/vendor/ink/build/components/AppContext.js +25 -0
  258. package/vendor/ink/build/components/AppContext.js.map +1 -0
  259. package/vendor/ink/build/components/BackgroundContext.d.ts +4 -0
  260. package/vendor/ink/build/components/BackgroundContext.js +3 -0
  261. package/vendor/ink/build/components/BackgroundContext.js.map +1 -0
  262. package/vendor/ink/build/components/Box.d.ts +130 -0
  263. package/vendor/ink/build/components/Box.js +34 -0
  264. package/vendor/ink/build/components/Box.js.map +1 -0
  265. package/vendor/ink/build/components/CursorContext.d.ts +11 -0
  266. package/vendor/ink/build/components/CursorContext.js +8 -0
  267. package/vendor/ink/build/components/CursorContext.js.map +1 -0
  268. package/vendor/ink/build/components/ErrorBoundary.d.ts +18 -0
  269. package/vendor/ink/build/components/ErrorBoundary.js +23 -0
  270. package/vendor/ink/build/components/ErrorBoundary.js.map +1 -0
  271. package/vendor/ink/build/components/ErrorOverview.d.ts +6 -0
  272. package/vendor/ink/build/components/ErrorOverview.js +90 -0
  273. package/vendor/ink/build/components/ErrorOverview.js.map +1 -0
  274. package/vendor/ink/build/components/FocusContext.d.ts +16 -0
  275. package/vendor/ink/build/components/FocusContext.js +17 -0
  276. package/vendor/ink/build/components/FocusContext.js.map +1 -0
  277. package/vendor/ink/build/components/Newline.d.ts +13 -0
  278. package/vendor/ink/build/components/Newline.js +8 -0
  279. package/vendor/ink/build/components/Newline.js.map +1 -0
  280. package/vendor/ink/build/components/Spacer.d.ts +7 -0
  281. package/vendor/ink/build/components/Spacer.js +11 -0
  282. package/vendor/ink/build/components/Spacer.js.map +1 -0
  283. package/vendor/ink/build/components/Static.d.ts +24 -0
  284. package/vendor/ink/build/components/Static.js +28 -0
  285. package/vendor/ink/build/components/Static.js.map +1 -0
  286. package/vendor/ink/build/components/StderrContext.d.ts +15 -0
  287. package/vendor/ink/build/components/StderrContext.js +13 -0
  288. package/vendor/ink/build/components/StderrContext.js.map +1 -0
  289. package/vendor/ink/build/components/StdinContext.d.ts +28 -0
  290. package/vendor/ink/build/components/StdinContext.js +20 -0
  291. package/vendor/ink/build/components/StdinContext.js.map +1 -0
  292. package/vendor/ink/build/components/StdoutContext.d.ts +15 -0
  293. package/vendor/ink/build/components/StdoutContext.js +13 -0
  294. package/vendor/ink/build/components/StdoutContext.js.map +1 -0
  295. package/vendor/ink/build/components/Text.d.ts +55 -0
  296. package/vendor/ink/build/components/Text.js +50 -0
  297. package/vendor/ink/build/components/Text.js.map +1 -0
  298. package/vendor/ink/build/components/Transform.d.ts +16 -0
  299. package/vendor/ink/build/components/Transform.js +15 -0
  300. package/vendor/ink/build/components/Transform.js.map +1 -0
  301. package/vendor/ink/build/cursor-helpers.d.ts +39 -0
  302. package/vendor/ink/build/cursor-helpers.js +62 -0
  303. package/vendor/ink/build/cursor-helpers.js.map +1 -0
  304. package/vendor/ink/build/devtools-window-polyfill.d.ts +1 -0
  305. package/vendor/ink/build/devtools-window-polyfill.js +68 -0
  306. package/vendor/ink/build/devtools-window-polyfill.js.map +1 -0
  307. package/vendor/ink/build/devtools.d.ts +1 -0
  308. package/vendor/ink/build/devtools.js +36 -0
  309. package/vendor/ink/build/devtools.js.map +1 -0
  310. package/vendor/ink/build/dom.d.ts +62 -0
  311. package/vendor/ink/build/dom.js +143 -0
  312. package/vendor/ink/build/dom.js.map +1 -0
  313. package/vendor/ink/build/get-max-width.d.ts +3 -0
  314. package/vendor/ink/build/get-max-width.js +10 -0
  315. package/vendor/ink/build/get-max-width.js.map +1 -0
  316. package/vendor/ink/build/hooks/use-animation.d.ts +49 -0
  317. package/vendor/ink/build/hooks/use-animation.js +87 -0
  318. package/vendor/ink/build/hooks/use-animation.js.map +1 -0
  319. package/vendor/ink/build/hooks/use-app.d.ts +5 -0
  320. package/vendor/ink/build/hooks/use-app.js +8 -0
  321. package/vendor/ink/build/hooks/use-app.js.map +1 -0
  322. package/vendor/ink/build/hooks/use-box-metrics.d.ts +59 -0
  323. package/vendor/ink/build/hooks/use-box-metrics.js +81 -0
  324. package/vendor/ink/build/hooks/use-box-metrics.js.map +1 -0
  325. package/vendor/ink/build/hooks/use-cursor.d.ts +12 -0
  326. package/vendor/ink/build/hooks/use-cursor.js +29 -0
  327. package/vendor/ink/build/hooks/use-cursor.js.map +1 -0
  328. package/vendor/ink/build/hooks/use-focus-manager.d.ts +43 -0
  329. package/vendor/ink/build/hooks/use-focus-manager.js +18 -0
  330. package/vendor/ink/build/hooks/use-focus-manager.js.map +1 -0
  331. package/vendor/ink/build/hooks/use-focus.d.ts +30 -0
  332. package/vendor/ink/build/hooks/use-focus.js +43 -0
  333. package/vendor/ink/build/hooks/use-focus.js.map +1 -0
  334. package/vendor/ink/build/hooks/use-input.d.ts +132 -0
  335. package/vendor/ink/build/hooks/use-input.js +126 -0
  336. package/vendor/ink/build/hooks/use-input.js.map +1 -0
  337. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.d.ts +6 -0
  338. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js +12 -0
  339. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -0
  340. package/vendor/ink/build/hooks/use-paste.d.ts +35 -0
  341. package/vendor/ink/build/hooks/use-paste.js +62 -0
  342. package/vendor/ink/build/hooks/use-paste.js.map +1 -0
  343. package/vendor/ink/build/hooks/use-stderr.d.ts +5 -0
  344. package/vendor/ink/build/hooks/use-stderr.js +8 -0
  345. package/vendor/ink/build/hooks/use-stderr.js.map +1 -0
  346. package/vendor/ink/build/hooks/use-stdin.d.ts +7 -0
  347. package/vendor/ink/build/hooks/use-stdin.js +9 -0
  348. package/vendor/ink/build/hooks/use-stdin.js.map +1 -0
  349. package/vendor/ink/build/hooks/use-stdout.d.ts +5 -0
  350. package/vendor/ink/build/hooks/use-stdout.js +8 -0
  351. package/vendor/ink/build/hooks/use-stdout.js.map +1 -0
  352. package/vendor/ink/build/hooks/use-window-size.d.ts +18 -0
  353. package/vendor/ink/build/hooks/use-window-size.js +22 -0
  354. package/vendor/ink/build/hooks/use-window-size.js.map +1 -0
  355. package/vendor/ink/build/index.d.ts +42 -0
  356. package/vendor/ink/build/index.js +24 -0
  357. package/vendor/ink/build/index.js.map +1 -0
  358. package/vendor/ink/build/ink.d.ts +146 -0
  359. package/vendor/ink/build/ink.js +1022 -0
  360. package/vendor/ink/build/ink.js.map +1 -0
  361. package/vendor/ink/build/input-parser.d.ts +10 -0
  362. package/vendor/ink/build/input-parser.js +194 -0
  363. package/vendor/ink/build/input-parser.js.map +1 -0
  364. package/vendor/ink/build/instances.d.ts +3 -0
  365. package/vendor/ink/build/instances.js +8 -0
  366. package/vendor/ink/build/instances.js.map +1 -0
  367. package/vendor/ink/build/kitty-keyboard.d.ts +23 -0
  368. package/vendor/ink/build/kitty-keyboard.js +32 -0
  369. package/vendor/ink/build/kitty-keyboard.js.map +1 -0
  370. package/vendor/ink/build/log-update.d.ts +20 -0
  371. package/vendor/ink/build/log-update.js +261 -0
  372. package/vendor/ink/build/log-update.js.map +1 -0
  373. package/vendor/ink/build/measure-element.d.ts +20 -0
  374. package/vendor/ink/build/measure-element.js +13 -0
  375. package/vendor/ink/build/measure-element.js.map +1 -0
  376. package/vendor/ink/build/measure-text.d.ts +6 -0
  377. package/vendor/ink/build/measure-text.js +21 -0
  378. package/vendor/ink/build/measure-text.js.map +1 -0
  379. package/vendor/ink/build/output.d.ts +35 -0
  380. package/vendor/ink/build/output.js +328 -0
  381. package/vendor/ink/build/output.js.map +1 -0
  382. package/vendor/ink/build/parse-keypress.d.ts +20 -0
  383. package/vendor/ink/build/parse-keypress.js +495 -0
  384. package/vendor/ink/build/parse-keypress.js.map +1 -0
  385. package/vendor/ink/build/reconciler.d.ts +4 -0
  386. package/vendor/ink/build/reconciler.js +306 -0
  387. package/vendor/ink/build/reconciler.js.map +1 -0
  388. package/vendor/ink/build/render-background.d.ts +4 -0
  389. package/vendor/ink/build/render-background.js +25 -0
  390. package/vendor/ink/build/render-background.js.map +1 -0
  391. package/vendor/ink/build/render-border.d.ts +4 -0
  392. package/vendor/ink/build/render-border.js +84 -0
  393. package/vendor/ink/build/render-border.js.map +1 -0
  394. package/vendor/ink/build/render-node-to-output.d.ts +14 -0
  395. package/vendor/ink/build/render-node-to-output.js +162 -0
  396. package/vendor/ink/build/render-node-to-output.js.map +1 -0
  397. package/vendor/ink/build/render-to-string.d.ts +38 -0
  398. package/vendor/ink/build/render-to-string.js +116 -0
  399. package/vendor/ink/build/render-to-string.js.map +1 -0
  400. package/vendor/ink/build/render.d.ts +176 -0
  401. package/vendor/ink/build/render.js +71 -0
  402. package/vendor/ink/build/render.js.map +1 -0
  403. package/vendor/ink/build/renderer.d.ts +8 -0
  404. package/vendor/ink/build/renderer.js +64 -0
  405. package/vendor/ink/build/renderer.js.map +1 -0
  406. package/vendor/ink/build/sanitize-ansi.d.ts +2 -0
  407. package/vendor/ink/build/sanitize-ansi.js +27 -0
  408. package/vendor/ink/build/sanitize-ansi.js.map +1 -0
  409. package/vendor/ink/build/squash-text-nodes.d.ts +3 -0
  410. package/vendor/ink/build/squash-text-nodes.js +36 -0
  411. package/vendor/ink/build/squash-text-nodes.js.map +1 -0
  412. package/vendor/ink/build/styles.d.ts +302 -0
  413. package/vendor/ink/build/styles.js +303 -0
  414. package/vendor/ink/build/styles.js.map +1 -0
  415. package/vendor/ink/build/utils.d.ts +9 -0
  416. package/vendor/ink/build/utils.js +19 -0
  417. package/vendor/ink/build/utils.js.map +1 -0
  418. package/vendor/ink/build/wrap-text.d.ts +3 -0
  419. package/vendor/ink/build/wrap-text.js +38 -0
  420. package/vendor/ink/build/wrap-text.js.map +1 -0
  421. package/vendor/ink/build/write-synchronized.d.ts +4 -0
  422. package/vendor/ink/build/write-synchronized.js +9 -0
  423. package/vendor/ink/build/write-synchronized.js.map +1 -0
  424. package/vendor/ink/license +10 -0
  425. package/vendor/ink/package.json +137 -0
  426. package/.claude-plugin/marketplace.json +0 -34
  427. package/.claude-plugin/plugin.json +0 -20
  428. package/.gitattributes +0 -34
  429. package/.mcp.json +0 -14
  430. package/ARCHITECTURE.md +0 -77
  431. package/CHANGELOG.md +0 -30
  432. package/CONTRIBUTING.md +0 -45
  433. package/DATA-FLOW.md +0 -79
  434. package/LICENSE +0 -21
  435. package/SECURITY.md +0 -138
  436. package/UNINSTALL.md +0 -115
  437. package/agents/maintenance.md +0 -5
  438. package/agents/memory-classification.md +0 -30
  439. package/agents/scheduler-task.md +0 -18
  440. package/agents/webhook-handler.md +0 -27
  441. package/agents/worker.md +0 -24
  442. package/bin/bridge +0 -133
  443. package/bin/statusline-launcher.mjs +0 -82
  444. package/bin/statusline-lib.mjs +0 -581
  445. package/bin/statusline-route.mjs +0 -273
  446. package/bin/statusline.mjs +0 -638
  447. package/bun.lock +0 -927
  448. package/commands/config.md +0 -16
  449. package/commands/doctor.md +0 -13
  450. package/commands/model.md +0 -61
  451. package/commands/setup.md +0 -17
  452. package/defaults/hidden-roles.json +0 -68
  453. package/defaults/memory-chunk-prompt.md +0 -63
  454. package/defaults/mixdog-config.template.json +0 -27
  455. package/defaults/user-workflow.json +0 -8
  456. package/defaults/user-workflow.md +0 -17
  457. package/hooks/hooks.json +0 -73
  458. package/hooks/lib/active-instance.cjs +0 -77
  459. package/hooks/lib/permission-evaluator.cjs +0 -411
  460. package/hooks/lib/permission-route.cjs +0 -63
  461. package/hooks/lib/settings-loader.cjs +0 -117
  462. package/hooks/post-tool-use.cjs +0 -84
  463. package/hooks/pre-mcp-sandbox.cjs +0 -158
  464. package/hooks/pre-tool-subagent.cjs +0 -258
  465. package/hooks/session-start.cjs +0 -1493
  466. package/hooks/shim-launcher.cjs +0 -65
  467. package/hooks/turn-timer.cjs +0 -82
  468. package/lib/claude-md-writer.cjs +0 -386
  469. package/lib/keychain-cjs.cjs +0 -290
  470. package/lib/plugin-paths.cjs +0 -69
  471. package/lib/rules-builder.cjs +0 -241
  472. package/native/README.md +0 -117
  473. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  474. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  475. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  476. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  477. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  478. package/prompts/code-review.txt +0 -16
  479. package/prompts/security-audit.txt +0 -17
  480. package/rules/bridge/00-common.md +0 -39
  481. package/rules/bridge/20-skip-protocol.md +0 -18
  482. package/rules/bridge/30-explorer.md +0 -33
  483. package/rules/bridge/40-cycle1-agent.md +0 -52
  484. package/rules/bridge/41-cycle2-agent.md +0 -62
  485. package/rules/lead/00-tool-lead.md +0 -61
  486. package/rules/lead/01-general.md +0 -26
  487. package/rules/lead/02-channels.md +0 -49
  488. package/rules/lead/03-team.md +0 -27
  489. package/rules/lead/04-workflow.md +0 -20
  490. package/rules/shared/00-language.md +0 -14
  491. package/rules/shared/01-tool.md +0 -138
  492. package/scripts/bootstrap.mjs +0 -130
  493. package/scripts/bridge-unify-smoke.mjs +0 -308
  494. package/scripts/build-runtime-linux.sh +0 -348
  495. package/scripts/build-runtime-macos.sh +0 -217
  496. package/scripts/build-runtime-windows.ps1 +0 -242
  497. package/scripts/builtin-utils-smoke.mjs +0 -398
  498. package/scripts/bump.mjs +0 -80
  499. package/scripts/check-json.mjs +0 -45
  500. package/scripts/check-syntax-changed.mjs +0 -102
  501. package/scripts/check-syntax.mjs +0 -58
  502. package/scripts/code-graph-batch.test.mjs +0 -33
  503. package/scripts/config-preserve-smoke.mjs +0 -180
  504. package/scripts/doctor.mjs +0 -489
  505. package/scripts/edit-normalize-fuzz.mjs +0 -130
  506. package/scripts/edit-normalize-smoke.mjs +0 -401
  507. package/scripts/edit-operation-smoke.mjs +0 -369
  508. package/scripts/edit2-smoke.mjs +0 -63
  509. package/scripts/ensure-deps.mjs +0 -259
  510. package/scripts/fuzzy-e2e.mjs +0 -28
  511. package/scripts/fuzzy-smoke.mjs +0 -26
  512. package/scripts/gateway-model.mjs +0 -596
  513. package/scripts/generate-runtime-manifest.mjs +0 -166
  514. package/scripts/guard-smoke.mjs +0 -66
  515. package/scripts/hidden-role-schema-smoke.mjs +0 -162
  516. package/scripts/hook-routing-smoke.mjs +0 -29
  517. package/scripts/inject-input.ps1 +0 -204
  518. package/scripts/io-complex-smoke.mjs +0 -667
  519. package/scripts/io-explore-bench.mjs +0 -424
  520. package/scripts/io-guardrails-smoke.mjs +0 -205
  521. package/scripts/io-mini-bench-baseline.json +0 -11
  522. package/scripts/io-mini-bench.mjs +0 -216
  523. package/scripts/io-route-harness.mjs +0 -933
  524. package/scripts/io-telemetry-report.mjs +0 -691
  525. package/scripts/lib/gateway-inventory.mjs +0 -178
  526. package/scripts/lib/gateway-settings.mjs +0 -78
  527. package/scripts/mutation-bench.mjs +0 -564
  528. package/scripts/mutation-io-smoke.mjs +0 -1097
  529. package/scripts/native-patch-bridge-smoke.mjs +0 -288
  530. package/scripts/native-patch-smoke.mjs +0 -304
  531. package/scripts/openai-oauth-catalog-smoke.mjs +0 -53
  532. package/scripts/patch-interior-context-smoke.mjs +0 -49
  533. package/scripts/patch-newline-utf8-smoke.mjs +0 -157
  534. package/scripts/perf-hook-smoke.mjs +0 -71
  535. package/scripts/permission-eval-smoke.mjs +0 -443
  536. package/scripts/prep-patch.mjs +0 -53
  537. package/scripts/prep-shim.mjs +0 -96
  538. package/scripts/provider-cache-smoke.mjs +0 -687
  539. package/scripts/report-runtime-health.mjs +0 -132
  540. package/scripts/resolve-bun.mjs +0 -60
  541. package/scripts/run-mcp.mjs +0 -1473
  542. package/scripts/salvage-v4a-shatter.test.mjs +0 -58
  543. package/scripts/scoped-cache-io-smoke.mjs +0 -103
  544. package/scripts/shell-policy-round3-smoke.mjs +0 -46
  545. package/scripts/smoke-runtime-negative.ps1 +0 -100
  546. package/scripts/smoke-runtime-negative.sh +0 -95
  547. package/scripts/stall-policy-smoke.mjs +0 -50
  548. package/scripts/start-memory-worker.mjs +0 -23
  549. package/scripts/statusline-launcher-smoke.mjs +0 -235
  550. package/scripts/stress-atomic-write.mjs +0 -1028
  551. package/scripts/test-fault-inject.mjs +0 -164
  552. package/scripts/test-large-file.mjs +0 -174
  553. package/scripts/tool-edge-smoke.mjs +0 -209
  554. package/scripts/uninstall.mjs +0 -238
  555. package/scripts/webhook-selfheal-smoke.mjs +0 -27
  556. package/scripts/write-overwrite-guard-smoke.mjs +0 -56
  557. package/server-main.mjs +0 -3350
  558. package/server.mjs +0 -468
  559. package/setup/config-merge.mjs +0 -246
  560. package/setup/install.mjs +0 -574
  561. package/setup/launch-core.mjs +0 -617
  562. package/setup/launch.mjs +0 -101
  563. package/setup/locate-claude.mjs +0 -56
  564. package/setup/mixdog-cli.mjs +0 -122
  565. package/setup/setup-server.mjs +0 -3305
  566. package/setup/setup.html +0 -3740
  567. package/setup/tui.mjs +0 -325
  568. package/skills/retro-skill-proposer/SKILL.md +0 -92
  569. package/skills/schedule-add/SKILL.md +0 -77
  570. package/skills/setup/SKILL.md +0 -356
  571. package/skills/webhook-add/SKILL.md +0 -81
  572. package/src/agent/bridge-stall-watchdog.mjs +0 -337
  573. package/src/agent/index.mjs +0 -2229
  574. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +0 -1010
  575. package/src/agent/orchestrator/bridge-retry.mjs +0 -220
  576. package/src/agent/orchestrator/bridge-trace.mjs +0 -601
  577. package/src/agent/orchestrator/cache-mtime.mjs +0 -58
  578. package/src/agent/orchestrator/config.mjs +0 -405
  579. package/src/agent/orchestrator/context/collect.mjs +0 -651
  580. package/src/agent/orchestrator/dispatch-persist.mjs +0 -549
  581. package/src/agent/orchestrator/drain-registry.mjs +0 -50
  582. package/src/agent/orchestrator/explore-validator.mjs +0 -8
  583. package/src/agent/orchestrator/internal-roles.mjs +0 -118
  584. package/src/agent/orchestrator/internal-tools.mjs +0 -88
  585. package/src/agent/orchestrator/jobs.mjs +0 -116
  586. package/src/agent/orchestrator/mcp/client.mjs +0 -364
  587. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +0 -1884
  588. package/src/agent/orchestrator/providers/anthropic.mjs +0 -598
  589. package/src/agent/orchestrator/providers/gemini.mjs +0 -1530
  590. package/src/agent/orchestrator/providers/grok-oauth.mjs +0 -779
  591. package/src/agent/orchestrator/providers/model-catalog.mjs +0 -374
  592. package/src/agent/orchestrator/providers/openai-compat-stream.mjs +0 -366
  593. package/src/agent/orchestrator/providers/openai-compat.mjs +0 -1511
  594. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -1891
  595. package/src/agent/orchestrator/providers/openai-oauth.mjs +0 -1456
  596. package/src/agent/orchestrator/providers/openai-ws.mjs +0 -127
  597. package/src/agent/orchestrator/providers/registry.mjs +0 -192
  598. package/src/agent/orchestrator/providers/retry-classifier.mjs +0 -325
  599. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +0 -42
  600. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +0 -142
  601. package/src/agent/orchestrator/session/cache/read-cache.mjs +0 -319
  602. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +0 -361
  603. package/src/agent/orchestrator/session/loop.mjs +0 -1619
  604. package/src/agent/orchestrator/session/manager.mjs +0 -1991
  605. package/src/agent/orchestrator/session/result-classification.mjs +0 -65
  606. package/src/agent/orchestrator/session/store.mjs +0 -632
  607. package/src/agent/orchestrator/session/stream-watchdog.mjs +0 -130
  608. package/src/agent/orchestrator/session/tool-result-offload.mjs +0 -166
  609. package/src/agent/orchestrator/session/trim.mjs +0 -491
  610. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +0 -115
  611. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +0 -331
  612. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +0 -150
  613. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +0 -228
  614. package/src/agent/orchestrator/smart-bridge/index.mjs +0 -215
  615. package/src/agent/orchestrator/smart-bridge/profiles.mjs +0 -37
  616. package/src/agent/orchestrator/smart-bridge/registry.mjs +0 -348
  617. package/src/agent/orchestrator/stall-policy.mjs +0 -201
  618. package/src/agent/orchestrator/tool-loop-guard.mjs +0 -75
  619. package/src/agent/orchestrator/tools/bash-session.mjs +0 -722
  620. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +0 -171
  621. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +0 -511
  622. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -480
  623. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +0 -256
  624. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +0 -58
  625. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +0 -240
  626. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +0 -113
  627. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +0 -74
  628. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +0 -242
  629. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +0 -211
  630. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +0 -1364
  631. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +0 -126
  632. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +0 -141
  633. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +0 -194
  634. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +0 -60
  635. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +0 -168
  636. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +0 -173
  637. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +0 -48
  638. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -99
  639. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +0 -193
  640. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +0 -597
  641. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  642. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +0 -300
  643. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +0 -152
  644. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +0 -118
  645. package/src/agent/orchestrator/tools/builtin/read-open.mjs +0 -190
  646. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +0 -728
  647. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +0 -173
  648. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +0 -602
  649. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +0 -196
  650. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +0 -422
  651. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +0 -158
  652. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +0 -869
  653. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +0 -962
  654. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +0 -223
  655. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +0 -206
  656. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +0 -401
  657. package/src/agent/orchestrator/tools/builtin.mjs +0 -503
  658. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +0 -24
  659. package/src/agent/orchestrator/tools/code-graph.mjs +0 -4095
  660. package/src/agent/orchestrator/tools/cwd-tool.mjs +0 -298
  661. package/src/agent/orchestrator/tools/destructive-warning.mjs +0 -323
  662. package/src/agent/orchestrator/tools/edit-normalize.mjs +0 -603
  663. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +0 -154
  664. package/src/agent/orchestrator/tools/graph-manifest.json +0 -26
  665. package/src/agent/orchestrator/tools/host-input.mjs +0 -204
  666. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +0 -143
  667. package/src/agent/orchestrator/tools/patch-manifest.json +0 -26
  668. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +0 -20
  669. package/src/agent/orchestrator/tools/patch.mjs +0 -2754
  670. package/src/agent/orchestrator/tools/progress-message.mjs +0 -118
  671. package/src/agent/orchestrator/tools/shell-command.mjs +0 -865
  672. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +0 -7
  673. package/src/agent/orchestrator/tools/shell-snapshot.mjs +0 -313
  674. package/src/agent/orchestrator/workflow-store.mjs +0 -93
  675. package/src/agent/tool-defs.mjs +0 -110
  676. package/src/channels/backends/discord.mjs +0 -784
  677. package/src/channels/data/voice-runtime-manifest.json +0 -138
  678. package/src/channels/index.mjs +0 -3268
  679. package/src/channels/lib/config.mjs +0 -292
  680. package/src/channels/lib/drop-trace.mjs +0 -71
  681. package/src/channels/lib/event-pipeline.mjs +0 -81
  682. package/src/channels/lib/holidays.mjs +0 -138
  683. package/src/channels/lib/hook-pipe-server.mjs +0 -822
  684. package/src/channels/lib/output-forwarder.mjs +0 -765
  685. package/src/channels/lib/runtime-paths.mjs +0 -552
  686. package/src/channels/lib/scheduler.mjs +0 -723
  687. package/src/channels/lib/session-discovery.mjs +0 -103
  688. package/src/channels/lib/state-file.mjs +0 -68
  689. package/src/channels/lib/status-snapshot.mjs +0 -219
  690. package/src/channels/lib/tool-format.mjs +0 -140
  691. package/src/channels/lib/transcript-discovery.mjs +0 -195
  692. package/src/channels/lib/voice-runtime-fetcher.mjs +0 -734
  693. package/src/channels/lib/webhook.mjs +0 -1318
  694. package/src/channels/tool-defs.mjs +0 -170
  695. package/src/daemon/host.mjs +0 -118
  696. package/src/daemon/mcp-transport.mjs +0 -47
  697. package/src/daemon/session.mjs +0 -100
  698. package/src/daemon/thin-client.mjs +0 -71
  699. package/src/daemon/transport.mjs +0 -163
  700. package/src/gateway/claude-current.mjs +0 -255
  701. package/src/gateway/oauth-usage.mjs +0 -598
  702. package/src/gateway/route-meta.mjs +0 -629
  703. package/src/gateway/server.mjs +0 -713
  704. package/src/memory/data/runtime-manifest.json +0 -40
  705. package/src/memory/index.mjs +0 -3332
  706. package/src/memory/lib/core-memory-store.mjs +0 -330
  707. package/src/memory/lib/embedding-provider.mjs +0 -269
  708. package/src/memory/lib/embedding-worker.mjs +0 -323
  709. package/src/memory/lib/memory-cycle1.mjs +0 -645
  710. package/src/memory/lib/memory-cycle2.mjs +0 -1284
  711. package/src/memory/lib/memory-cycle3.mjs +0 -540
  712. package/src/memory/lib/memory-embed.mjs +0 -299
  713. package/src/memory/lib/memory-ops-policy.mjs +0 -190
  714. package/src/memory/lib/memory-recall-store.mjs +0 -638
  715. package/src/memory/lib/memory.mjs +0 -412
  716. package/src/memory/lib/pg/adapter.mjs +0 -308
  717. package/src/memory/lib/pg/process.mjs +0 -360
  718. package/src/memory/lib/pg/supervisor.mjs +0 -396
  719. package/src/memory/lib/runtime-fetcher.mjs +0 -458
  720. package/src/memory/lib/trace-store.mjs +0 -728
  721. package/src/memory/tool-defs.mjs +0 -79
  722. package/src/search/index.mjs +0 -1173
  723. package/src/search/lib/backends/anthropic-oauth.mjs +0 -98
  724. package/src/search/lib/backends/exa.mjs +0 -50
  725. package/src/search/lib/backends/firecrawl.mjs +0 -61
  726. package/src/search/lib/backends/gemini-api.mjs +0 -83
  727. package/src/search/lib/backends/grok-oauth.mjs +0 -86
  728. package/src/search/lib/backends/index.mjs +0 -150
  729. package/src/search/lib/backends/openai-api.mjs +0 -144
  730. package/src/search/lib/backends/openai-oauth.mjs +0 -102
  731. package/src/search/lib/backends/openai-web-search.mjs +0 -76
  732. package/src/search/lib/backends/tavily.mjs +0 -55
  733. package/src/search/lib/backends/xai-api.mjs +0 -113
  734. package/src/search/lib/config.mjs +0 -192
  735. package/src/search/lib/provider-usage.mjs +0 -67
  736. package/src/search/lib/providers.mjs +0 -47
  737. package/src/search/lib/search-intent.mjs +0 -109
  738. package/src/search/lib/setup-handler.mjs +0 -261
  739. package/src/search/lib/web-tools.mjs +0 -1219
  740. package/src/search/tool-defs.mjs +0 -83
  741. package/src/setup/defender-exclusion.mjs +0 -183
  742. package/src/shared/atomic-file.mjs +0 -436
  743. package/src/shared/config.mjs +0 -372
  744. package/src/shared/daemon-recycle.mjs +0 -108
  745. package/src/shared/disable-claude-builtins.mjs +0 -91
  746. package/src/shared/err-text.mjs +0 -12
  747. package/src/shared/llm/http-agent.mjs +0 -123
  748. package/src/shared/open-url.mjs +0 -62
  749. package/src/shared/plugin-paths.mjs +0 -58
  750. package/src/shared/schedules-store.mjs +0 -70
  751. package/src/shared/seed.mjs +0 -161
  752. package/src/shared/user-cwd.mjs +0 -225
  753. package/src/shared/user-data-guard.mjs +0 -244
  754. package/src/status/aggregator.mjs +0 -584
  755. package/src/status/server.mjs +0 -413
  756. package/tools.json +0 -1671
  757. /package/{defaults → src/defaults}/cycle3-review-prompt.md +0 -0
  758. /package/{defaults → src/defaults}/memory-promote-prompt.md +0 -0
  759. /package/{hooks → src/hooks}/lib/permission-rules.cjs +0 -0
  760. /package/{lib → src/lib}/config-cjs.cjs +0 -0
  761. /package/{lib → src/lib}/hook-pipe-path.cjs +0 -0
  762. /package/{lib → src/lib}/mixdog-debug.cjs +0 -0
  763. /package/{lib → src/lib}/text-utils.cjs +0 -0
  764. /package/{rules → src/rules}/bridge/42-cycle3-agent.md +0 -0
  765. /package/src/{agent → runtime/agent}/orchestrator/activity-bus.mjs +0 -0
  766. /package/src/{agent → runtime/agent}/orchestrator/providers/anthropic-betas.mjs +0 -0
  767. /package/src/{agent → runtime/agent}/orchestrator/session/abort-lookup.mjs +0 -0
  768. /package/src/{agent → runtime/agent}/orchestrator/session/cache/scoped-cache-outcome.mjs +0 -0
  769. /package/src/{agent → runtime/agent}/orchestrator/session/cache/util.mjs +0 -0
  770. /package/src/{agent → runtime/agent}/orchestrator/session/read-dedup.mjs +0 -0
  771. /package/src/{agent → runtime/agent}/orchestrator/session/save-session-worker.mjs +0 -0
  772. /package/src/{agent → runtime/agent}/orchestrator/smart-bridge/session-builder.mjs +0 -0
  773. /package/src/{agent → runtime/agent}/orchestrator/tools/bash-policy-scan.mjs +0 -0
  774. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/atomic-write.mjs +0 -0
  775. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/binary-file.mjs +0 -0
  776. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cache-layers.mjs +0 -0
  777. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cwd-utils.mjs +0 -0
  778. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/device-paths.mjs +0 -0
  779. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -0
  780. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diff-utils.mjs +0 -0
  781. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/fs-reachability.mjs +0 -0
  782. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/grep-formatting.mjs +0 -0
  783. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/hash-utils.mjs +0 -0
  784. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/list-formatting.mjs +0 -0
  785. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/open-config-tool.mjs +0 -0
  786. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-locks.mjs +0 -0
  787. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-utils.mjs +0 -0
  788. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-args.mjs +0 -0
  789. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-batch.mjs +0 -0
  790. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-constants.mjs +0 -0
  791. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image-resize.mjs +0 -0
  792. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image.mjs +0 -0
  793. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-lines.mjs +0 -0
  794. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-mode-tool.mjs +0 -0
  795. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-range-index.mjs +0 -0
  796. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-ranges.mjs +0 -0
  797. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-special-files.mjs +0 -0
  798. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-tool.mjs +0 -0
  799. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-windows.mjs +0 -0
  800. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-analysis.mjs +0 -0
  801. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-output.mjs +0 -0
  802. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-helpers.mjs +0 -0
  803. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-validation.mjs +0 -0
  804. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/text-stats.mjs +0 -0
  805. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/windows-roots.mjs +0 -0
  806. /package/src/{agent → runtime/agent}/orchestrator/tools/code-graph-prewarm-worker.mjs +0 -0
  807. /package/src/{agent → runtime/agent}/orchestrator/tools/env-scrub.mjs +0 -0
  808. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-content-cache.mjs +0 -0
  809. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-planner.mjs +0 -0
  810. /package/src/{agent → runtime/agent}/orchestrator/tools/next-call-utils.mjs +0 -0
  811. /package/src/{agent → runtime/agent}/orchestrator/tools/result-compression.mjs +0 -0
  812. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-exec-policy.mjs +0 -0
  813. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy-danger-target.mjs +0 -0
  814. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy.mjs +0 -0
  815. /package/src/{channels → runtime/channels}/lib/cli-worker-host.mjs +0 -0
  816. /package/src/{channels → runtime/channels}/lib/config-lock.mjs +0 -0
  817. /package/src/{channels → runtime/channels}/lib/event-queue.mjs +0 -0
  818. /package/src/{channels → runtime/channels}/lib/executor.mjs +0 -0
  819. /package/src/{channels → runtime/channels}/lib/format.mjs +0 -0
  820. /package/src/{channels → runtime/channels}/lib/interaction-workflows.mjs +0 -0
  821. /package/src/{channels → runtime/channels}/lib/memory-client.mjs +0 -0
  822. /package/src/{channels → runtime/channels}/lib/session-control.mjs +0 -0
  823. /package/src/{channels → runtime/channels}/lib/settings.mjs +0 -0
  824. /package/src/{channels → runtime/channels}/lib/whisper-server.mjs +0 -0
  825. /package/src/{memory → runtime/memory}/lib/agent-ipc.mjs +0 -0
  826. /package/src/{memory → runtime/memory}/lib/bridge-trace-queries.mjs +0 -0
  827. /package/src/{memory → runtime/memory}/lib/llm-worker-host.mjs +0 -0
  828. /package/src/{memory → runtime/memory}/lib/memory-cycle.mjs +0 -0
  829. /package/src/{memory → runtime/memory}/lib/memory-extraction.mjs +0 -0
  830. /package/src/{memory → runtime/memory}/lib/memory-maintenance-store.mjs +0 -0
  831. /package/src/{memory → runtime/memory}/lib/memory-recall-id-patch.mjs +0 -0
  832. /package/src/{memory → runtime/memory}/lib/memory-recall-read-query.mjs +0 -0
  833. /package/src/{memory → runtime/memory}/lib/memory-recall-scope-filter.mjs +0 -0
  834. /package/src/{memory → runtime/memory}/lib/memory-retrievers.mjs +0 -0
  835. /package/src/{memory → runtime/memory}/lib/memory-score.mjs +0 -0
  836. /package/src/{memory → runtime/memory}/lib/memory-text-utils.mjs +0 -0
  837. /package/src/{memory → runtime/memory}/lib/model-profile.mjs +0 -0
  838. /package/src/{memory → runtime/memory}/lib/project-id-resolver.mjs +0 -0
  839. /package/src/{search → runtime/search}/lib/cache.mjs +0 -0
  840. /package/src/{search → runtime/search}/lib/formatter.mjs +0 -0
  841. /package/src/{search → runtime/search}/lib/state.mjs +0 -0
  842. /package/src/{shared → runtime/shared}/abort-controller.mjs +0 -0
  843. /package/src/{shared → runtime/shared}/llm/cost.mjs +0 -0
  844. /package/src/{shared → runtime/shared}/llm/index.mjs +0 -0
  845. /package/src/{shared → runtime/shared}/llm/pid-cleanup.mjs +0 -0
  846. /package/src/{shared → runtime/shared}/llm/usage-log.mjs +0 -0
  847. /package/src/{shared → runtime/shared}/wsl.mjs +0 -0
@@ -0,0 +1,3813 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { basename, dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { performance } from 'node:perf_hooks';
6
+ import { ensureProjectMixdogMd, ensureStandaloneEnvironment } from './standalone/seeds.mjs';
7
+ import { createStandaloneBridge } from './standalone/bridge-tool.mjs';
8
+ import { EXPLORE_TOOL, runExplore } from './standalone/explore-tool.mjs';
9
+ import { createStandaloneChannelWorker } from './standalone/channel-worker.mjs';
10
+ import { createStandaloneHookBus } from './standalone/hook-bus.mjs';
11
+ import { writeLastSessionCwd } from './runtime/shared/user-cwd.mjs';
12
+ import { cancelBackgroundTasks } from './runtime/shared/background-tasks.mjs';
13
+ import { createWorkspaceRouter, formatWorkspaceSessionContext } from './runtime/shared/workspace-router.mjs';
14
+ import { setConfiguredShell } from './runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs';
15
+ import {
16
+ PROVIDER_STATUS_TOOL,
17
+ beginOAuthProviderLogin,
18
+ forgetProviderAuth,
19
+ loginOAuthProvider,
20
+ providerSetup,
21
+ renderProviderStatus,
22
+ saveOpenAIUsageSessionKey,
23
+ saveOpenCodeGoUsageAuth,
24
+ saveProviderApiKey,
25
+ setLocalProvider,
26
+ } from './standalone/provider-admin.mjs';
27
+ import { createUsageDashboard } from './standalone/usage-dashboard.mjs';
28
+ import {
29
+ channelSetup,
30
+ deleteChannel,
31
+ deleteSchedule,
32
+ deleteWebhook,
33
+ forgetDiscordToken,
34
+ forgetWebhookAuthtoken,
35
+ renderChannelStatus,
36
+ saveChannel,
37
+ saveDiscordToken,
38
+ saveSchedule,
39
+ saveWebhook,
40
+ saveWebhookAuthtoken,
41
+ setScheduleEnabled,
42
+ setWebhookEnabled,
43
+ setWebhookConfig,
44
+ } from './standalone/channel-admin.mjs';
45
+ import {
46
+ addPlugin as registryAddPlugin,
47
+ listRegisteredPlugins,
48
+ pluginAdminStatus,
49
+ removePlugin as registryRemovePlugin,
50
+ updatePlugin as registryUpdatePlugin,
51
+ } from './standalone/plugin-admin.mjs';
52
+ import {
53
+ estimateMessagesTokens,
54
+ estimateRequestReserveTokens,
55
+ estimateToolSchemaTokens,
56
+ } from './runtime/agent/orchestrator/session/context-utils.mjs';
57
+
58
+ function sessionMessageText(content) {
59
+ if (content == null) return '';
60
+ if (typeof content === 'string') return content;
61
+ const parts = Array.isArray(content)
62
+ ? content
63
+ : (content && typeof content === 'object' && Array.isArray(content.content) ? content.content : null);
64
+ if (parts) {
65
+ return parts.map((part) => {
66
+ if (typeof part === 'string') return part;
67
+ return part?.text ?? '';
68
+ }).filter(Boolean).join('\n');
69
+ }
70
+ if (typeof content === 'object' && typeof content.text === 'string') return content.text;
71
+ try { return JSON.stringify(content); } catch { return String(content); }
72
+ }
73
+
74
+ function roughTokenCount(text) {
75
+ return Math.ceil(String(text ?? '').length / 4);
76
+ }
77
+
78
+ function messageContextText(message) {
79
+ if (!message || typeof message !== 'object') return '';
80
+ let text = sessionMessageText(message.content);
81
+ if (message.role === 'assistant' && Array.isArray(message.toolCalls) && message.toolCalls.length) {
82
+ try { text += `\n${JSON.stringify(message.toolCalls)}`; }
83
+ catch { text += `\n[${message.toolCalls.length} tool calls]`; }
84
+ }
85
+ if (message.role === 'tool' && message.toolCallId) text += `\n${message.toolCallId}`;
86
+ return text;
87
+ }
88
+
89
+ function stripSystemReminder(text) {
90
+ return String(text || '')
91
+ .replace(/^\s*<system-reminder>\s*/i, '')
92
+ .replace(/\s*<\/system-reminder>\s*$/i, '')
93
+ .trim();
94
+ }
95
+
96
+ function splitMarkdownSections(text) {
97
+ const sections = [];
98
+ let current = [];
99
+ for (const line of String(text || '').split(/\r?\n/)) {
100
+ if (/^#\s+/.test(line) && current.length) {
101
+ const body = current.join('\n').trim();
102
+ if (body) sections.push(body);
103
+ current = [line];
104
+ } else {
105
+ current.push(line);
106
+ }
107
+ }
108
+ const tail = current.join('\n').trim();
109
+ if (tail) sections.push(tail);
110
+ return sections;
111
+ }
112
+
113
+ function reminderSectionBucket(section) {
114
+ const heading = String(section.match(/^#\s+([^\n]+)/)?.[1] || '').trim().toLowerCase();
115
+ if (heading.includes('core memory')) return 'memory';
116
+ if (heading.includes('mixdog-project-context') || heading.includes('project-context')) return 'project';
117
+ if (heading.includes('active workflow') || heading.includes('available agents') || heading.includes('workflow')) return 'workflow';
118
+ if (heading.includes('workspace') || heading === 'role' || heading.includes('role-identity') || heading.includes('task-brief')) return 'workspace';
119
+ if (heading.includes('environment')) return 'environment';
120
+ return 'other';
121
+ }
122
+
123
+ function summarizeContextMessages(messages) {
124
+ const rows = {
125
+ system: { count: 0, tokens: 0 },
126
+ user: { count: 0, tokens: 0 },
127
+ assistant: { count: 0, tokens: 0 },
128
+ tool: { count: 0, tokens: 0 },
129
+ other: { count: 0, tokens: 0 },
130
+ };
131
+ const semantic = {
132
+ system: { count: 0, tokens: 0 },
133
+ chat: { count: 0, tokens: 0 },
134
+ assistant: { count: 0, tokens: 0 },
135
+ toolResults: { count: 0, tokens: 0 },
136
+ reminders: { count: 0, tokens: 0, otherTokens: 0 },
137
+ project: { tokens: 0 },
138
+ workflow: { tokens: 0 },
139
+ memory: { tokens: 0 },
140
+ workspace: { tokens: 0 },
141
+ environment: { tokens: 0 },
142
+ other: { tokens: 0 },
143
+ };
144
+ let toolCallCount = 0;
145
+ let toolCallTokens = 0;
146
+ let toolResultCount = 0;
147
+ let toolResultTokens = 0;
148
+ for (const message of messages || []) {
149
+ const role = rows[message?.role] ? message.role : 'other';
150
+ const text = messageContextText(message);
151
+ const tokens = roughTokenCount(text) + 4;
152
+ rows[role].count += 1;
153
+ rows[role].tokens += tokens;
154
+ if (role === 'system') {
155
+ semantic.system.count += 1;
156
+ semantic.system.tokens += tokens;
157
+ } else if (role === 'user') {
158
+ if (String(text || '').trim().startsWith('<system-reminder>')) {
159
+ semantic.reminders.count += 1;
160
+ semantic.reminders.tokens += tokens;
161
+ let sectionTokens = 0;
162
+ for (const section of splitMarkdownSections(stripSystemReminder(text))) {
163
+ const bucket = reminderSectionBucket(section);
164
+ const sectionTokenCount = roughTokenCount(section);
165
+ semantic[bucket].tokens += sectionTokenCount;
166
+ sectionTokens += sectionTokenCount;
167
+ }
168
+ semantic.reminders.otherTokens += Math.max(0, tokens - sectionTokens);
169
+ } else {
170
+ semantic.chat.count += 1;
171
+ semantic.chat.tokens += tokens;
172
+ }
173
+ } else if (role === 'assistant') {
174
+ semantic.assistant.count += 1;
175
+ semantic.assistant.tokens += tokens;
176
+ } else if (role === 'tool') {
177
+ semantic.toolResults.count += 1;
178
+ semantic.toolResults.tokens += tokens;
179
+ }
180
+ if (message?.role === 'assistant' && Array.isArray(message.toolCalls) && message.toolCalls.length) {
181
+ toolCallCount += message.toolCalls.length;
182
+ try { toolCallTokens += roughTokenCount(JSON.stringify(message.toolCalls)); }
183
+ catch { toolCallTokens += roughTokenCount(`[${message.toolCalls.length} tool calls]`); }
184
+ }
185
+ if (message?.role === 'tool') {
186
+ toolResultCount += 1;
187
+ toolResultTokens += tokens;
188
+ }
189
+ }
190
+ return {
191
+ count: Array.isArray(messages) ? messages.length : 0,
192
+ estimatedTokens: Array.isArray(messages) ? estimateMessagesTokens(messages) : 0,
193
+ roles: rows,
194
+ semantic,
195
+ toolCallCount,
196
+ toolCallTokens,
197
+ toolResultCount,
198
+ toolResultTokens,
199
+ };
200
+ }
201
+
202
+ function isSessionPreviewNoise(text) {
203
+ const value = String(text || '').trim();
204
+ return !value
205
+ || value.startsWith('<system-reminder>')
206
+ || value.startsWith('</system-reminder>')
207
+ || /^#\s*permission\b/i.test(value)
208
+ || /^permission:\s*/i.test(value)
209
+ || /^cwd:\s*/i.test(value);
210
+ }
211
+
212
+ function cleanSessionPreview(text) {
213
+ return String(text || '')
214
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, ' ')
215
+ .replace(/\s+/g, ' ')
216
+ .trim()
217
+ .slice(0, 160);
218
+ }
219
+
220
+ const RUNTIME = './runtime/agent/orchestrator';
221
+ const SEARCH_RUNTIME = './runtime/search/index.mjs';
222
+ const SEARCH_TOOL_DEFS = './runtime/search/tool-defs.mjs';
223
+ const MEMORY_TOOL_DEFS = './runtime/memory/tool-defs.mjs';
224
+ const MEMORY_RUNTIME = './runtime/memory/index.mjs';
225
+ const CHANNEL_TOOL_DEFS = './runtime/channels/tool-defs.mjs';
226
+ const CHANNEL_WORKER_ENTRY = './runtime/channels/index.mjs';
227
+ const CODE_GRAPH_TOOL_DEFS = './runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs';
228
+ const CODE_GRAPH_RUNTIME = './runtime/agent/orchestrator/tools/code-graph.mjs';
229
+ const STATUSLINE_SESSION_ROUTES = './vendor/statusline/src/gateway/session-routes.mjs';
230
+ const __dirname = dirname(fileURLToPath(import.meta.url));
231
+ const STANDALONE_SOURCE_ROOT = __dirname;
232
+ // Resource root stays at src/ because defaults/, rules/, runtime/, vendor/ live
233
+ // there. User-owned standalone state lives under MIXDOG_HOME (~/.mixdog).
234
+ const STANDALONE_ROOT = STANDALONE_SOURCE_ROOT;
235
+ const MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
236
+ const STANDALONE_DATA_DIR = process.env.MIXDOG_DATA_DIR || join(MIXDOG_HOME, 'data');
237
+
238
+ const DEFAULT_PROVIDER = 'anthropic-oauth';
239
+ const DEFAULT_MODEL = '';
240
+ const TOOL_MODES = new Set(['full', 'readonly', 'lead']);
241
+ const ALL_EFFORT_LEVELS = new Set(['none', 'low', 'medium', 'high', 'xhigh', 'max']);
242
+ const EFFORT_LABELS = {
243
+ none: 'None',
244
+ low: 'Low',
245
+ medium: 'Medium',
246
+ high: 'High',
247
+ xhigh: 'Extra High',
248
+ max: 'Max',
249
+ };
250
+
251
+ function envFlag(name) {
252
+ return /^(1|true|yes|on)$/i.test(String(process.env[name] || ''));
253
+ }
254
+
255
+ function envDelayMs(name, fallback, { min = 0, max = 60_000 } = {}) {
256
+ const raw = process.env[name];
257
+ if (raw === undefined || raw === '') return fallback;
258
+ const n = Number(raw);
259
+ if (!Number.isFinite(n)) return fallback;
260
+ return Math.min(max, Math.max(min, Math.floor(n)));
261
+ }
262
+
263
+ const BOOT_PROFILE_ENABLED = envFlag('MIXDOG_BOOT_PROFILE');
264
+ const BOOT_PROFILE_START = globalThis.__mixdogBootProfileStart || (globalThis.__mixdogBootProfileStart = performance.now());
265
+
266
+ function bootProfile(event, fields = {}) {
267
+ if (!BOOT_PROFILE_ENABLED) return;
268
+ const elapsedMs = performance.now() - BOOT_PROFILE_START;
269
+ const parts = [`[mixdog-boot] +${elapsedMs.toFixed(1)}ms`, event];
270
+ for (const [key, value] of Object.entries(fields || {})) {
271
+ if (value === undefined || value === null || value === '') continue;
272
+ parts.push(`${key}=${String(value).replace(/\s+/g, '_')}`);
273
+ }
274
+ try { process.stderr.write(`${parts.join(' ')}\n`); } catch {}
275
+ }
276
+
277
+ async function profiledImport(label, spec, { optional = false } = {}) {
278
+ const startedAt = performance.now();
279
+ try {
280
+ const mod = await import(spec);
281
+ bootProfile(`import:${label}`, { ms: (performance.now() - startedAt).toFixed(1) });
282
+ return mod;
283
+ } catch (error) {
284
+ bootProfile(`import:${label}:failed`, {
285
+ ms: (performance.now() - startedAt).toFixed(1),
286
+ error: error?.message || String(error),
287
+ });
288
+ if (optional) return null;
289
+ throw error;
290
+ }
291
+ }
292
+ const EFFORT_OPTIONS_BY_PROVIDER = {
293
+ openai: ['none', 'low', 'medium', 'high', 'xhigh'],
294
+ 'openai-oauth': ['none', 'low', 'medium', 'high', 'xhigh'],
295
+ anthropic: ['low', 'medium', 'high', 'max'],
296
+ 'anthropic-oauth': ['low', 'medium', 'high', 'xhigh', 'max'],
297
+ xai: ['none', 'low', 'medium', 'high'],
298
+ 'grok-oauth': ['none', 'low', 'medium', 'high'],
299
+ 'opencode-go': ['high', 'max'],
300
+ };
301
+ const EFFORT_BY_FAMILY = {
302
+ opus: ['low', 'medium', 'high', 'xhigh', 'max'],
303
+ sonnet: ['low', 'medium', 'high'],
304
+ haiku: [],
305
+ 'gpt-5.5': ['none', 'low', 'medium', 'high', 'xhigh'],
306
+ 'gpt-5.4': ['none', 'low', 'medium', 'high', 'xhigh'],
307
+ 'gpt-5.2': ['none', 'low', 'medium', 'high', 'xhigh'],
308
+ 'gpt-5': ['none', 'low', 'medium', 'high', 'xhigh'],
309
+ 'gpt-mini': ['none', 'low', 'medium', 'high', 'xhigh'],
310
+ 'gpt-nano': ['none', 'low', 'medium', 'high'],
311
+ 'gpt-codex': ['none', 'low', 'medium', 'high'],
312
+ grok: ['none', 'low', 'medium', 'high'],
313
+ };
314
+ const EFFORT_FALLBACKS = {
315
+ max: ['max', 'xhigh', 'high', 'medium', 'low'],
316
+ xhigh: ['xhigh', 'high', 'medium', 'low'],
317
+ high: ['high', 'medium', 'low'],
318
+ medium: ['medium', 'low'],
319
+ low: ['low'],
320
+ none: ['none'],
321
+ };
322
+
323
+ export const TOOL_SEARCH_TOOL = {
324
+ name: 'tool_search',
325
+ title: 'Tool Search',
326
+ annotations: {
327
+ title: 'Tool Search',
328
+ readOnlyHint: true,
329
+ destructiveHint: false,
330
+ idempotentHint: true,
331
+ openWorldHint: false,
332
+ bridgeHidden: true,
333
+ },
334
+ description: 'Search the current standalone tool surface and select deferred tools/skills for the task. Use before unfamiliar or currently inactive tools.',
335
+ inputSchema: {
336
+ type: 'object',
337
+ properties: {
338
+ query: { type: 'string', description: 'Optional search text, e.g. shell, bridge, memory, skill, mcp.' },
339
+ select: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Tool/skill names to activate, as comma-separated text or an array.' },
340
+ limit: { type: 'number', description: 'Maximum matches to return.' },
341
+ },
342
+ additionalProperties: false,
343
+ },
344
+ };
345
+
346
+ const CHANNEL_STATUS_TOOL = {
347
+ name: 'channel_status',
348
+ title: 'Channel Status',
349
+ annotations: {
350
+ title: 'Channel Status',
351
+ readOnlyHint: true,
352
+ destructiveHint: false,
353
+ idempotentHint: true,
354
+ openWorldHint: false,
355
+ bridgeHidden: true,
356
+ },
357
+ description: 'List standalone Discord/channel/schedule/webhook configuration status. Read-only and never returns secrets.',
358
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
359
+ };
360
+
361
+ const CWD_TOOL = {
362
+ name: 'cwd',
363
+ title: 'Current Working Directory',
364
+ annotations: {
365
+ title: 'Current Working Directory',
366
+ readOnlyHint: false,
367
+ destructiveHint: false,
368
+ idempotentHint: false,
369
+ openWorldHint: false,
370
+ bridgeHidden: true,
371
+ },
372
+ description: 'Show or set the standalone session working directory. Default get; action=set requires path.',
373
+ inputSchema: {
374
+ type: 'object',
375
+ properties: {
376
+ action: { type: 'string', enum: ['get', 'set'], description: 'Default get, or set when path is provided.' },
377
+ path: { type: 'string', description: 'Directory path for action=set. Relative paths resolve from the current cwd.' },
378
+ },
379
+ additionalProperties: false,
380
+ },
381
+ };
382
+
383
+ const MEASURED_TOOL_USAGE = Object.freeze({
384
+ read: 710,
385
+ code_graph: 520,
386
+ grep: 500,
387
+ glob: 460,
388
+ list: 430,
389
+ apply_patch: 400,
390
+ explore: 360,
391
+ bridge: 330,
392
+ shell: 81,
393
+ cwd: 2,
394
+ diagnostics: 2,
395
+ recall: 2,
396
+ search: 2,
397
+ web_fetch: 2,
398
+ provider_status: 2,
399
+ channel_status: 2,
400
+ });
401
+ const MEASURED_TOOL_ORDER = Object.freeze(Object.keys(MEASURED_TOOL_USAGE));
402
+ const DEFERRED_ALWAYS_ACTIVE_TOOLS = new Set([
403
+ 'tool_search',
404
+ 'recall',
405
+ 'search',
406
+ 'web_fetch',
407
+ ]);
408
+ const LEAD_DISALLOWED_TOOLS = Object.freeze(['diagnostics', 'open_config']);
409
+ const DEFERRED_DEFAULT_FULL_LIMIT = 9;
410
+ const DEFERRED_DEFAULT_READONLY_TOOLS = Object.freeze([
411
+ 'read',
412
+ 'code_graph',
413
+ 'grep',
414
+ 'glob',
415
+ 'list',
416
+ 'explore',
417
+ 'tool_search',
418
+ ]);
419
+ const DEFERRED_DEFAULT_LEAD_TOOLS = Object.freeze([
420
+ 'read',
421
+ 'code_graph',
422
+ 'grep',
423
+ 'glob',
424
+ 'list',
425
+ 'shell',
426
+ 'task',
427
+ 'explore',
428
+ 'apply_patch',
429
+ 'bridge',
430
+ 'recall',
431
+ 'search',
432
+ 'web_fetch',
433
+ 'cwd',
434
+ 'tool_search',
435
+ ]);
436
+ const READONLY_TOOL_NAMES = new Set([
437
+ 'read',
438
+ 'list',
439
+ 'grep',
440
+ 'glob',
441
+ 'code_graph',
442
+ 'search',
443
+ 'web_fetch',
444
+ 'recall',
445
+ 'memory',
446
+ 'provider_status',
447
+ 'channel_status',
448
+ 'schedule_status',
449
+ 'fetch',
450
+ ]);
451
+ const BRIDGE_HIDDEN_WRAPPER_TOOLS = new Set(['explore', 'search']);
452
+
453
+ function applyStandaloneToolDefaults(tool) {
454
+ if (!tool || !BRIDGE_HIDDEN_WRAPPER_TOOLS.has(tool.name)) return tool;
455
+ return {
456
+ ...tool,
457
+ annotations: {
458
+ ...(tool.annotations || {}),
459
+ bridgeHidden: true,
460
+ },
461
+ };
462
+ }
463
+ const DEFERRED_SELECT_ALIASES = {
464
+ filesystem: ['read', 'list', 'grep', 'glob'],
465
+ search: ['search', 'web_fetch'],
466
+ web: ['web_fetch', 'search'],
467
+ memory: ['memory', 'recall'],
468
+ channels: ['reply', 'fetch', 'react', 'edit_message', 'download_attachment', 'schedule_status', 'trigger_schedule', 'schedule_control', 'reload_config'],
469
+ discord: ['reply', 'fetch', 'react', 'edit_message', 'download_attachment'],
470
+ providers: ['provider_status'],
471
+ provider: ['provider_status'],
472
+ status: ['provider_status', 'channel_status', 'schedule_status'],
473
+ schedule: ['schedule_status', 'trigger_schedule', 'schedule_control'],
474
+ channel: ['channel_status'],
475
+ explore: ['explore'],
476
+ discovery: ['explore'],
477
+ bridge: ['bridge'],
478
+ graph: ['code_graph'],
479
+ code: ['code_graph'],
480
+ shell: ['shell', 'task'],
481
+ };
482
+
483
+ function normalizeToolMode(mode) {
484
+ const value = String(mode || '').trim().toLowerCase();
485
+ return TOOL_MODES.has(value) ? value : 'full';
486
+ }
487
+
488
+ function normalizeEffortInput(value) {
489
+ const v = clean(value).toLowerCase();
490
+ if (!v || v === 'auto') return null;
491
+ if (!ALL_EFFORT_LEVELS.has(v)) {
492
+ throw new Error(`effort must be one of auto, ${[...ALL_EFFORT_LEVELS].join(', ')}`);
493
+ }
494
+ return v;
495
+ }
496
+
497
+ function effortOptionsFor(provider, model) {
498
+ const providerAllowed = EFFORT_OPTIONS_BY_PROVIDER[provider] || null;
499
+ const filterProvider = (values) => {
500
+ const unique = [...new Set((values || []).map(clean).filter(Boolean))];
501
+ return providerAllowed ? unique.filter((v) => providerAllowed.includes(v)) : unique;
502
+ };
503
+ const declared = Array.isArray(model?.reasoningLevels)
504
+ ? model.reasoningLevels.map(clean).filter(Boolean)
505
+ : [];
506
+ if (Array.isArray(model?.reasoningLevels)) return filterProvider(declared);
507
+ const family = clean(model?.family).toLowerCase();
508
+ if (Object.prototype.hasOwnProperty.call(EFFORT_BY_FAMILY, family)) {
509
+ return filterProvider(EFFORT_BY_FAMILY[family]);
510
+ }
511
+ return providerAllowed || [];
512
+ }
513
+
514
+ function coerceEffortFor(provider, model, effort) {
515
+ if (!effort) return null;
516
+ const allowed = effortOptionsFor(provider, model);
517
+ if (!allowed || allowed.length === 0) return null;
518
+ if (allowed.includes(effort)) return effort;
519
+ for (const candidate of EFFORT_FALLBACKS[effort] || []) {
520
+ if (allowed.includes(candidate)) return candidate;
521
+ }
522
+ return null;
523
+ }
524
+
525
+ function hasOwn(obj, key) {
526
+ return Object.prototype.hasOwnProperty.call(obj || {}, key);
527
+ }
528
+
529
+ function modelSettingsFor(config, provider, model) {
530
+ const key = routeFastKey(provider, model);
531
+ const value = key ? config?.modelSettings?.[key] : null;
532
+ return value && typeof value === 'object' ? value : {};
533
+ }
534
+
535
+ function normalizeSavedEffort(value) {
536
+ try {
537
+ return normalizeEffortInput(value);
538
+ } catch {
539
+ return null;
540
+ }
541
+ }
542
+
543
+ function effortItemsFor(provider, model, activeEffort) {
544
+ const allowed = effortOptionsFor(provider, model);
545
+ const items = [];
546
+ for (const value of allowed || []) {
547
+ items.push({
548
+ value,
549
+ label: EFFORT_LABELS[value] || value,
550
+ description: value === activeEffort ? 'current' : '',
551
+ });
552
+ }
553
+ return items;
554
+ }
555
+
556
+ function toolSpecForMode(mode) {
557
+ return mode === 'readonly' ? ['tools:readonly'] : 'full';
558
+ }
559
+
560
+ function clean(value) {
561
+ return String(value ?? '').trim();
562
+ }
563
+
564
+ const OUTPUT_STYLE_ORDER = ['default', 'simple', 'extreme-simple'];
565
+ const OUTPUT_STYLE_ALIASES = new Map([
566
+ ['compact', 'default'],
567
+ ['normal', 'default'],
568
+ ['extreme', 'extreme-simple'],
569
+ ['extremesimple', 'extreme-simple'],
570
+ ['extreme-simple', 'extreme-simple'],
571
+ ['extreme_simple', 'extreme-simple'],
572
+ ]);
573
+
574
+ function normalizeOutputStyleId(value) {
575
+ const raw = clean(value).toLowerCase();
576
+ if (!raw) return '';
577
+ const slug = raw.replace(/[_\s]+/g, '-').replace(/^-+|-+$/g, '');
578
+ const compact = slug.replace(/[_.-]+/g, '');
579
+ if (OUTPUT_STYLE_ALIASES.has(slug)) return OUTPUT_STYLE_ALIASES.get(slug);
580
+ if (OUTPUT_STYLE_ALIASES.has(compact)) return OUTPUT_STYLE_ALIASES.get(compact);
581
+ return /^[a-z0-9.-]+$/.test(slug) ? slug : '';
582
+ }
583
+
584
+ function outputStyleCompactKey(value) {
585
+ return normalizeOutputStyleId(value).replace(/[_.-]+/g, '');
586
+ }
587
+
588
+ function titleCaseOutputStyle(id) {
589
+ return clean(id)
590
+ .split(/[_.-]+/)
591
+ .filter(Boolean)
592
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
593
+ .join(' ') || 'Default';
594
+ }
595
+
596
+ function parseOutputStyleFrontmatter(markdown) {
597
+ const match = String(markdown || '').match(/^---\r?\n([\s\S]*?)\r?\n---/);
598
+ const meta = {};
599
+ if (!match) return meta;
600
+ for (const line of match[1].split(/\r?\n/)) {
601
+ const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*?)\s*$/);
602
+ if (!kv) continue;
603
+ meta[kv[1]] = kv[2].replace(/^['"]|['"]$/g, '').trim();
604
+ }
605
+ return meta;
606
+ }
607
+
608
+ function readOutputStyleMetadata(filePath, source) {
609
+ let raw = '';
610
+ try { raw = readFileSync(filePath, 'utf8'); } catch { return null; }
611
+ const meta = parseOutputStyleFrontmatter(raw);
612
+ const fileId = normalizeOutputStyleId(basename(filePath).replace(/\.md$/i, ''));
613
+ const id = normalizeOutputStyleId(meta.name) || fileId;
614
+ if (!id) return null;
615
+ const aliases = clean(meta.aliases)
616
+ .split(',')
617
+ .map((value) => normalizeOutputStyleId(value))
618
+ .filter(Boolean);
619
+ const label = clean(meta.title || meta.label) || titleCaseOutputStyle(id);
620
+ return {
621
+ id,
622
+ label,
623
+ description: clean(meta.description),
624
+ aliases,
625
+ source,
626
+ };
627
+ }
628
+
629
+ function listOutputStyleCatalog(dataDir = STANDALONE_DATA_DIR) {
630
+ const byId = new Map();
631
+ const dirs = [
632
+ { dir: join(STANDALONE_ROOT, 'output-styles'), source: 'builtin' },
633
+ { dir: join(dataDir || STANDALONE_DATA_DIR, 'output-styles'), source: 'user' },
634
+ ];
635
+ for (const { dir, source } of dirs) {
636
+ let entries = [];
637
+ try { entries = readdirSync(dir, { withFileTypes: true }); } catch { continue; }
638
+ for (const entry of entries) {
639
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith('.md')) continue;
640
+ const style = readOutputStyleMetadata(join(dir, entry.name), source);
641
+ if (style) byId.set(style.id, style);
642
+ }
643
+ }
644
+ return [...byId.values()].sort((a, b) => {
645
+ const ai = OUTPUT_STYLE_ORDER.indexOf(a.id);
646
+ const bi = OUTPUT_STYLE_ORDER.indexOf(b.id);
647
+ if (ai !== bi) return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi);
648
+ return a.label.localeCompare(b.label, 'en', { sensitivity: 'base' });
649
+ });
650
+ }
651
+
652
+ function findOutputStyle(value, styles) {
653
+ const id = normalizeOutputStyleId(value);
654
+ const compact = outputStyleCompactKey(value);
655
+ if (!id && !compact) return null;
656
+ return (styles || []).find((style) => {
657
+ if (style.id === id || outputStyleCompactKey(style.id) === compact) return true;
658
+ if (outputStyleCompactKey(style.label) === compact) return true;
659
+ return (style.aliases || []).some((alias) => alias === id || outputStyleCompactKey(alias) === compact);
660
+ }) || null;
661
+ }
662
+
663
+ function configuredOutputStyleValue(dataDir = STANDALONE_DATA_DIR) {
664
+ const unified = readJsonSafe(join(dataDir || STANDALONE_DATA_DIR, 'mixdog-config.json')) || {};
665
+ return clean(unified.outputStyle || (unified.agent && unified.agent.outputStyle) || 'default') || 'default';
666
+ }
667
+
668
+ function outputStyleStatus(dataDir = STANDALONE_DATA_DIR) {
669
+ const styles = listOutputStyleCatalog(dataDir);
670
+ const configured = configuredOutputStyleValue(dataDir);
671
+ const current = findOutputStyle(configured, styles)
672
+ || findOutputStyle('default', styles)
673
+ || styles[0]
674
+ || { id: 'default', label: 'Default', description: '', aliases: [], source: 'builtin' };
675
+ return { configured, current, styles };
676
+ }
677
+
678
+ function sessionHasConversationMessages(activeSession) {
679
+ const messages = Array.isArray(activeSession?.messages) ? activeSession.messages : [];
680
+ return messages.some((message) => {
681
+ const role = message?.role;
682
+ if (role !== 'user' && role !== 'assistant' && role !== 'tool') return false;
683
+ const text = sessionMessageText(message.content).trim();
684
+ if (!text && role !== 'assistant') return false;
685
+ if (role === 'user' && isSessionPreviewNoise(text)) return false;
686
+ return true;
687
+ });
688
+ }
689
+
690
+ function readJsonSafe(path) {
691
+ try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
692
+ }
693
+
694
+ function countSkillFiles(root) {
695
+ const skillsDir = join(root, 'skills');
696
+ if (!existsSync(skillsDir)) return 0;
697
+ let count = 0;
698
+ const walk = (dir) => {
699
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
700
+ const full = join(dir, entry.name);
701
+ if (entry.isDirectory()) walk(full);
702
+ else if (/^(SKILL|skill)\.md$/i.test(entry.name) || entry.name.toLowerCase().endsWith('.md')) count += 1;
703
+ }
704
+ };
705
+ try { walk(skillsDir); } catch { return count; }
706
+ return count;
707
+ }
708
+
709
+ function mcpScriptForPlugin(root) {
710
+ const candidates = [
711
+ 'scripts/run-mcp.mjs',
712
+ 'mcp/server.mjs',
713
+ 'server.mjs',
714
+ ];
715
+ return candidates.find((rel) => existsSync(join(root, rel))) || null;
716
+ }
717
+
718
+ function pluginManifest(root) {
719
+ return readJsonSafe(join(root, '.codex-plugin', 'plugin.json'))
720
+ || readJsonSafe(join(root, 'plugin.json'))
721
+ || {};
722
+ }
723
+
724
+ function pluginMcpServerName(plugin = {}) {
725
+ const base = clean(plugin.name || plugin.title || 'plugin')
726
+ .toLowerCase()
727
+ .replace(/[^a-z0-9_.-]+/g, '-')
728
+ .replace(/^-+|-+$/g, '');
729
+ return base ? `plugin-${base}` : 'plugin-mcp';
730
+ }
731
+
732
+ function findPreset(config, key) {
733
+ const wanted = clean(key).toLowerCase();
734
+ if (!wanted) return null;
735
+ const presets = Array.isArray(config?.presets) ? config.presets : [];
736
+ return presets.find((p) => {
737
+ const id = clean(p?.id).toLowerCase();
738
+ const name = clean(p?.name).toLowerCase();
739
+ return id === wanted || name === wanted;
740
+ }) || null;
741
+ }
742
+
743
+ function resolveRoute(config, { provider, model, effort, fast } = {}) {
744
+ const explicitProvider = clean(provider);
745
+ const explicitModel = clean(model);
746
+ const hasExplicitEffort = effort !== undefined;
747
+ const explicitEffort = hasExplicitEffort ? normalizeEffortInput(effort) : undefined;
748
+ const hasExplicitFast = fast !== undefined;
749
+ const explicitFast = fast === true;
750
+
751
+ if (explicitModel && !explicitProvider) {
752
+ const preset = findPreset(config, explicitModel);
753
+ if (preset) {
754
+ const p = clean(preset.provider) || DEFAULT_PROVIDER;
755
+ const m = clean(preset.model) || DEFAULT_MODEL;
756
+ const saved = modelSettingsFor(config, p, m);
757
+ return {
758
+ provider: p,
759
+ model: m,
760
+ preset,
761
+ effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
762
+ fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
763
+ };
764
+ }
765
+ }
766
+
767
+ if (!explicitProvider && !explicitModel) {
768
+ const defaultKey = config?.default;
769
+ const preset = findPreset(config, defaultKey);
770
+ if (preset) {
771
+ const p = clean(preset.provider) || DEFAULT_PROVIDER;
772
+ const m = clean(preset.model) || DEFAULT_MODEL;
773
+ const saved = modelSettingsFor(config, p, m);
774
+ return {
775
+ provider: p,
776
+ model: m,
777
+ preset,
778
+ effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
779
+ fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
780
+ };
781
+ }
782
+ }
783
+
784
+ const p = explicitProvider || DEFAULT_PROVIDER;
785
+ const m = explicitModel || DEFAULT_MODEL;
786
+ const saved = modelSettingsFor(config, p, m);
787
+ return {
788
+ provider: p,
789
+ model: m,
790
+ preset: null,
791
+ effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort),
792
+ fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : fastPreferenceFor(config, p, m)),
793
+ };
794
+ }
795
+
796
+ function ensureProviderEnabled(config, provider) {
797
+ const providers = { ...(config?.providers || {}) };
798
+ providers[provider] = { ...(providers[provider] || {}), enabled: true };
799
+ return providers;
800
+ }
801
+
802
+ const AUTO_CLEAR_DEFAULT_IDLE_MS = 60 * 60 * 1000;
803
+
804
+ function normalizeSystemShellConfig(value = {}) {
805
+ const raw = value && typeof value === 'object' ? value : {};
806
+ const command = clean(raw.command ?? raw.path ?? raw.executable ?? raw.shell);
807
+ const envCommand = clean(process.env.MIXDOG_SHELL);
808
+ return {
809
+ command,
810
+ effective: command || envCommand || '',
811
+ source: command ? 'config' : (envCommand ? 'env' : 'auto'),
812
+ };
813
+ }
814
+
815
+ function normalizeSystemShellCommand(value) {
816
+ const command = clean(value).replace(/^auto$/i, '').replace(/^['"](.+)['"]$/, '$1').trim();
817
+ if (!command) return '';
818
+ if (process.platform === 'win32') {
819
+ const stem = command.split(/[\\/]/).pop().toLowerCase().replace(/\.exe$/, '');
820
+ if (stem !== 'powershell' && stem !== 'pwsh') {
821
+ throw new Error('system shell command must be powershell.exe or pwsh on Windows');
822
+ }
823
+ }
824
+ return command;
825
+ }
826
+
827
+ function normalizeAutoClearConfig(value = {}) {
828
+ const raw = value && typeof value === 'object' ? value : {};
829
+ const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
830
+ return {
831
+ enabled: raw.enabled !== false,
832
+ idleMs: Number.isFinite(idleMs) && idleMs > 0 ? Math.max(60_000, Math.round(idleMs)) : AUTO_CLEAR_DEFAULT_IDLE_MS,
833
+ };
834
+ }
835
+
836
+ function formatDurationMs(ms) {
837
+ const value = Math.max(0, Number(ms) || 0);
838
+ if (value % 3_600_000 === 0) return `${value / 3_600_000}h`;
839
+ if (value % 60_000 === 0) return `${value / 60_000}m`;
840
+ return `${Math.round(value / 1000)}s`;
841
+ }
842
+
843
+ function parseDurationMs(input) {
844
+ const text = clean(input).toLowerCase();
845
+ if (!text) return null;
846
+ const match = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/.exec(text);
847
+ if (!match) return null;
848
+ const n = Number(match[1]);
849
+ if (!Number.isFinite(n) || n <= 0) return null;
850
+ const unit = match[2] || 'm';
851
+ const mult = unit === 'h' ? 3_600_000 : unit === 'm' ? 60_000 : unit === 's' ? 1000 : 1;
852
+ return Math.max(60_000, Math.round(n * mult));
853
+ }
854
+
855
+ const FAST_CAPABLE_PROVIDERS = new Set(['anthropic', 'anthropic-oauth', 'openai', 'openai-oauth']);
856
+ const LAZY_SECRET_PROVIDERS = new Set(['openai-oauth', 'anthropic-oauth', 'grok-oauth', 'ollama', 'lmstudio']);
857
+
858
+ function routeFastKey(provider, model) {
859
+ const p = clean(provider);
860
+ const m = clean(model);
861
+ return p && m ? `${p}/${m}` : '';
862
+ }
863
+
864
+ function openAiModelMetaSupportsFast(model) {
865
+ const tiers = Array.isArray(model?.serviceTiers) ? model.serviceTiers : [];
866
+ const speedTiers = Array.isArray(model?.additionalSpeedTiers) ? model.additionalSpeedTiers : [];
867
+ if (tiers.length || speedTiers.length || model?.defaultServiceTier) {
868
+ return tiers.some((tier) => tier?.id === 'priority')
869
+ || speedTiers.includes('priority')
870
+ || model?.defaultServiceTier === 'priority';
871
+ }
872
+ const id = clean(model?.id || model).toLowerCase();
873
+ if (id.includes('mini') || id.includes('nano') || id.includes('codex')) return false;
874
+ return /^gpt-5(\.|-|$)/.test(id);
875
+ }
876
+
877
+ function openAiDirectModelSupportsFast(model) {
878
+ const id = clean(model?.id || model);
879
+ return /^gpt-5\.5(?:-\d{4}|$)/.test(id)
880
+ || /^gpt-5\.4(?:-\d{4}|$)/.test(id)
881
+ || /^gpt-5\.4-mini(?:-\d{4}|$)/.test(id);
882
+ }
883
+
884
+ function anthropicModelMetaSupportsFast(model) {
885
+ const id = clean(model?.id || model).toLowerCase();
886
+ return /^claude-(opus|sonnet)/.test(id);
887
+ }
888
+
889
+ function fastCapableFor(provider, model) {
890
+ const p = clean(provider);
891
+ if (!FAST_CAPABLE_PROVIDERS.has(p)) return false;
892
+ if (p === 'openai') return openAiDirectModelSupportsFast(model);
893
+ if (p === 'openai-oauth') return openAiModelMetaSupportsFast(model);
894
+ if (p === 'anthropic' || p === 'anthropic-oauth') return anthropicModelMetaSupportsFast(model);
895
+ return false;
896
+ }
897
+
898
+ function fastPreferenceFor(config, provider, model) {
899
+ const key = routeFastKey(provider, model);
900
+ if (!key) return false;
901
+ const saved = config?.modelSettings?.[key];
902
+ if (saved && typeof saved === 'object' && hasOwn(saved, 'fast')) return saved.fast === true;
903
+ return config?.fastModels?.[key] === true;
904
+ }
905
+
906
+ function saveModelSettings(cfgMod, route, { fastCapable = true } = {}) {
907
+ const key = routeFastKey(route?.provider, route?.model);
908
+ if (!key) return cfgMod.loadConfig();
909
+ const nextConfig = cfgMod.loadConfig();
910
+ const modelSettings = { ...(nextConfig.modelSettings || {}) };
911
+ const nextSetting = { ...(modelSettings[key] || {}) };
912
+ if (hasOwn(route, 'effort') && route.effort) nextSetting.effort = route.effort;
913
+ else delete nextSetting.effort;
914
+ if (fastCapable) nextSetting.fast = route.fast === true;
915
+ else nextSetting.fast = false;
916
+ modelSettings[key] = nextSetting;
917
+
918
+ // Legacy compatibility: keep fastModels true entries for old readers, but
919
+ // let modelSettings.fast=false override them in new readers.
920
+ const fastModels = { ...(nextConfig.fastModels || {}) };
921
+ if (nextSetting.fast === true) fastModels[key] = true;
922
+ else delete fastModels[key];
923
+
924
+ const savedConfig = { ...nextConfig, modelSettings, fastModels };
925
+ cfgMod.saveConfig(savedConfig);
926
+ return savedConfig;
927
+ }
928
+
929
+ function routeForStatusline(route) {
930
+ const out = {
931
+ mode: 'fixed',
932
+ defaultProvider: route.provider,
933
+ defaultModel: route.model,
934
+ };
935
+ const preset = route.preset || {};
936
+ if (preset.id) out.presetId = preset.id;
937
+ if (preset.name) out.presetName = preset.name;
938
+ if (preset.modelDisplay) out.modelDisplay = preset.modelDisplay;
939
+ if (route.fast === true || route.fast === false) out.fast = route.fast;
940
+ else if (preset.fast === true || preset.fast === false) out.fast = preset.fast;
941
+ if (route.effectiveEffort) {
942
+ out.effort = route.effectiveEffort;
943
+ out.displayEffort = route.effectiveEffort;
944
+ } else if (hasOwn(route, 'effort')) {
945
+ delete out.effort;
946
+ delete out.displayEffort;
947
+ }
948
+ return out;
949
+ }
950
+
951
+ const ONBOARDING_VERSION = 1;
952
+ const WORKFLOW_ROUTE_SLOTS = ['lead', 'bridge', 'explorer', 'memory'];
953
+ const FIXED_AGENT_SLOTS = Object.freeze([
954
+ { id: 'explore', label: 'Explore', description: 'Broad repository exploration', workflowSlot: 'explorer' },
955
+ { id: 'web-researcher', label: 'Web Researcher', description: 'External current-info research' },
956
+ { id: 'maintainer', label: 'Maintainer', description: 'Background memory and upkeep', workflowSlot: 'memory' },
957
+ { id: 'worker', label: 'Worker', description: 'Scoped implementation' },
958
+ { id: 'heavy-worker', label: 'Heavy Worker', description: 'Broad or multi-file implementation' },
959
+ { id: 'reviewer', label: 'Reviewer', description: 'Diff review and risk checks' },
960
+ { id: 'debugger', label: 'Debugger', description: 'Root-cause analysis and failure tracing' },
961
+ ]);
962
+ const AGENT_ROLE_IDS = new Set(FIXED_AGENT_SLOTS.map((agent) => agent.id));
963
+ const agentDefinitionCache = new Map();
964
+ const DEFAULT_WORKFLOW_ID = 'default';
965
+
966
+ function workflowPresetId(slot) {
967
+ return `workflow-${slot}`;
968
+ }
969
+
970
+ function workflowPresetName(slot) {
971
+ return `WORKFLOW ${String(slot || '').toUpperCase()}`;
972
+ }
973
+
974
+ function agentPresetSlot(agentId) {
975
+ return `agent-${String(agentId || '').replace(/[^a-z0-9_.-]+/gi, '-').toLowerCase()}`;
976
+ }
977
+
978
+ function normalizeAgentId(value) {
979
+ const id = clean(value).toLowerCase().replace(/[\s_]+/g, '-');
980
+ if (id === 'explorer') return 'explore';
981
+ if (id === 'maint' || id === 'maintenance' || id === 'memory') return 'maintainer';
982
+ if (id === 'heavy' || id === 'heavyworker') return 'heavy-worker';
983
+ if (id === 'review') return 'reviewer';
984
+ if (id === 'debug') return 'debugger';
985
+ return AGENT_ROLE_IDS.has(id) ? id : '';
986
+ }
987
+
988
+ function normalizeWorkflowId(value, fallback = '') {
989
+ const id = clean(value).toLowerCase().replace(/[\s_]+/g, '-');
990
+ return /^[a-z0-9][a-z0-9_.-]*$/.test(id) ? id : fallback;
991
+ }
992
+
993
+ function readTextSafe(path) {
994
+ try { return readFileSync(path, 'utf8').trim(); } catch { return ''; }
995
+ }
996
+
997
+ function workflowSourceDirs(dataDir) {
998
+ return [
999
+ { root: join(STANDALONE_ROOT, 'workflows'), source: 'built-in' },
1000
+ { root: join(dataDir || STANDALONE_DATA_DIR, 'workflows'), source: 'user' },
1001
+ ];
1002
+ }
1003
+
1004
+ function agentSourceDirs(dataDir, id) {
1005
+ return [
1006
+ join(dataDir || STANDALONE_DATA_DIR, 'agents', id),
1007
+ join(STANDALONE_ROOT, 'agents', id),
1008
+ ];
1009
+ }
1010
+
1011
+ function readWorkflowPackFromDir(dir, source = 'built-in') {
1012
+ const manifest = readJsonSafe(join(dir, 'workflow.json'));
1013
+ if (!manifest || typeof manifest !== 'object') return null;
1014
+ const id = normalizeWorkflowId(manifest.id || manifest.name);
1015
+ if (!id) return null;
1016
+ const entry = clean(manifest.entry) || 'WORKFLOW.md';
1017
+ const body = readTextSafe(join(dir, entry));
1018
+ if (!body) return null;
1019
+ return {
1020
+ id,
1021
+ name: clean(manifest.name) || id,
1022
+ description: clean(manifest.description),
1023
+ entry,
1024
+ agents: Array.isArray(manifest.agents) ? manifest.agents.map((agent) => normalizeAgentId(agent) || normalizeWorkflowId(agent)).filter(Boolean) : [],
1025
+ body,
1026
+ source,
1027
+ };
1028
+ }
1029
+
1030
+ function listWorkflowPacks(dataDir) {
1031
+ const byId = new Map();
1032
+ for (const { root, source } of workflowSourceDirs(dataDir)) {
1033
+ if (!existsSync(root)) continue;
1034
+ let entries = [];
1035
+ try { entries = readdirSync(root, { withFileTypes: true }); } catch { entries = []; }
1036
+ for (const entry of entries) {
1037
+ if (!entry.isDirectory()) continue;
1038
+ const pack = readWorkflowPackFromDir(join(root, entry.name), source);
1039
+ if (pack) byId.set(pack.id, pack);
1040
+ }
1041
+ }
1042
+ return [...byId.values()].sort((a, b) => {
1043
+ if (a.id === DEFAULT_WORKFLOW_ID) return -1;
1044
+ if (b.id === DEFAULT_WORKFLOW_ID) return 1;
1045
+ return a.name.localeCompare(b.name);
1046
+ });
1047
+ }
1048
+
1049
+ function activeWorkflowId(config) {
1050
+ return normalizeWorkflowId(config?.workflow?.active, DEFAULT_WORKFLOW_ID);
1051
+ }
1052
+
1053
+ function loadWorkflowPack(dataDir, id) {
1054
+ const wanted = normalizeWorkflowId(id, DEFAULT_WORKFLOW_ID);
1055
+ for (const { root, source } of workflowSourceDirs(dataDir).reverse()) {
1056
+ const pack = readWorkflowPackFromDir(join(root, wanted), source);
1057
+ if (pack) return pack;
1058
+ }
1059
+ return readWorkflowPackFromDir(join(STANDALONE_ROOT, 'workflows', DEFAULT_WORKFLOW_ID), 'built-in');
1060
+ }
1061
+
1062
+ function loadAgentDefinition(dataDir, id) {
1063
+ const agentId = normalizeAgentId(id) || normalizeWorkflowId(id);
1064
+ if (!agentId) return null;
1065
+ const cacheKey = `${dataDir || STANDALONE_DATA_DIR}\n${agentId}`;
1066
+ if (agentDefinitionCache.has(cacheKey)) return agentDefinitionCache.get(cacheKey);
1067
+ for (const dir of agentSourceDirs(dataDir, agentId)) {
1068
+ const manifest = readJsonSafe(join(dir, 'agent.json')) || {};
1069
+ const entry = clean(manifest.entry) || 'AGENT.md';
1070
+ const body = readTextSafe(join(dir, entry));
1071
+ if (!body) continue;
1072
+ const definition = {
1073
+ id: agentId,
1074
+ name: clean(manifest.name) || FIXED_AGENT_SLOTS.find((agent) => agent.id === agentId)?.label || agentId,
1075
+ description: clean(manifest.description) || FIXED_AGENT_SLOTS.find((agent) => agent.id === agentId)?.description || '',
1076
+ body,
1077
+ };
1078
+ agentDefinitionCache.set(cacheKey, definition);
1079
+ return definition;
1080
+ }
1081
+ const legacyBody = readTextSafe(join(dataDir || STANDALONE_DATA_DIR, 'roles', `${agentId}.md`))
1082
+ || readTextSafe(join(STANDALONE_ROOT, 'agents', `${agentId}.md`));
1083
+ if (!legacyBody) {
1084
+ agentDefinitionCache.set(cacheKey, null);
1085
+ return null;
1086
+ }
1087
+ const definition = {
1088
+ id: agentId,
1089
+ name: FIXED_AGENT_SLOTS.find((agent) => agent.id === agentId)?.label || agentId,
1090
+ description: '',
1091
+ body: legacyBody,
1092
+ };
1093
+ agentDefinitionCache.set(cacheKey, definition);
1094
+ return definition;
1095
+ }
1096
+
1097
+ function workflowContextBlock(config, dataDir) {
1098
+ const pack = loadWorkflowPack(dataDir, activeWorkflowId(config));
1099
+ if (!pack) return '';
1100
+ const lines = [
1101
+ `# Active Workflow: ${pack.name}`,
1102
+ ];
1103
+ if (pack.description) lines.push(pack.description);
1104
+ lines.push(pack.body);
1105
+
1106
+ const agentIds = pack.agents.length ? pack.agents : FIXED_AGENT_SLOTS.map((agent) => agent.id);
1107
+ const agentBlocks = agentIds
1108
+ .map((id) => loadAgentDefinition(dataDir, id))
1109
+ .filter(Boolean);
1110
+ if (agentBlocks.length) {
1111
+ lines.push('# Available Agents');
1112
+ for (const agent of agentBlocks) {
1113
+ lines.push(`## ${agent.name} (${agent.id})`);
1114
+ if (agent.description) lines.push(agent.description);
1115
+ lines.push(agent.body);
1116
+ }
1117
+ }
1118
+ return lines.join('\n\n');
1119
+ }
1120
+
1121
+ function normalizeWorkflowRoute(routeLike, fallback = {}) {
1122
+ const provider = clean(routeLike?.provider) || clean(fallback.provider);
1123
+ const model = clean(routeLike?.model) || clean(fallback.model);
1124
+ if (!provider || !model) return null;
1125
+ const effort = normalizeEffortInput(routeLike?.effort ?? fallback.effort);
1126
+ const fast = routeLike?.fast ?? fallback.fast;
1127
+ return {
1128
+ provider,
1129
+ model,
1130
+ ...(effort ? { effort } : {}),
1131
+ ...(fast === true ? { fast: true } : {}),
1132
+ };
1133
+ }
1134
+
1135
+ function upsertWorkflowPreset(presets, slot, routeLike) {
1136
+ const route = normalizeWorkflowRoute(routeLike);
1137
+ if (!route) return presets;
1138
+ const id = workflowPresetId(slot);
1139
+ const preset = {
1140
+ id,
1141
+ name: workflowPresetName(slot),
1142
+ type: 'bridge',
1143
+ provider: route.provider,
1144
+ model: route.model,
1145
+ ...(route.effort ? { effort: route.effort } : {}),
1146
+ ...(route.fast === true ? { fast: true } : {}),
1147
+ tools: 'full',
1148
+ };
1149
+ const next = (Array.isArray(presets) ? presets : []).filter((p) => clean(p?.id) !== id && clean(p?.name) !== preset.name);
1150
+ next.push(preset);
1151
+ return next;
1152
+ }
1153
+
1154
+ function summarizeWorkflowRoutes(config) {
1155
+ const routes = config?.workflowRoutes && typeof config.workflowRoutes === 'object' ? config.workflowRoutes : {};
1156
+ const out = {};
1157
+ for (const slot of WORKFLOW_ROUTE_SLOTS) {
1158
+ const route = routes[slot];
1159
+ if (route?.provider && route?.model) out[slot] = normalizeWorkflowRoute(route);
1160
+ }
1161
+ return out;
1162
+ }
1163
+
1164
+ function legacyWorkflowRolePreset(dataDir, role) {
1165
+ try {
1166
+ const file = join(dataDir, 'user-workflow.json');
1167
+ if (!existsSync(file)) return '';
1168
+ const raw = JSON.parse(readFileSync(file, 'utf8'));
1169
+ const found = (raw?.roles || []).find((item) => clean(item?.name) === role);
1170
+ return clean(found?.preset);
1171
+ } catch {
1172
+ return '';
1173
+ }
1174
+ }
1175
+
1176
+ function routeFromPreset(config, presetName) {
1177
+ const preset = findPreset(config, presetName);
1178
+ return preset ? normalizeWorkflowRoute(preset) : null;
1179
+ }
1180
+
1181
+ function agentRouteFromConfig(config, agentId, dataDir) {
1182
+ const id = normalizeAgentId(agentId);
1183
+ if (!id) return null;
1184
+ const explicit = normalizeWorkflowRoute(config?.agents?.[id])
1185
+ || (id === 'maintainer' ? normalizeWorkflowRoute(config?.agents?.maintenance) : null);
1186
+ if (explicit) return explicit;
1187
+
1188
+ const agent = FIXED_AGENT_SLOTS.find((item) => item.id === id);
1189
+ if (agent?.workflowSlot) {
1190
+ const workflowRoute = normalizeWorkflowRoute(config?.workflowRoutes?.[agent.workflowSlot]);
1191
+ if (workflowRoute) return workflowRoute;
1192
+ }
1193
+
1194
+ if (id === 'explore') return routeFromPreset(config, config?.maintenance?.explore);
1195
+ if (id === 'maintainer') return routeFromPreset(config, config?.maintenance?.memory);
1196
+
1197
+ return routeFromPreset(config, legacyWorkflowRolePreset(dataDir, id));
1198
+ }
1199
+
1200
+ function toolResponseText(result) {
1201
+ if (result && typeof result === 'object' && Array.isArray(result.content)) {
1202
+ return result.content
1203
+ .map((part) => (part?.type === 'text' ? part.text || '' : JSON.stringify(part)))
1204
+ .join('\n');
1205
+ }
1206
+ if (typeof result === 'string') return result;
1207
+ return JSON.stringify(result, null, 2);
1208
+ }
1209
+
1210
+ function isEmptyRecallText(value) {
1211
+ const text = String(value || '').trim();
1212
+ return !text || /^\(?no results\)?$/i.test(text) || /^\(?empty memory result\)?$/i.test(text);
1213
+ }
1214
+
1215
+ function currentSessionRecallRows(session, query, { limit = 10 } = {}) {
1216
+ const messages = Array.isArray(session?.messages) ? session.messages : [];
1217
+ if (!messages.length) return '(no results)';
1218
+ const terms = [...new Set(String(query || '').toLowerCase().match(/[\p{L}\p{N}_./:-]{2,}/gu) || [])]
1219
+ .filter(Boolean)
1220
+ .slice(0, 16);
1221
+ const max = Math.max(1, Math.min(100, Number(limit) || 10));
1222
+ const rows = [];
1223
+ for (let i = messages.length - 1; i >= 0 && rows.length < max; i -= 1) {
1224
+ const m = messages[i];
1225
+ if (!m || (m.role !== 'user' && m.role !== 'assistant' && m.role !== 'tool')) continue;
1226
+ const text = messageContextText(m).replace(/\s+/g, ' ').trim();
1227
+ if (!text) continue;
1228
+ if (terms.length && !terms.some((term) => text.toLowerCase().includes(term))) continue;
1229
+ rows.push(`[session:${i + 1}] ${m.role}: ${text.slice(0, 1000)}`);
1230
+ }
1231
+ return rows.length ? rows.join('\n') : '(no results)';
1232
+ }
1233
+
1234
+ function parseToolSelection(value) {
1235
+ if (Array.isArray(value)) return value.map(clean).filter(Boolean);
1236
+ return String(value || '')
1237
+ .split(/[,\s]+/)
1238
+ .map(clean)
1239
+ .filter(Boolean);
1240
+ }
1241
+
1242
+ function toolKind(tool) {
1243
+ const name = clean(tool?.name);
1244
+ if (name.startsWith('mcp__')) return 'mcp';
1245
+ if (name.startsWith('skill_') || name === 'skills_list') return 'skill';
1246
+ if (tool?.annotations?.bridgeHidden) return 'control';
1247
+ if (['apply_patch', 'shell'].includes(name)) return 'mutation';
1248
+ return 'tool';
1249
+ }
1250
+
1251
+ function toolSchemaBucket(tool) {
1252
+ const name = clean(tool?.name);
1253
+ const kind = toolKind(tool);
1254
+ if (kind === 'mcp') return 'mcp';
1255
+ if (kind === 'skill') return 'skills';
1256
+ if (name === 'memory' || name === 'recall' || name.includes('memory')) return 'memory';
1257
+ if (name === 'search' || name === 'web_fetch') return 'web';
1258
+ if (['read', 'grep', 'glob', 'list', 'code_graph', 'explore'].includes(name)) return 'code';
1259
+ if (['shell', 'apply_patch'].includes(name)) return 'mutation';
1260
+ if (name === 'bridge' || name === 'delegate' || name.includes('bridge')) return 'agents';
1261
+ if (name.includes('channel') || name.includes('discord') || name.includes('webhook')) return 'channels';
1262
+ if (name.includes('provider') || name === 'tool_search' || name === 'cwd') return 'setup';
1263
+ return 'other';
1264
+ }
1265
+
1266
+ function estimateToolSchemaBreakdown(tools) {
1267
+ const out = {};
1268
+ for (const tool of Array.isArray(tools) ? tools : []) {
1269
+ const bucket = toolSchemaBucket(tool);
1270
+ const row = out[bucket] || { count: 0, tokens: 0 };
1271
+ row.count += 1;
1272
+ row.tokens += estimateToolSchemaTokens([tool]);
1273
+ out[bucket] = row;
1274
+ }
1275
+ return out;
1276
+ }
1277
+
1278
+ function measuredToolUsage(name) {
1279
+ return MEASURED_TOOL_USAGE[clean(name)] || 0;
1280
+ }
1281
+
1282
+ function measuredToolRank(name) {
1283
+ const index = MEASURED_TOOL_ORDER.indexOf(clean(name));
1284
+ return index === -1 ? Number.MAX_SAFE_INTEGER : index;
1285
+ }
1286
+
1287
+ function sortedCatalogByMeasuredUsage(catalog) {
1288
+ return (catalog || [])
1289
+ .map((tool, index) => ({ tool, index }))
1290
+ .sort((a, b) => {
1291
+ const au = measuredToolUsage(a.tool?.name);
1292
+ const bu = measuredToolUsage(b.tool?.name);
1293
+ if (bu !== au) return bu - au;
1294
+ const ar = measuredToolRank(a.tool?.name);
1295
+ const br = measuredToolRank(b.tool?.name);
1296
+ if (ar !== br) return ar - br;
1297
+ return a.index - b.index;
1298
+ })
1299
+ .map((entry) => entry.tool);
1300
+ }
1301
+
1302
+ function activeToolForSurface(tool) {
1303
+ if (!tool || typeof tool !== 'object') return tool;
1304
+ return JSON.parse(JSON.stringify(tool));
1305
+ }
1306
+
1307
+ function filterDisallowedTools(tools, disallowed = []) {
1308
+ if (!Array.isArray(disallowed) || disallowed.length === 0) return tools;
1309
+ const deny = new Set(disallowed.map((name) => clean(name)).filter(Boolean));
1310
+ if (deny.size === 0) return tools;
1311
+ return (tools || []).filter((tool) => !deny.has(clean(tool?.name)));
1312
+ }
1313
+
1314
+ function sortedNamesByMeasuredUsage(names) {
1315
+ return [...(names || [])].sort((a, b) => {
1316
+ const au = measuredToolUsage(a);
1317
+ const bu = measuredToolUsage(b);
1318
+ if (bu !== au) return bu - au;
1319
+ const ar = measuredToolRank(a);
1320
+ const br = measuredToolRank(b);
1321
+ if (ar !== br) return ar - br;
1322
+ return String(a).localeCompare(String(b));
1323
+ });
1324
+ }
1325
+
1326
+ export function defaultDeferredToolNames(catalog, mode) {
1327
+ if (mode === 'lead') {
1328
+ const available = new Set((catalog || []).map((tool) => clean(tool?.name)).filter(Boolean));
1329
+ return new Set(DEFERRED_DEFAULT_LEAD_TOOLS.filter((name) => available.has(name)));
1330
+ }
1331
+ if (mode === 'readonly') {
1332
+ const available = new Set((catalog || []).map((tool) => clean(tool?.name)).filter(Boolean));
1333
+ return new Set(DEFERRED_DEFAULT_READONLY_TOOLS.filter((name) => available.has(name)));
1334
+ }
1335
+ const names = new Set(DEFERRED_ALWAYS_ACTIVE_TOOLS);
1336
+ const limit = DEFERRED_DEFAULT_FULL_LIMIT;
1337
+ for (const tool of sortedCatalogByMeasuredUsage(catalog)) {
1338
+ const name = clean(tool?.name);
1339
+ if (!name || names.has(name)) continue;
1340
+ if (mode === 'readonly' && !isReadonlySelectable(tool)) continue;
1341
+ names.add(name);
1342
+ if (names.size >= DEFERRED_ALWAYS_ACTIVE_TOOLS.size + limit) break;
1343
+ }
1344
+ return names;
1345
+ }
1346
+
1347
+ export function compactToolSearchDescription(value, max = 220) {
1348
+ const text = clean(value).replace(/\s+/g, ' ');
1349
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
1350
+ }
1351
+
1352
+ function toolRow(tool, activeNames = new Set()) {
1353
+ const name = clean(tool?.name);
1354
+ return {
1355
+ name,
1356
+ kind: toolKind(tool),
1357
+ usage: measuredToolUsage(name),
1358
+ active: activeNames.has(name),
1359
+ description: compactToolSearchDescription(tool?.description),
1360
+ };
1361
+ }
1362
+
1363
+ function toolSearchTokens(value) {
1364
+ return (clean(value).toLowerCase().match(/[a-z0-9_.-]+/g) || [])
1365
+ .map((token) => token.replace(/[-.]+/g, '_'))
1366
+ .filter(Boolean);
1367
+ }
1368
+
1369
+ function toolSearchText(row) {
1370
+ const text = `${row.name} ${String(row.name || '').replace(/_/g, ' ')} ${row.kind} ${row.description} ${row.active ? 'active' : 'deferred'}`;
1371
+ return `${text} ${text.replace(/[-.]+/g, '_')}`.toLowerCase();
1372
+ }
1373
+
1374
+ function toolSearchMatches(row, query) {
1375
+ const raw = clean(query).toLowerCase();
1376
+ if (!raw) return true;
1377
+ const haystack = toolSearchText(row);
1378
+ if (haystack.includes(raw)) return true;
1379
+ const tokens = toolSearchTokens(raw);
1380
+ if (tokens.length === 0) return haystack.includes(raw);
1381
+ return tokens.some((token) => haystack.includes(token));
1382
+ }
1383
+
1384
+ function expandSelectionNames(names) {
1385
+ const out = [];
1386
+ for (const raw of names || []) {
1387
+ const key = clean(raw);
1388
+ if (!key) continue;
1389
+ const alias = DEFERRED_SELECT_ALIASES[key.toLowerCase()];
1390
+ if (alias) out.push(...alias);
1391
+ else out.push(key);
1392
+ }
1393
+ return [...new Set(out)];
1394
+ }
1395
+
1396
+ function isReadonlySelectable(tool) {
1397
+ const name = clean(tool?.name);
1398
+ if (READONLY_TOOL_NAMES.has(name)) return true;
1399
+ const annotations = tool?.annotations || {};
1400
+ if (annotations.destructiveHint === true) return false;
1401
+ if (annotations.readOnlyHint === true) return true;
1402
+ return false;
1403
+ }
1404
+
1405
+ function applyDeferredToolSurface(session, mode, extraTools = []) {
1406
+ if (!session || !Array.isArray(session.tools)) return session;
1407
+ const byName = new Map();
1408
+ for (const tool of [...session.tools, ...(extraTools || [])]) {
1409
+ const name = clean(tool?.name);
1410
+ if (!name || byName.has(name)) continue;
1411
+ byName.set(name, activeToolForSurface(tool));
1412
+ }
1413
+ const catalog = sortedCatalogByMeasuredUsage([...byName.values()]);
1414
+ const defaultNames = defaultDeferredToolNames(catalog, mode);
1415
+ session.deferredToolCatalog = catalog;
1416
+ session.deferredToolUsage = MEASURED_TOOL_USAGE;
1417
+ session.deferredSelectedTools = sortedNamesByMeasuredUsage(defaultNames);
1418
+ session.tools.length = 0;
1419
+ for (const tool of catalog) {
1420
+ if (!defaultNames.has(clean(tool?.name))) continue;
1421
+ if (mode === 'readonly' && !isReadonlySelectable(tool)) continue;
1422
+ session.tools.push(tool);
1423
+ }
1424
+ return session;
1425
+ }
1426
+
1427
+ function selectDeferredTools(session, names, mode) {
1428
+ const catalog = Array.isArray(session?.deferredToolCatalog)
1429
+ ? session.deferredToolCatalog
1430
+ : (Array.isArray(session?.tools) ? session.tools : []);
1431
+ const active = new Set((session?.tools || []).map((tool) => tool?.name).filter(Boolean));
1432
+ const byName = new Map(catalog.map((tool) => [tool?.name, tool]).filter(([name]) => name));
1433
+ const added = [];
1434
+ const already = [];
1435
+ const blocked = [];
1436
+ const missing = [];
1437
+ for (const name of expandSelectionNames(names)) {
1438
+ const tool = byName.get(name);
1439
+ if (!tool) {
1440
+ missing.push(name);
1441
+ continue;
1442
+ }
1443
+ if (mode === 'readonly' && !isReadonlySelectable(tool)) {
1444
+ blocked.push({ name, reason: 'readonly mode' });
1445
+ continue;
1446
+ }
1447
+ if (active.has(name)) {
1448
+ already.push(name);
1449
+ continue;
1450
+ }
1451
+ session.tools.push(tool);
1452
+ active.add(name);
1453
+ added.push(name);
1454
+ }
1455
+ session.deferredSelectedTools = sortedNamesByMeasuredUsage(active);
1456
+ return { added, already, blocked, missing };
1457
+ }
1458
+
1459
+ function renderToolSearch(args = {}, session, mode = 'full') {
1460
+ const catalog = Array.isArray(session?.deferredToolCatalog)
1461
+ ? session.deferredToolCatalog
1462
+ : (Array.isArray(session?.tools) ? session.tools : []);
1463
+ const activeNames = new Set((session?.tools || []).map((tool) => tool?.name).filter(Boolean));
1464
+ const query = clean(args.query).toLowerCase();
1465
+ const selectedNames = parseToolSelection(args.select);
1466
+ const limit = Math.max(1, Math.min(50, Number(args.limit) || 20));
1467
+ const selection = selectedNames.length ? selectDeferredTools(session, selectedNames, mode) : null;
1468
+ const nextActiveNames = new Set((session?.tools || []).map((tool) => tool?.name).filter(Boolean));
1469
+ const rows = catalog.map((tool) => toolRow(tool, nextActiveNames)).filter((row) => row.name);
1470
+ const matches = query
1471
+ ? rows.filter((row) => toolSearchMatches(row, query))
1472
+ : rows;
1473
+ return JSON.stringify({
1474
+ selected: selection,
1475
+ totalMatches: matches.length,
1476
+ matches: matches.slice(0, limit),
1477
+ activeTools: sortedNamesByMeasuredUsage(nextActiveNames),
1478
+ note: 'standalone: tool_search adds deferred tools to the current session schema for the next model iteration.',
1479
+ }, null, 2);
1480
+ }
1481
+
1482
+ function resolveCwdPath(currentCwd, value) {
1483
+ const raw = clean(value);
1484
+ if (!raw) throw new Error('cwd: path is required for action=set');
1485
+ const next = resolve(currentCwd || process.cwd(), raw);
1486
+ const stat = statSync(next);
1487
+ if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${next}`);
1488
+ return next;
1489
+ }
1490
+
1491
+ export async function createMixdogSessionRuntime({
1492
+ provider,
1493
+ model,
1494
+ cwd = process.cwd(),
1495
+ toolMode = 'full',
1496
+ } = {}) {
1497
+ bootProfile('session-runtime:start', { provider, model, toolMode, cwd });
1498
+ process.env.MIXDOG_QUIET_SESSION_LOG ??= '1';
1499
+ const standaloneStartedAt = performance.now();
1500
+ ensureStandaloneEnvironment({
1501
+ rootDir: STANDALONE_ROOT,
1502
+ dataDir: STANDALONE_DATA_DIR,
1503
+ });
1504
+ ensureProjectMixdogMd({ cwd });
1505
+ bootProfile('standalone-env:ready', { ms: (performance.now() - standaloneStartedAt).toFixed(1) });
1506
+
1507
+ const importsStartedAt = performance.now();
1508
+ const [
1509
+ cfgMod,
1510
+ sharedCfgMod,
1511
+ reg,
1512
+ mcpClient,
1513
+ mgr,
1514
+ contextMod,
1515
+ internalTools,
1516
+ statusRoutes,
1517
+ searchToolDefs,
1518
+ memoryToolDefs,
1519
+ channelToolDefs,
1520
+ codeGraphToolDefs,
1521
+ ] = await Promise.all([
1522
+ profiledImport('config', `${RUNTIME}/config.mjs`),
1523
+ profiledImport('shared-config', `${RUNTIME}/../../shared/config.mjs`),
1524
+ profiledImport('providers-registry', `${RUNTIME}/providers/registry.mjs`),
1525
+ profiledImport('mcp-client', `${RUNTIME}/mcp/client.mjs`),
1526
+ profiledImport('session-manager', `${RUNTIME}/session/manager.mjs`),
1527
+ profiledImport('context-collect', `${RUNTIME}/context/collect.mjs`),
1528
+ profiledImport('internal-tools', `${RUNTIME}/internal-tools.mjs`),
1529
+ profiledImport('status-routes', STATUSLINE_SESSION_ROUTES, { optional: true }),
1530
+ profiledImport('search-tool-defs', SEARCH_TOOL_DEFS, { optional: true }),
1531
+ profiledImport('memory-tool-defs', MEMORY_TOOL_DEFS, { optional: true }),
1532
+ profiledImport('channel-tool-defs', CHANNEL_TOOL_DEFS, { optional: true }),
1533
+ profiledImport('code-graph-tool-defs', CODE_GRAPH_TOOL_DEFS, { optional: true }),
1534
+ ]);
1535
+ bootProfile('imports:ready', { ms: (performance.now() - importsStartedAt).toFixed(1) });
1536
+ let memoryModPromise = null;
1537
+ let memoryInitPromise = null;
1538
+ let searchModPromise = null;
1539
+ let codeGraphModPromise = null;
1540
+
1541
+ async function getMemoryModule() {
1542
+ const startedAt = performance.now();
1543
+ memoryModPromise ??= import(MEMORY_RUNTIME);
1544
+ const mod = await memoryModPromise;
1545
+ if (typeof mod?.init === 'function') {
1546
+ memoryInitPromise ??= mod.init();
1547
+ await memoryInitPromise;
1548
+ }
1549
+ bootProfile('memory-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
1550
+ return mod;
1551
+ }
1552
+
1553
+ async function getSearchModule() {
1554
+ const startedAt = performance.now();
1555
+ searchModPromise ??= import(SEARCH_RUNTIME);
1556
+ const mod = await searchModPromise;
1557
+ bootProfile('search-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
1558
+ return mod;
1559
+ }
1560
+
1561
+ async function getCodeGraphModule() {
1562
+ const startedAt = performance.now();
1563
+ codeGraphModPromise ??= import(CODE_GRAPH_RUNTIME);
1564
+ const mod = await codeGraphModPromise;
1565
+ bootProfile('code-graph-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
1566
+ return mod;
1567
+ }
1568
+
1569
+ function persistLeadRoute(routeLike) {
1570
+ const leadRoute = normalizeWorkflowRoute(routeLike);
1571
+ if (!leadRoute) return null;
1572
+
1573
+ const nextConfig = { ...(config || {}) };
1574
+ nextConfig.presets = upsertWorkflowPreset(nextConfig.presets, 'lead', leadRoute);
1575
+ nextConfig.workflowRoutes = {
1576
+ ...(nextConfig.workflowRoutes || {}),
1577
+ lead: leadRoute,
1578
+ };
1579
+ nextConfig.default = workflowPresetId('lead');
1580
+
1581
+ cfgMod.saveConfig(nextConfig);
1582
+ config = nextConfig;
1583
+ return leadRoute;
1584
+ }
1585
+
1586
+ async function closePatchRuntimeIfLoaded(options = {}) {
1587
+ const closer = globalThis.__mixdogCloseNativePatchServers;
1588
+ if (typeof closer !== 'function' || globalThis.__mixdogNativePatchRuntimeTouched !== true) return;
1589
+ bootProfile('patch-runtime:close:start');
1590
+ const startedAt = performance.now();
1591
+ try {
1592
+ await closer(options);
1593
+ } catch {
1594
+ // Best-effort shutdown only; terminal restore must continue.
1595
+ } finally {
1596
+ bootProfile('patch-runtime:close:done', { ms: (performance.now() - startedAt).toFixed(1) });
1597
+ }
1598
+ }
1599
+
1600
+ function formatCoreMemoryLines(payload = {}) {
1601
+ const seen = new Set();
1602
+ const lines = [];
1603
+ for (const value of [
1604
+ ...(Array.isArray(payload.userLines) ? payload.userLines : []),
1605
+ ...(Array.isArray(payload.dbLines) ? payload.dbLines : []),
1606
+ ]) {
1607
+ const text = clean(value).replace(/\s+/g, ' ');
1608
+ if (!text) continue;
1609
+ const key = text.toLowerCase();
1610
+ if (seen.has(key)) continue;
1611
+ seen.add(key);
1612
+ lines.push(`- ${text}`);
1613
+ if (lines.length >= 40) break;
1614
+ }
1615
+ const out = lines.join('\n');
1616
+ const maxChars = 6000;
1617
+ return out.length > maxChars ? `${out.slice(0, maxChars).replace(/\s+\S*$/, '')}\n- ...` : out;
1618
+ }
1619
+
1620
+ async function loadCoreMemoryContext() {
1621
+ // Boot should not pay for memory/PG startup unless explicitly requested.
1622
+ // Recall and memory tools still initialize the memory service on first use.
1623
+ if (process.env.MIXDOG_BOOT_CORE_MEMORY !== '1') {
1624
+ bootProfile('core-memory:skipped');
1625
+ return '';
1626
+ }
1627
+ const startedAt = performance.now();
1628
+ let timer = null;
1629
+ const timeout = new Promise((resolve) => {
1630
+ timer = setTimeout(() => resolve(''), 2000);
1631
+ timer.unref?.();
1632
+ });
1633
+ try {
1634
+ return await Promise.race([
1635
+ (async () => {
1636
+ const memoryMod = await getMemoryModule();
1637
+ if (typeof memoryMod?.buildSessionCoreMemoryPayload !== 'function') return '';
1638
+ return formatCoreMemoryLines(await memoryMod.buildSessionCoreMemoryPayload(currentCwd));
1639
+ })(),
1640
+ timeout,
1641
+ ]);
1642
+ } catch {
1643
+ return '';
1644
+ } finally {
1645
+ if (timer) clearTimeout(timer);
1646
+ bootProfile('core-memory:done', { ms: (performance.now() - startedAt).toFixed(1) });
1647
+ }
1648
+ }
1649
+
1650
+ const configStartedAt = performance.now();
1651
+ let config = cfgMod.loadConfig({ secrets: false });
1652
+ setConfiguredShell(normalizeSystemShellConfig(config.shell).command);
1653
+ let configHasSecrets = false;
1654
+ let route = resolveRoute(config, { provider, model });
1655
+ bootProfile('config:ready', { ms: (performance.now() - configStartedAt).toFixed(1) });
1656
+ let mode = normalizeToolMode(toolMode);
1657
+ let session = null;
1658
+ let sessionCreatePromise = null;
1659
+ let currentCwd = cwd;
1660
+ let sessionNeedsCwdRefresh = false;
1661
+ const workspaceRouter = createWorkspaceRouter({ entryCwd: cwd });
1662
+ let closeRequested = false;
1663
+ let channelStartTimer = null;
1664
+ let providerWarmupTimer = null;
1665
+ let providerModelWarmupTimer = null;
1666
+ let activeTurnCount = 0;
1667
+ let firstTurnCompleted = false;
1668
+ const sessionPrewarmDelayMs = envDelayMs('MIXDOG_SESSION_PREWARM_DELAY_MS', 50, { min: 0, max: 10_000 });
1669
+ const providerWarmupDelayMs = envDelayMs('MIXDOG_PROVIDER_WARMUP_DELAY_MS', 1_500, { min: 0, max: 60_000 });
1670
+ const providerModelWarmupDelayMs = envDelayMs('MIXDOG_PROVIDER_MODEL_WARMUP_DELAY_MS', 15_000, { min: 0, max: 120_000 });
1671
+ const channelStartDelayMs = envDelayMs('MIXDOG_CHANNEL_START_DELAY_MS', 10_000, { min: 0, max: 120_000 });
1672
+ const backgroundBusyRetryMs = envDelayMs('MIXDOG_BACKGROUND_BUSY_RETRY_MS', 1_000, { min: 50, max: 10_000 });
1673
+ const modelMetaByRoute = new Map();
1674
+ const notificationListeners = new Set();
1675
+ let providerModelsCache = { models: null, at: 0 };
1676
+ let providerModelsPromise = null;
1677
+ let providerSetupCache = { setup: null, at: 0 };
1678
+ let providerSetupPromise = null;
1679
+ let providerInitPromise = null;
1680
+ const PROVIDER_SETUP_CACHE_TTL_MS = 10_000;
1681
+ let mcpFailures = [];
1682
+ let preSessionToolSurface = null;
1683
+ let contextStatusCacheKey = null;
1684
+ let contextStatusCacheValue = null;
1685
+ const hooksStartedAt = performance.now();
1686
+ const hooks = createStandaloneHookBus({ dataDir: cfgMod.getPluginData() });
1687
+ hooks.emit('runtime:start', { cwd: currentCwd, provider: route.provider, model: route.model, toolMode: mode });
1688
+ bootProfile('hooks:ready', { ms: (performance.now() - hooksStartedAt).toFixed(1) });
1689
+
1690
+ function contextContentLength(content) {
1691
+ if (typeof content === 'string') return content.length;
1692
+ if (!Array.isArray(content)) {
1693
+ try { return JSON.stringify(content ?? '').length; } catch { return String(content ?? '').length; }
1694
+ }
1695
+ let length = 0;
1696
+ for (const part of content) {
1697
+ if (typeof part === 'string') length += part.length;
1698
+ else if (typeof part?.text === 'string') length += part.text.length;
1699
+ else {
1700
+ try { length += JSON.stringify(part ?? '').length; } catch { length += String(part ?? '').length; }
1701
+ }
1702
+ }
1703
+ return length;
1704
+ }
1705
+
1706
+ function sameContextStatusKey(a, b) {
1707
+ if (!a || !b || a.length !== b.length) return false;
1708
+ for (let i = 0; i < a.length; i++) {
1709
+ if (!Object.is(a[i], b[i])) return false;
1710
+ }
1711
+ return true;
1712
+ }
1713
+
1714
+ function buildContextStatusCacheKey(messages, tools, bridgeMode) {
1715
+ const lastMessage = messages[messages.length - 1] || null;
1716
+ const compaction = session?.compaction || null;
1717
+ return [
1718
+ session?.id || null,
1719
+ route.provider,
1720
+ route.model,
1721
+ currentCwd,
1722
+ mode,
1723
+ bridgeMode,
1724
+ messages,
1725
+ messages.length,
1726
+ lastMessage,
1727
+ lastMessage?.role || null,
1728
+ lastMessage?.content || null,
1729
+ contextContentLength(lastMessage?.content),
1730
+ Array.isArray(lastMessage?.toolCalls) ? lastMessage.toolCalls.length : 0,
1731
+ tools,
1732
+ tools.length,
1733
+ session?.contextWindow || null,
1734
+ session?.rawContextWindow || null,
1735
+ session?.effectiveContextWindowPercent || null,
1736
+ session?.lastContextTokens || 0,
1737
+ session?.lastContextTokensUpdatedAt || 0,
1738
+ session?.lastContextTokensStaleAfterCompact === true,
1739
+ session?.lastInputTokens || 0,
1740
+ session?.lastOutputTokens || 0,
1741
+ session?.lastCachedReadTokens || 0,
1742
+ session?.lastCacheWriteTokens || 0,
1743
+ session?.totalInputTokens || 0,
1744
+ session?.totalOutputTokens || 0,
1745
+ session?.totalCachedReadTokens || 0,
1746
+ session?.totalCacheWriteTokens || 0,
1747
+ session?.compactBoundaryTokens || 0,
1748
+ compaction,
1749
+ compaction?.lastChangedAt || 0,
1750
+ compaction?.lastCompactAt || 0,
1751
+ compaction?.boundaryTokens || 0,
1752
+ compaction?.triggerTokens || 0,
1753
+ ];
1754
+ }
1755
+
1756
+ function mcpTransportLabel(cfg = {}) {
1757
+ if (cfg.autoDetect) return `autoDetect:${cfg.autoDetect}`;
1758
+ if (cfg.transport === 'http' || cfg.url) return 'http';
1759
+ if (cfg.command) return 'stdio';
1760
+ return 'unknown';
1761
+ }
1762
+
1763
+ function emitRuntimeNotification(content, meta = {}) {
1764
+ const text = String(content || '').trim();
1765
+ if (!text) return;
1766
+ const event = { content: text, meta: meta && typeof meta === 'object' ? meta : {} };
1767
+ for (const listener of [...notificationListeners]) {
1768
+ try { listener(event); } catch {}
1769
+ }
1770
+ }
1771
+
1772
+ function notifyFnForSession(callerSessionId) {
1773
+ return (text, meta = {}) => {
1774
+ const hadRuntimeListener = notificationListeners.size > 0;
1775
+ emitRuntimeNotification(text, meta);
1776
+ // TUI sessions keep their own Claude-Code-style command queue via
1777
+ // onNotification. Headless/model-tool callers have no listener, so fall
1778
+ // back to the session manager pending-message queue.
1779
+ if (!hadRuntimeListener && callerSessionId && typeof mgr.enqueuePendingMessage === 'function') {
1780
+ try { mgr.enqueuePendingMessage(callerSessionId, String(text || '')); } catch {}
1781
+ }
1782
+ };
1783
+ }
1784
+
1785
+ function mcpStatus() {
1786
+ const configured = config?.mcpServers && typeof config.mcpServers === 'object'
1787
+ ? config.mcpServers
1788
+ : {};
1789
+ const connected = new Map((mcpClient.getMcpServerStatus?.() || []).map((row) => [row.name, row]));
1790
+ const failures = new Map((mcpFailures || []).map((row) => [row.name, row]));
1791
+ const servers = [];
1792
+ for (const [name, cfg] of Object.entries(configured)) {
1793
+ const live = connected.get(name);
1794
+ const fail = failures.get(name);
1795
+ servers.push({
1796
+ name,
1797
+ configured: true,
1798
+ enabled: cfg?.enabled !== false,
1799
+ connected: Boolean(live),
1800
+ status: cfg?.enabled === false ? 'disabled' : live ? 'connected' : fail ? 'failed' : 'disconnected',
1801
+ transport: mcpTransportLabel(cfg),
1802
+ toolCount: live?.toolCount || 0,
1803
+ tools: live?.tools || [],
1804
+ error: fail?.msg || null,
1805
+ });
1806
+ connected.delete(name);
1807
+ }
1808
+ for (const live of connected.values()) {
1809
+ servers.push({ ...live, configured: false, status: 'connected' });
1810
+ }
1811
+ servers.sort((a, b) => String(a.name).localeCompare(String(b.name)));
1812
+ return {
1813
+ servers,
1814
+ configuredCount: Object.keys(configured).length,
1815
+ connectedCount: servers.filter((row) => row.connected).length,
1816
+ failedCount: servers.filter((row) => row.status === 'failed').length,
1817
+ };
1818
+ }
1819
+
1820
+ function skillsStatus() {
1821
+ const skills = typeof contextMod.collectSkillsCached === 'function'
1822
+ ? contextMod.collectSkillsCached(currentCwd)
1823
+ : [];
1824
+ const norm = (value) => String(value || '').replace(/\\/g, '/').toLowerCase();
1825
+ const cwdNorm = norm(currentCwd);
1826
+ const sourceForSkill = (filePath) => {
1827
+ const path = norm(filePath);
1828
+ if (cwdNorm && path.startsWith(`${cwdNorm}/.mixdog/skills/`)) return 'project';
1829
+ return 'skill';
1830
+ };
1831
+ return {
1832
+ cwd: currentCwd,
1833
+ count: skills.length,
1834
+ skills: skills.map((skill) => ({
1835
+ name: skill.name,
1836
+ description: skill.description || '',
1837
+ filePath: skill.filePath || null,
1838
+ source: sourceForSkill(skill.filePath),
1839
+ })),
1840
+ };
1841
+ }
1842
+
1843
+ function applyResolvedCwd(nextCwd, { markRefresh = true } = {}) {
1844
+ const resolved = resolve(nextCwd);
1845
+ const stat = statSync(resolved);
1846
+ if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${resolved}`);
1847
+ const changed = resolve(currentCwd) !== resolved;
1848
+ currentCwd = resolved;
1849
+ ensureProjectMixdogMd({ cwd: currentCwd });
1850
+ process.env.MIXDOG_SESSION_CWD = currentCwd;
1851
+ writeLastSessionCwd(currentCwd);
1852
+ if (session) session.cwd = currentCwd;
1853
+ if (changed && markRefresh && session?.id) sessionNeedsCwdRefresh = true;
1854
+ return currentCwd;
1855
+ }
1856
+
1857
+ async function refreshSessionForCwdIfNeeded(reason = 'cwd-change') {
1858
+ if (!session?.id || !sessionNeedsCwdRefresh) return session;
1859
+ const previousId = session.id;
1860
+ statusRoutes?.clearGatewaySessionRoute?.(previousId);
1861
+ mgr.closeSession(previousId, reason);
1862
+ session = null;
1863
+ sessionNeedsCwdRefresh = false;
1864
+ return await createCurrentSession();
1865
+ }
1866
+
1867
+ function buildWorkspaceContext() {
1868
+ try {
1869
+ return formatWorkspaceSessionContext(workspaceRouter.snapshot(currentCwd));
1870
+ } catch (error) {
1871
+ return [
1872
+ '# Workspace',
1873
+ `current cwd: ${currentCwd}`,
1874
+ `project candidates: unavailable (${error?.message || String(error)})`,
1875
+ ].join('\n');
1876
+ }
1877
+ }
1878
+
1879
+ function skillContent(name) {
1880
+ const content = typeof contextMod.loadSkillContent === 'function'
1881
+ ? contextMod.loadSkillContent(name, currentCwd)
1882
+ : null;
1883
+ if (!content) throw new Error(`skill not found: ${name}`);
1884
+ return { name, content };
1885
+ }
1886
+
1887
+ function addProjectSkill(input = {}) {
1888
+ const name = clean(input.name).replace(/[^a-zA-Z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
1889
+ if (!name) throw new Error('skill name is required');
1890
+ const dir = join(currentCwd, '.mixdog', 'skills', name);
1891
+ const filePath = join(dir, 'SKILL.md');
1892
+ if (existsSync(filePath)) throw new Error(`skill already exists: ${name}`);
1893
+ const description = clean(input.description) || 'Project skill.';
1894
+ mkdirSync(dir, { recursive: true });
1895
+ writeFileSync(filePath, [
1896
+ '---',
1897
+ `name: ${name}`,
1898
+ `description: ${description}`,
1899
+ '---',
1900
+ '',
1901
+ '# Instructions',
1902
+ '',
1903
+ 'Describe when and how to use this skill.',
1904
+ '',
1905
+ ].join('\n'), 'utf8');
1906
+ return { name, filePath };
1907
+ }
1908
+
1909
+ function pluginsStatus() {
1910
+ const dataDir = cfgMod.getPluginData?.();
1911
+ const configuredMcp = config?.mcpServers && typeof config.mcpServers === 'object'
1912
+ ? config.mcpServers
1913
+ : {};
1914
+ const plugins = [];
1915
+ const addRegisteredPlugin = (entry) => {
1916
+ const root = clean(entry.root);
1917
+ if (!root || !existsSync(root)) return;
1918
+ const manifest = pluginManifest(root);
1919
+ const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
1920
+ const plugin = {
1921
+ id: clean(entry.id) || name,
1922
+ name,
1923
+ title: clean(manifest.title) || clean(manifest.displayName) || clean(entry.title) || name,
1924
+ version: clean(manifest.version) || clean(entry.version) || null,
1925
+ description: clean(manifest.description) || clean(entry.description),
1926
+ marketplace: null,
1927
+ source: clean(entry.sourceType) === 'local' ? 'local' : 'registry',
1928
+ sourceUrl: clean(entry.source),
1929
+ sourceType: clean(entry.sourceType) || 'git',
1930
+ managed: entry.managed !== false,
1931
+ root,
1932
+ installedAt: entry.installedAt || null,
1933
+ updatedAt: entry.updatedAt || null,
1934
+ skillCount: countSkillFiles(root),
1935
+ mcpScript: mcpScriptForPlugin(root),
1936
+ };
1937
+ plugin.mcpServerName = pluginMcpServerName(plugin);
1938
+ plugin.mcpEnabled = Object.prototype.hasOwnProperty.call(configuredMcp, plugin.mcpServerName);
1939
+ plugins.push(plugin);
1940
+ };
1941
+
1942
+ for (const entry of listRegisteredPlugins({ dataDir })) addRegisteredPlugin(entry);
1943
+
1944
+ plugins.sort((a, b) => {
1945
+ if (a.source !== b.source) return a.source.localeCompare(b.source);
1946
+ return a.name.localeCompare(b.name);
1947
+ });
1948
+ const admin = pluginAdminStatus({ dataDir });
1949
+ return {
1950
+ count: plugins.length,
1951
+ plugins,
1952
+ roots: {
1953
+ registry: admin.registryPath,
1954
+ installed: admin.installRoot,
1955
+ },
1956
+ };
1957
+ }
1958
+
1959
+ async function connectConfiguredMcp({ reset = false } = {}) {
1960
+ if (reset) await mcpClient.disconnectAll?.();
1961
+ mcpFailures = [];
1962
+ const servers = config?.mcpServers && typeof config.mcpServers === 'object'
1963
+ ? config.mcpServers
1964
+ : {};
1965
+ if (Object.keys(servers).length === 0) return mcpStatus();
1966
+ try {
1967
+ await mcpClient.connectMcpServers(servers);
1968
+ } catch (error) {
1969
+ mcpFailures = Array.isArray(error?.failures)
1970
+ ? error.failures
1971
+ : [{ name: 'mcp', msg: error?.message || String(error) }];
1972
+ }
1973
+ return mcpStatus();
1974
+ }
1975
+
1976
+ function normalizeMcpServerInput(input = {}) {
1977
+ const name = clean(input.name).toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
1978
+ if (!name) throw new Error('MCP server name is required');
1979
+ const url = clean(input.url);
1980
+ if (url) {
1981
+ if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
1982
+ return { name, config: { transport: 'http', url } };
1983
+ }
1984
+ const command = clean(input.command);
1985
+ if (!command) throw new Error('MCP server command or URL is required');
1986
+ const args = Array.isArray(input.args)
1987
+ ? input.args.map((v) => String(v)).filter(Boolean)
1988
+ : clean(input.args).split(/\s+/).filter(Boolean);
1989
+ const requestedCwd = clean(input.cwd);
1990
+ const cwdForServer = requestedCwd ? resolve(currentCwd, requestedCwd) : currentCwd;
1991
+ const root = resolve(currentCwd);
1992
+ const resolvedCwd = resolve(cwdForServer);
1993
+ if (resolvedCwd !== root && !resolvedCwd.startsWith(`${root}\\`) && !resolvedCwd.startsWith(`${root}/`)) {
1994
+ throw new Error('MCP server cwd must stay under the current project');
1995
+ }
1996
+ return { name, config: { command, args, cwd: resolvedCwd } };
1997
+ }
1998
+
1999
+ const persistedBridgeMode = (() => {
2000
+ try { return (sharedCfgMod.readSection('agent') || {}).bridgeMode; } catch { return undefined; }
2001
+ })();
2002
+ const bridgeStartedAt = performance.now();
2003
+ const bridge = createStandaloneBridge({
2004
+ cfgMod,
2005
+ reg,
2006
+ mgr,
2007
+ dataDir: cfgMod.getPluginData(),
2008
+ cwd,
2009
+ defaultMode: persistedBridgeMode ?? 'async',
2010
+ });
2011
+ bootProfile('bridge:ready', { ms: (performance.now() - bridgeStartedAt).toFixed(1) });
2012
+ const bridgeStatusState = () => {
2013
+ try {
2014
+ const status = bridge.getStatus?.({ clientHostPid: session?.clientHostPid || process.pid }) || {};
2015
+ return {
2016
+ bridgeMode: bridge.getDefaultMode?.() || status.bridgeMode || 'async',
2017
+ bridgeWorkers: Array.isArray(status.workers) ? status.workers : [],
2018
+ bridgeJobs: Array.isArray(status.jobs) ? status.jobs : [],
2019
+ bridgeScope: status.scope || null,
2020
+ };
2021
+ } catch {
2022
+ return { bridgeMode: bridge.getDefaultMode?.() || 'async', bridgeWorkers: [], bridgeJobs: [], bridgeScope: null };
2023
+ }
2024
+ };
2025
+ const channelsStartedAt = performance.now();
2026
+ const channels = createStandaloneChannelWorker({
2027
+ entry: join(STANDALONE_ROOT, CHANNEL_WORKER_ENTRY.replace(/^\.\//, '')),
2028
+ rootDir: STANDALONE_ROOT,
2029
+ dataDir: cfgMod.getPluginData(),
2030
+ cwd,
2031
+ });
2032
+ bootProfile('channels:worker-ready', { ms: (performance.now() - channelsStartedAt).toFixed(1) });
2033
+ const toolsStartedAt = performance.now();
2034
+ const standaloneTools = [
2035
+ TOOL_SEARCH_TOOL,
2036
+ CWD_TOOL,
2037
+ EXPLORE_TOOL,
2038
+ ...(searchToolDefs?.TOOL_DEFS || []).filter((tool) => tool?.name === 'search' || tool?.name === 'web_fetch'),
2039
+ ...(memoryToolDefs?.TOOL_DEFS || []).filter((tool) => tool?.name === 'recall' || tool?.name === 'memory'),
2040
+ ...(channelToolDefs?.TOOL_DEFS || []).filter((tool) => channels.isChannelTool(tool?.name)),
2041
+ ...(codeGraphToolDefs?.CODE_GRAPH_TOOL_DEFS || []).filter((tool) => tool?.name === 'code_graph'),
2042
+ ...bridge.tools,
2043
+ PROVIDER_STATUS_TOOL,
2044
+ CHANNEL_STATUS_TOOL,
2045
+ ].map(applyStandaloneToolDefaults);
2046
+ bootProfile('tools:ready', { ms: (performance.now() - toolsStartedAt).toFixed(1), count: standaloneTools.length });
2047
+
2048
+ function invalidatePreSessionToolSurface() {
2049
+ preSessionToolSurface = null;
2050
+ }
2051
+
2052
+ function invalidateContextStatusCache() {
2053
+ contextStatusCacheKey = null;
2054
+ contextStatusCacheValue = null;
2055
+ }
2056
+
2057
+ function buildPreSessionToolSurface() {
2058
+ const previewTools = typeof mgr.previewSessionTools === 'function'
2059
+ ? mgr.previewSessionTools(toolSpecForMode(mode), [])
2060
+ : [];
2061
+ const tools = filterDisallowedTools(previewTools, LEAD_DISALLOWED_TOOLS);
2062
+ const surface = { tools: Array.isArray(tools) ? tools.slice() : [] };
2063
+ applyDeferredToolSurface(surface, mode, standaloneTools);
2064
+ return surface;
2065
+ }
2066
+
2067
+ function activeToolSurface() {
2068
+ if (session) return session;
2069
+ preSessionToolSurface ??= buildPreSessionToolSurface();
2070
+ return preSessionToolSurface;
2071
+ }
2072
+
2073
+ function applyPreSessionToolSelection() {
2074
+ if (!session || !preSessionToolSurface) return;
2075
+ const selected = Array.isArray(preSessionToolSurface.deferredSelectedTools)
2076
+ ? preSessionToolSurface.deferredSelectedTools
2077
+ : [];
2078
+ if (selected.length) selectDeferredTools(session, selected, mode);
2079
+ }
2080
+ internalTools.setInternalToolsProvider({
2081
+ tools: standaloneTools,
2082
+ executor: async (name, args, callerCtx = {}) => {
2083
+ const callerCwd = callerCtx?.callerCwd || currentCwd;
2084
+ if (name === 'search' || name === 'web_fetch') {
2085
+ const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
2086
+ const searchMod = await getSearchModule();
2087
+ if (!searchMod?.handleToolCall) throw new Error('search runtime is not available');
2088
+ return await searchMod.handleToolCall(name, args || {}, {
2089
+ callerCwd,
2090
+ callerSessionId,
2091
+ routingSessionId: callerSessionId,
2092
+ clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
2093
+ notifyFn: notifyFnForSession(callerSessionId),
2094
+ agentSearch: name === 'search'
2095
+ ? async (searchArgs) => {
2096
+ const query = Array.isArray(searchArgs.keywords)
2097
+ ? searchArgs.keywords.join('\n')
2098
+ : String(searchArgs.keywords || searchArgs.query || '');
2099
+ const prompt = searchArgs.prompt || [
2100
+ 'Perform a concise web research task for Mixdog search.',
2101
+ '',
2102
+ `Query: ${query}`,
2103
+ searchArgs.site ? `Site/domain restriction: ${searchArgs.site}` : null,
2104
+ searchArgs.type ? `Search type: ${searchArgs.type}` : null,
2105
+ searchArgs.locale ? `Locale: ${typeof searchArgs.locale === 'string' ? searchArgs.locale : JSON.stringify(searchArgs.locale)}` : null,
2106
+ `Max results: ${Math.max(1, Math.min(20, Number(searchArgs.maxResults) || 10))}`,
2107
+ '',
2108
+ 'Return a short answer first, then cite useful results as title + URL + one-line snippet.',
2109
+ 'Do not edit files.',
2110
+ ].filter(Boolean).join('\n');
2111
+ const rendered = await bridge.execute({
2112
+ type: 'spawn',
2113
+ agent: 'web-researcher',
2114
+ tag: `search_${Date.now().toString(36)}`,
2115
+ prompt,
2116
+ cwd: callerCwd,
2117
+ wait: true,
2118
+ firstResponseTimeoutMs: Number.isFinite(Number(searchArgs.firstResponseTimeoutMs)) ? Number(searchArgs.firstResponseTimeoutMs) : 120_000,
2119
+ idleTimeoutMs: Number.isFinite(Number(searchArgs.idleTimeoutMs)) ? Number(searchArgs.idleTimeoutMs) : 30 * 60_000,
2120
+ }, { invocationSource: 'user-command', callerCwd });
2121
+ return String(rendered || '').replace(/^bridge result[^\n]*\n/i, '').trim();
2122
+ }
2123
+ : undefined,
2124
+ });
2125
+ }
2126
+ if (name === 'recall' || name === 'memory' || name === 'search_memories') {
2127
+ const memoryMod = await getMemoryModule();
2128
+ if (!memoryMod?.handleToolCall) throw new Error('memory runtime is not available');
2129
+ return await memoryMod.handleToolCall(name, args || {});
2130
+ }
2131
+ if (name === 'code_graph') {
2132
+ const codeGraphMod = await getCodeGraphModule();
2133
+ if (!codeGraphMod?.executeCodeGraphTool) throw new Error('code_graph runtime is not available');
2134
+ return await codeGraphMod.executeCodeGraphTool(name, args || {}, args?.cwd || callerCwd);
2135
+ }
2136
+ if (name === 'tool_search') return renderToolSearch(args, session, mode);
2137
+ if (name === 'explore') {
2138
+ const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
2139
+ return await runExplore(args || {}, {
2140
+ callerCwd: args?.cwd ? resolveCwdPath(currentCwd, args.cwd) : callerCwd,
2141
+ callerSessionId,
2142
+ routingSessionId: callerSessionId,
2143
+ clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
2144
+ notifyFn: notifyFnForSession(callerSessionId),
2145
+ });
2146
+ }
2147
+ if (name === 'cwd') {
2148
+ const action = clean(args?.action || (args?.path ? 'set' : 'get')).toLowerCase();
2149
+ if (action === 'set') {
2150
+ applyResolvedCwd(resolveCwdPath(currentCwd, args?.path));
2151
+ } else if (action !== 'get') {
2152
+ throw new Error(`cwd: unknown action "${action}"`);
2153
+ }
2154
+ return JSON.stringify({ cwd: currentCwd, sessionId: session?.id || null }, null, 2);
2155
+ }
2156
+ if (name === 'bridge') {
2157
+ const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
2158
+ return await bridge.execute(args, {
2159
+ callerCwd,
2160
+ invocationSource: 'model-tool',
2161
+ callerSessionId,
2162
+ clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
2163
+ signal: callerCtx?.signal,
2164
+ notifyFn: notifyFnForSession(callerSessionId),
2165
+ });
2166
+ }
2167
+ if (name === 'provider_status') return renderProviderStatus(cfgMod.loadConfig());
2168
+ if (name === 'channel_status') return renderChannelStatus();
2169
+ if (channels.isChannelTool(name)) return await channels.execute(name, args || {});
2170
+ throw new Error(`unknown standalone internal tool: ${name}`);
2171
+ },
2172
+ });
2173
+ internalTools.markBootReady?.();
2174
+ void connectConfiguredMcp()
2175
+ .then((status) => bootProfile('mcp:ready', {
2176
+ connected: Number(status?.connectedCount || 0),
2177
+ failed: Number(status?.failedCount || 0),
2178
+ }))
2179
+ .catch((error) => bootProfile('mcp:failed', { error: error?.message || String(error) }));
2180
+
2181
+ function reloadChannelsSoon() {
2182
+ channels.execute('reload_config', {}).catch(() => {});
2183
+ }
2184
+
2185
+ function invalidateProviderCaches() {
2186
+ providerModelsCache = { models: null, at: 0 };
2187
+ providerModelsPromise = null;
2188
+ providerSetupCache = { setup: null, at: 0 };
2189
+ providerSetupPromise = null;
2190
+ providerInitPromise = null;
2191
+ modelMetaByRoute.clear();
2192
+ }
2193
+
2194
+ function ensureFullConfig() {
2195
+ if (configHasSecrets) return config;
2196
+ config = cfgMod.loadConfig();
2197
+ configHasSecrets = true;
2198
+ return config;
2199
+ }
2200
+
2201
+ function ensureConfigForRouteProvider() {
2202
+ const providerId = clean(route.provider);
2203
+ const providerCfg = config?.providers?.[providerId];
2204
+ if (configHasSecrets || LAZY_SECRET_PROVIDERS.has(providerId) || providerCfg?.apiKey) {
2205
+ return config;
2206
+ }
2207
+ return ensureFullConfig();
2208
+ }
2209
+
2210
+ async function ensureProvidersReady(providerConfig = config.providers || {}) {
2211
+ if (providerInitPromise) return await providerInitPromise;
2212
+ providerInitPromise = reg.initProviders(providerConfig)
2213
+ .finally(() => {
2214
+ providerInitPromise = null;
2215
+ });
2216
+ return await providerInitPromise;
2217
+ }
2218
+
2219
+ async function cachedProviderSetup({ force = false } = {}) {
2220
+ const now = Date.now();
2221
+ if (!force && providerSetupCache.setup && now - providerSetupCache.at < PROVIDER_SETUP_CACHE_TTL_MS) {
2222
+ return providerSetupCache.setup;
2223
+ }
2224
+ if (!force && providerSetupPromise) return await providerSetupPromise;
2225
+ providerSetupPromise = providerSetup(cfgMod.loadConfig())
2226
+ .then((setup) => {
2227
+ providerSetupCache = { setup, at: Date.now() };
2228
+ return setup;
2229
+ })
2230
+ .finally(() => {
2231
+ providerSetupPromise = null;
2232
+ });
2233
+ return await providerSetupPromise;
2234
+ }
2235
+
2236
+ function modelMetaKey(providerId, modelId) {
2237
+ return `${clean(providerId)}\n${clean(modelId)}`;
2238
+ }
2239
+
2240
+ async function lookupModelMeta(providerId, modelId, { allowFetch = false } = {}) {
2241
+ const key = modelMetaKey(providerId, modelId);
2242
+ if (modelMetaByRoute.has(key)) return modelMetaByRoute.get(key);
2243
+ const providerImpl = reg.getProvider(providerId);
2244
+ if (!providerImpl || typeof providerImpl.listModels !== 'function') {
2245
+ const fallback = { id: modelId, provider: providerId };
2246
+ modelMetaByRoute.set(key, fallback);
2247
+ return fallback;
2248
+ }
2249
+ if (typeof providerImpl.getCachedModelInfo === 'function') {
2250
+ const cached = providerImpl.getCachedModelInfo(modelId);
2251
+ if (cached) {
2252
+ const meta = { ...cached, id: cached.id || modelId, provider: providerId };
2253
+ modelMetaByRoute.set(key, meta);
2254
+ return meta;
2255
+ }
2256
+ }
2257
+ if (!allowFetch) {
2258
+ const fallback = { id: modelId, provider: providerId };
2259
+ modelMetaByRoute.set(key, fallback);
2260
+ scheduleProviderModelWarmup();
2261
+ return fallback;
2262
+ }
2263
+ try {
2264
+ const models = await providerImpl.listModels();
2265
+ const found = Array.isArray(models) ? models.find((m) => m?.id === modelId) : null;
2266
+ const meta = found || { id: modelId, provider: providerId };
2267
+ modelMetaByRoute.set(key, meta);
2268
+ return meta;
2269
+ } catch {
2270
+ const fallback = { id: modelId, provider: providerId };
2271
+ modelMetaByRoute.set(key, fallback);
2272
+ return fallback;
2273
+ }
2274
+ }
2275
+
2276
+ function parsedProviderModelVersion(id) {
2277
+ const text = clean(id).toLowerCase();
2278
+ const claude = text.match(/^claude-[a-z]+-(\d+)(?:[-.](\d+))?/);
2279
+ if (claude) return [Number(claude[1]) || 0, Number(claude[2]) || 0];
2280
+ const compact = text.match(/(?:^|[-_])(?:o|gpt|grok|qwen|llama|mistral|gemma|phi|glm)(\d+)(?:\.(\d+))?(?:\.(\d{1,3}))?/);
2281
+ if (compact) return compact.slice(1).filter((v) => v != null).map((v) => Number(v) || 0);
2282
+ const generic = text.match(/(?:^|[-_v])(\d+)(?:\.(\d+))?(?:\.(\d{1,3}))?/);
2283
+ return generic ? generic.slice(1).filter((v) => v != null).map((v) => Number(v) || 0) : [];
2284
+ }
2285
+
2286
+ function compareProviderModelVersion(a, b) {
2287
+ const va = parsedProviderModelVersion(a.id || a.display || a.name);
2288
+ const vb = parsedProviderModelVersion(b.id || b.display || b.name);
2289
+ if (va.length === 0 && vb.length === 0) return 0;
2290
+ if (va.length === 0) return 1;
2291
+ if (vb.length === 0) return -1;
2292
+ for (let i = 0; i < Math.max(va.length, vb.length); i += 1) {
2293
+ const delta = (vb[i] || 0) - (va[i] || 0);
2294
+ if (delta) return delta;
2295
+ }
2296
+ return 0;
2297
+ }
2298
+
2299
+ function providerModelReleaseTime(model) {
2300
+ if (model?.releaseDate) {
2301
+ const t = Date.parse(model.releaseDate);
2302
+ if (Number.isFinite(t)) return t;
2303
+ }
2304
+ const created = Number(model?.created);
2305
+ if (Number.isFinite(created) && created > 0) {
2306
+ return created < 1_000_000_000_000 ? created * 1000 : created;
2307
+ }
2308
+ const dated = clean(model?.id).match(/(?:^|-)(\d{4})(\d{2})(\d{2})(?:$|-)/);
2309
+ return dated ? (Date.parse(`${dated[1]}-${dated[2]}-${dated[3]}`) || 0) : 0;
2310
+ }
2311
+
2312
+ function isClaudeProviderModel(model) {
2313
+ return clean(model?.provider).toLowerCase().includes('anthropic')
2314
+ && /^claude-[a-z]+-/.test(clean(model?.id).toLowerCase());
2315
+ }
2316
+
2317
+ function compareProviderModelRecency(a, b) {
2318
+ if (isClaudeProviderModel(a) && isClaudeProviderModel(b)) {
2319
+ if (a.latest !== b.latest) return a.latest ? -1 : 1;
2320
+ const versionDelta = compareProviderModelVersion(a, b);
2321
+ if (versionDelta) return versionDelta;
2322
+ const ta = providerModelReleaseTime(a);
2323
+ const tb = providerModelReleaseTime(b);
2324
+ if (ta !== tb) return tb - ta;
2325
+ return clean(a.display || a.id).localeCompare(clean(b.display || b.id));
2326
+ }
2327
+ const ta = providerModelReleaseTime(a);
2328
+ const tb = providerModelReleaseTime(b);
2329
+ if (ta !== tb) return tb - ta;
2330
+ if (a.latest !== b.latest) return a.latest ? -1 : 1;
2331
+ const versionDelta = compareProviderModelVersion(a, b);
2332
+ if (versionDelta) return versionDelta;
2333
+ return clean(a.display || a.id).localeCompare(clean(b.display || b.id));
2334
+ }
2335
+
2336
+ function sortProviderModels(models) {
2337
+ return (models || []).sort((a, b) => {
2338
+ const ar = a.provider === route.provider ? 0 : 1;
2339
+ const br = b.provider === route.provider ? 0 : 1;
2340
+ if (ar !== br) return ar - br;
2341
+ if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);
2342
+ return compareProviderModelRecency(a, b);
2343
+ });
2344
+ }
2345
+
2346
+ function isSelectableLlmModel(model) {
2347
+ const id = clean(model?.id).toLowerCase();
2348
+ const display = clean(model?.display || model?.name).toLowerCase();
2349
+ const mode = clean(model?.mode).toLowerCase();
2350
+ const text = `${id} ${display}`;
2351
+ if (!id) return false;
2352
+ if (mode && !['chat', 'completion', 'responses', 'messages'].includes(mode)) return false;
2353
+ if (/(^|[-_\s])(image|images|video|videos|audio|tts|stt|speech|embedding|embeddings|rerank|moderation|imagine)([-_\s]|$)/i.test(text)) return false;
2354
+ if (/(^|[-_\s])(dall[-_\s]?e|sora|imagen)([-_\s]|$)/i.test(text)) return false;
2355
+ return true;
2356
+ }
2357
+
2358
+ function providerModelCacheRow(name, m) {
2359
+ return {
2360
+ id: m.id,
2361
+ provider: name,
2362
+ display: m.display || m.name || m.id,
2363
+ created: typeof m.created === 'number' ? m.created : null,
2364
+ releaseDate: m.releaseDate || null,
2365
+ contextWindow: m.contextWindow,
2366
+ outputTokens: m.outputTokens || null,
2367
+ family: m.family || null,
2368
+ tier: m.tier || null,
2369
+ latest: m.latest === true,
2370
+ description: m.description || '',
2371
+ supportsVision: m.supportsVision === true,
2372
+ supportsFunctionCalling: m.supportsFunctionCalling === true,
2373
+ supportsPromptCaching: m.supportsPromptCaching === true,
2374
+ supportsReasoning: m.supportsReasoning === true,
2375
+ reasoningLevels: Array.isArray(m.reasoningLevels) ? m.reasoningLevels : [],
2376
+ reasoningOptions: Array.isArray(m.reasoningOptions) ? m.reasoningOptions : [],
2377
+ reasoningContentField: m.reasoningContentField || null,
2378
+ mode: m.mode || null,
2379
+ };
2380
+ }
2381
+
2382
+ function hydrateProviderModelRow(row) {
2383
+ return {
2384
+ ...row,
2385
+ effortOptions: effortItemsFor(row.provider, row, null),
2386
+ fastCapable: fastCapableFor(row.provider, row),
2387
+ fastPreferred: fastPreferenceFor(config, row.provider, row.id),
2388
+ savedEffort: modelSettingsFor(config, row.provider, row.id).effort || null,
2389
+ savedFast: modelSettingsFor(config, row.provider, row.id).fast === true,
2390
+ };
2391
+ }
2392
+
2393
+ function providerModelsFromCacheRows(rows) {
2394
+ return sortProviderModels((rows || []).map(hydrateProviderModelRow));
2395
+ }
2396
+
2397
+ async function loadProviderModelsFresh() {
2398
+ ensureFullConfig();
2399
+ await ensureProvidersReady(config.providers || {});
2400
+ const allProviders = reg.getAllProviders();
2401
+ const results = [];
2402
+ const seen = new Set();
2403
+ for (const [name, provider] of allProviders) {
2404
+ if (typeof provider?.listModels !== 'function') continue;
2405
+ try {
2406
+ const models = await provider.listModels();
2407
+ if (Array.isArray(models)) {
2408
+ for (const m of models) {
2409
+ if (!m?.id) continue;
2410
+ if (!isSelectableLlmModel(m)) continue;
2411
+ const key = `${name}:${m.id}`;
2412
+ if (seen.has(key)) continue;
2413
+ seen.add(key);
2414
+ const row = providerModelCacheRow(name, m);
2415
+ results.push(row);
2416
+ modelMetaByRoute.set(modelMetaKey(name, m.id), row);
2417
+ }
2418
+ }
2419
+ } catch {
2420
+ // Ignore per-provider catalog failures so one bad credential or
2421
+ // transient /models error does not hide other authenticated models.
2422
+ }
2423
+ }
2424
+ return results;
2425
+ }
2426
+
2427
+ async function collectProviderModels({ force = false } = {}) {
2428
+ if (!force && Array.isArray(providerModelsCache.models)) {
2429
+ return providerModelsFromCacheRows(providerModelsCache.models);
2430
+ }
2431
+ if (!providerModelsPromise) {
2432
+ providerModelsPromise = loadProviderModelsFresh()
2433
+ .then((models) => {
2434
+ providerModelsCache = { models, at: Date.now() };
2435
+ return models;
2436
+ })
2437
+ .finally(() => {
2438
+ providerModelsPromise = null;
2439
+ });
2440
+ }
2441
+ return providerModelsFromCacheRows(await providerModelsPromise);
2442
+ }
2443
+
2444
+ function warmProviderModelCache() {
2445
+ if (Array.isArray(providerModelsCache.models) || providerModelsPromise) return providerModelsPromise;
2446
+ providerModelsPromise = loadProviderModelsFresh()
2447
+ .then((models) => {
2448
+ providerModelsCache = { models, at: Date.now() };
2449
+ bootProfile('provider-models:warm-ready', { count: models.length });
2450
+ return models;
2451
+ })
2452
+ .catch((err) => {
2453
+ const msg = err instanceof Error ? err.message : String(err);
2454
+ bootProfile('provider-models:warm-failed', { error: msg });
2455
+ return [];
2456
+ })
2457
+ .finally(() => {
2458
+ providerModelsPromise = null;
2459
+ });
2460
+ return providerModelsPromise;
2461
+ }
2462
+
2463
+ async function resolveMissingRouteModelForFirstTurn() {
2464
+ if (routeHasModel()) return route;
2465
+ const models = await collectProviderModels();
2466
+ const picked = models[0] || null;
2467
+ if (!picked) {
2468
+ throw new Error('No provider models available. Run /providers to authenticate, then /model to choose a model.');
2469
+ }
2470
+ route = {
2471
+ ...route,
2472
+ provider: picked.provider,
2473
+ model: picked.id,
2474
+ preset: null,
2475
+ };
2476
+ return route;
2477
+ }
2478
+
2479
+ async function refreshRouteEffort(modelMetaOverride = null) {
2480
+ await ensureProvidersReady(ensureProviderEnabled(config, route.provider));
2481
+ const modelMeta = modelMetaOverride || await lookupModelMeta(route.provider, route.model);
2482
+ const requested = hasOwn(route, 'effort') ? route.effort : (route.preset?.effort || null);
2483
+ const effectiveEffort = coerceEffortFor(route.provider, modelMeta, requested);
2484
+ const fastCapable = fastCapableFor(route.provider, modelMeta);
2485
+ route = {
2486
+ ...route,
2487
+ fast: fastCapable ? route.fast === true : false,
2488
+ fastCapable,
2489
+ effectiveEffort,
2490
+ effortOptions: effortItemsFor(route.provider, modelMeta, effectiveEffort),
2491
+ };
2492
+ return route;
2493
+ }
2494
+
2495
+ function routeHasModel() {
2496
+ return !!clean(route?.model);
2497
+ }
2498
+
2499
+ function requireModelRoute() {
2500
+ if (routeHasModel()) return;
2501
+ throw new Error('No model configured. Run /providers to authenticate, then /model to choose a model.');
2502
+ }
2503
+
2504
+ async function recreateCurrentSessionIfReady() {
2505
+ if (!routeHasModel()) {
2506
+ session = null;
2507
+ return null;
2508
+ }
2509
+ return await createCurrentSession();
2510
+ }
2511
+
2512
+ async function createCurrentSession(reason = 'demand') {
2513
+ if (sessionCreatePromise) return await sessionCreatePromise;
2514
+ if (session?.id && !sessionNeedsCwdRefresh) {
2515
+ const liveSession = mgr.getSession(session.id);
2516
+ if (liveSession && liveSession.closed !== true && liveSession.status !== 'closed') {
2517
+ session = liveSession;
2518
+ return session;
2519
+ }
2520
+ session = null;
2521
+ }
2522
+
2523
+ const startedAt = performance.now();
2524
+ bootProfile('session:create:start', { mode, reason });
2525
+ const promise = (async () => {
2526
+ ensureConfigForRouteProvider();
2527
+ await resolveMissingRouteModelForFirstTurn();
2528
+ requireModelRoute();
2529
+ bootProfile('session:create:route-ready', { ms: (performance.now() - startedAt).toFixed(1) });
2530
+ await refreshRouteEffort();
2531
+ bootProfile('session:create:effort-ready', { ms: (performance.now() - startedAt).toFixed(1) });
2532
+ const providerImpl = reg.getProvider(route.provider);
2533
+ if (!providerImpl) {
2534
+ throw new Error(`Provider "${route.provider}" is not configured.`);
2535
+ }
2536
+ bootProfile('session:create:provider-ready', { ms: (performance.now() - startedAt).toFixed(1) });
2537
+ const coreMemoryContext = await loadCoreMemoryContext();
2538
+ if (closeRequested) throw new Error('runtime is closing');
2539
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
2540
+ const workflowContext = workflowContextBlock(config, dataDir);
2541
+ const workspaceContext = buildWorkspaceContext();
2542
+ const sessionOpts = {
2543
+ provider: route.provider,
2544
+ model: route.model,
2545
+ preset: route.preset || undefined,
2546
+ tools: toolSpecForMode(mode),
2547
+ owner: 'cli',
2548
+ role: 'lead',
2549
+ lane: 'cli',
2550
+ sourceType: 'lead',
2551
+ sourceName: 'main',
2552
+ clientHostPid: process.pid,
2553
+ disallowedTools: LEAD_DISALLOWED_TOOLS,
2554
+ cwd: currentCwd,
2555
+ coreMemoryContext,
2556
+ workflowContext,
2557
+ workspaceContext,
2558
+ fast: route.fast === true,
2559
+ };
2560
+ if (hasOwn(route, 'effort') || route.effectiveEffort) {
2561
+ sessionOpts.effort = route.effectiveEffort || null;
2562
+ }
2563
+ session = mgr.createSession(sessionOpts);
2564
+ sessionNeedsCwdRefresh = false;
2565
+ Object.defineProperty(session, 'beforeToolHook', {
2566
+ value: (input) => hooks.beforeTool(input),
2567
+ enumerable: false,
2568
+ configurable: true,
2569
+ writable: true,
2570
+ });
2571
+ applyDeferredToolSurface(session, mode, standaloneTools);
2572
+ applyPreSessionToolSelection();
2573
+ statusRoutes?.writeGatewaySessionRoute?.(session.id, routeForStatusline(route));
2574
+ hooks.emit('session:create', { sessionId: session.id, provider: route.provider, model: route.model, toolMode: mode, cwd: currentCwd });
2575
+ bootProfile('session:create:ready', {
2576
+ ms: (performance.now() - startedAt).toFixed(1),
2577
+ reason,
2578
+ tools: Array.isArray(session.tools) ? session.tools.length : 0,
2579
+ catalog: Array.isArray(session.deferredToolCatalog) ? session.deferredToolCatalog.length : 0,
2580
+ });
2581
+ return session;
2582
+ })();
2583
+
2584
+ sessionCreatePromise = promise;
2585
+ try {
2586
+ return await promise;
2587
+ } finally {
2588
+ if (sessionCreatePromise === promise) sessionCreatePromise = null;
2589
+ }
2590
+ }
2591
+
2592
+ function scheduleLeadSessionPrewarm() {
2593
+ if (envFlag('MIXDOG_DISABLE_SESSION_PREWARM')) return;
2594
+ const timer = setTimeout(() => {
2595
+ if (closeRequested || session?.id || sessionCreatePromise || activeTurnCount > 0) return;
2596
+ void createCurrentSession('prewarm')
2597
+ .then(() => bootProfile('session:prewarm:ready'))
2598
+ .catch((error) => bootProfile('session:prewarm:failed', { error: error?.message || String(error) }));
2599
+ }, sessionPrewarmDelayMs);
2600
+ timer.unref?.();
2601
+ }
2602
+
2603
+ function scheduleProviderWarmup(delayMs = providerWarmupDelayMs) {
2604
+ if (envFlag('MIXDOG_DISABLE_PROVIDER_WARMUP')) {
2605
+ bootProfile('providers:warm-skipped');
2606
+ return;
2607
+ }
2608
+ if (providerWarmupTimer || closeRequested) return;
2609
+ providerWarmupTimer = setTimeout(() => {
2610
+ providerWarmupTimer = null;
2611
+ if (closeRequested) return;
2612
+ if (activeTurnCount > 0 || sessionCreatePromise) {
2613
+ bootProfile('providers:warm-deferred', { reason: activeTurnCount > 0 ? 'turn-active' : 'session-create' });
2614
+ scheduleProviderWarmup(backgroundBusyRetryMs);
2615
+ return;
2616
+ }
2617
+ const providersStartedAt = performance.now();
2618
+ try {
2619
+ config = cfgMod.loadConfig();
2620
+ configHasSecrets = true;
2621
+ } catch (error) {
2622
+ bootProfile('config:full-failed', { error: error?.message || String(error) });
2623
+ }
2624
+ void ensureProvidersReady(config.providers || {})
2625
+ .then(() => {
2626
+ bootProfile('providers:init:ready', { ms: (performance.now() - providersStartedAt).toFixed(1) });
2627
+ if (closeRequested) return null;
2628
+ if (activeTurnCount > 0 || sessionCreatePromise) {
2629
+ bootProfile('providers:optional-warm-deferred', { reason: activeTurnCount > 0 ? 'turn-active' : 'session-create' });
2630
+ scheduleProviderWarmup(backgroundBusyRetryMs);
2631
+ return null;
2632
+ }
2633
+ return cachedProviderSetup();
2634
+ })
2635
+ .then((setup) => {
2636
+ if (!setup) return;
2637
+ bootProfile('provider-setup:warm-ready', {
2638
+ api: Array.isArray(setup?.api) ? setup.api.length : 0,
2639
+ oauth: Array.isArray(setup?.oauth) ? setup.oauth.length : 0,
2640
+ local: Array.isArray(setup?.local) ? setup.local.length : 0,
2641
+ });
2642
+ })
2643
+ .catch((error) => bootProfile('providers:warm-failed', { error: error?.message || String(error) }));
2644
+ }, delayMs);
2645
+ providerWarmupTimer.unref?.();
2646
+ }
2647
+
2648
+ function scheduleProviderModelWarmup(delayMs = providerModelWarmupDelayMs) {
2649
+ if (envFlag('MIXDOG_DISABLE_PROVIDER_WARMUP')) return;
2650
+ if (providerModelWarmupTimer || closeRequested) return;
2651
+ providerModelWarmupTimer = setTimeout(() => {
2652
+ providerModelWarmupTimer = null;
2653
+ if (closeRequested || Array.isArray(providerModelsCache.models) || providerModelsPromise) return;
2654
+ if (activeTurnCount > 0 || sessionCreatePromise) {
2655
+ bootProfile('provider-models:warm-deferred', { reason: activeTurnCount > 0 ? 'turn-active' : 'session-create' });
2656
+ scheduleProviderModelWarmup(backgroundBusyRetryMs);
2657
+ return;
2658
+ }
2659
+ warmProviderModelCache();
2660
+ }, delayMs);
2661
+ providerModelWarmupTimer.unref?.();
2662
+ }
2663
+
2664
+ function scheduleChannelStart(delayMs = channelStartDelayMs) {
2665
+ if (envFlag('MIXDOG_DISABLE_CHANNEL_START')) {
2666
+ bootProfile('channels:start-skipped');
2667
+ return;
2668
+ }
2669
+ if (channelStartTimer || closeRequested) return;
2670
+ bootProfile('channels:start-scheduled', { delayMs });
2671
+ channelStartTimer = setTimeout(() => {
2672
+ channelStartTimer = null;
2673
+ if (closeRequested) return;
2674
+ if (activeTurnCount > 0 || sessionCreatePromise) {
2675
+ bootProfile('channels:start-deferred', { reason: activeTurnCount > 0 ? 'turn-active' : 'session-create' });
2676
+ scheduleChannelStart(backgroundBusyRetryMs);
2677
+ return;
2678
+ }
2679
+ const startedAt = performance.now();
2680
+ bootProfile('channels:start:begin');
2681
+ channels.start()
2682
+ .then(() => bootProfile('channels:start:ready', { ms: (performance.now() - startedAt).toFixed(1) }))
2683
+ .catch((error) => bootProfile('channels:start:failed', {
2684
+ ms: (performance.now() - startedAt).toFixed(1),
2685
+ error: error?.message || String(error),
2686
+ }));
2687
+ }, delayMs);
2688
+ channelStartTimer.unref?.();
2689
+ }
2690
+
2691
+ function withTeardownDeadline(promise, ms, fallback = false) {
2692
+ let timer = null;
2693
+ return Promise.race([
2694
+ Promise.resolve(promise),
2695
+ new Promise((resolve) => {
2696
+ timer = setTimeout(() => resolve(fallback), ms);
2697
+ }),
2698
+ ]).finally(() => {
2699
+ if (timer) clearTimeout(timer);
2700
+ });
2701
+ }
2702
+
2703
+ bootProfile('session-runtime:ready', { lazySession: true, prewarmSession: true });
2704
+ scheduleLeadSessionPrewarm();
2705
+ scheduleProviderWarmup();
2706
+ scheduleChannelStart();
2707
+
2708
+ function contextStatusCacheKeyFor({ messages, tools, bridgeMode }) {
2709
+ const compaction = session?.compaction || {};
2710
+ const lastMessage = messages[messages.length - 1] || null;
2711
+ return {
2712
+ session,
2713
+ sessionId: session?.id || null,
2714
+ provider: route.provider,
2715
+ model: route.model,
2716
+ cwd: currentCwd,
2717
+ mode,
2718
+ bridgeMode,
2719
+ messages,
2720
+ messageCount: messages.length,
2721
+ lastMessage,
2722
+ lastMessageRole: lastMessage?.role || null,
2723
+ lastMessageContent: lastMessage?.content || null,
2724
+ tools,
2725
+ toolCount: tools.length,
2726
+ contextWindow: session?.contextWindow || null,
2727
+ rawContextWindow: session?.rawContextWindow || null,
2728
+ effectiveContextWindowPercent: session?.effectiveContextWindowPercent || null,
2729
+ autoCompactTokenLimit: Number(session?.autoCompactTokenLimit || 0),
2730
+ lastContextTokens: Number(session?.lastContextTokens || 0),
2731
+ lastContextTokensUpdatedAt: Number(session?.lastContextTokensUpdatedAt || 0),
2732
+ lastContextTokensStaleAfterCompact: session?.lastContextTokensStaleAfterCompact === true,
2733
+ lastInputTokens: Number(session?.lastInputTokens || 0),
2734
+ lastOutputTokens: Number(session?.lastOutputTokens || 0),
2735
+ lastCachedReadTokens: Number(session?.lastCachedReadTokens || 0),
2736
+ lastCacheWriteTokens: Number(session?.lastCacheWriteTokens || 0),
2737
+ totalInputTokens: Number(session?.totalInputTokens || 0),
2738
+ totalOutputTokens: Number(session?.totalOutputTokens || 0),
2739
+ totalCachedReadTokens: Number(session?.totalCachedReadTokens || 0),
2740
+ totalCacheWriteTokens: Number(session?.totalCacheWriteTokens || 0),
2741
+ compactBoundaryTokens: Number(session?.compactBoundaryTokens || 0),
2742
+ compactionBoundaryTokens: Number(compaction.boundaryTokens || 0),
2743
+ compactionTriggerTokens: Number(compaction.triggerTokens || 0),
2744
+ compactionLastChangedAt: Number(compaction.lastChangedAt || 0),
2745
+ compactionLastCompactAt: Number(compaction.lastCompactAt || 0),
2746
+ };
2747
+ }
2748
+
2749
+ function sameContextStatusCacheKey(a, b) {
2750
+ if (!a || !b) return false;
2751
+ for (const key of Object.keys(a)) {
2752
+ if (!Object.is(a[key], b[key])) return false;
2753
+ }
2754
+ return true;
2755
+ }
2756
+
2757
+ return {
2758
+ get id() {
2759
+ return session?.id || null;
2760
+ },
2761
+ get provider() {
2762
+ return route.provider;
2763
+ },
2764
+ get model() {
2765
+ return route.model;
2766
+ },
2767
+ get effort() {
2768
+ return route.effectiveEffort || route.effort || route.preset?.effort || null;
2769
+ },
2770
+ get fast() {
2771
+ return route.fast === true;
2772
+ },
2773
+ get fastCapable() {
2774
+ return route.fastCapable === true;
2775
+ },
2776
+ get effortOptions() {
2777
+ return route.effortOptions || [];
2778
+ },
2779
+ get contextWindow() {
2780
+ return session?.contextWindow || null;
2781
+ },
2782
+ get rawContextWindow() {
2783
+ return session?.rawContextWindow || session?.contextWindow || null;
2784
+ },
2785
+ get effectiveContextWindowPercent() {
2786
+ return session?.effectiveContextWindowPercent || null;
2787
+ },
2788
+ get toolMode() {
2789
+ return mode;
2790
+ },
2791
+ get bridgeMode() {
2792
+ return bridge.getDefaultMode();
2793
+ },
2794
+ get autoClear() {
2795
+ return normalizeAutoClearConfig(config.autoClear);
2796
+ },
2797
+ get systemShell() {
2798
+ return normalizeSystemShellConfig(config.shell);
2799
+ },
2800
+ get workflow() {
2801
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
2802
+ const pack = loadWorkflowPack(dataDir, activeWorkflowId(config));
2803
+ return pack ? { id: pack.id, name: pack.name, description: pack.description, source: pack.source } : { id: DEFAULT_WORKFLOW_ID, name: 'Default' };
2804
+ },
2805
+ get outputStyle() {
2806
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
2807
+ return outputStyleStatus(dataDir).current;
2808
+ },
2809
+ get cwd() {
2810
+ return currentCwd;
2811
+ },
2812
+ get session() {
2813
+ return session;
2814
+ },
2815
+ contextStatus() {
2816
+ const messages = Array.isArray(session?.messages) ? session.messages : [];
2817
+ const tools = Array.isArray(session?.tools) ? session.tools : [];
2818
+ const bridgeMode = bridge.getDefaultMode();
2819
+ const cacheKey = contextStatusCacheKeyFor({ messages, tools, bridgeMode });
2820
+ if (contextStatusCacheValue && sameContextStatusCacheKey(cacheKey, contextStatusCacheKey)) {
2821
+ return contextStatusCacheValue;
2822
+ }
2823
+
2824
+ const messageSummary = summarizeContextMessages(messages);
2825
+ const toolSchemaTokens = estimateToolSchemaTokens(tools);
2826
+ const toolSchemaBreakdown = estimateToolSchemaBreakdown(tools);
2827
+ const requestReserveTokens = estimateRequestReserveTokens(tools);
2828
+ const requestOverheadTokens = Math.max(0, requestReserveTokens - toolSchemaTokens);
2829
+ const rawWindow = Number(session?.rawContextWindow || session?.contextWindow || 0);
2830
+ const effectiveWindow = Number(session?.contextWindow || rawWindow || 0);
2831
+ const lastContextTokens = Number(session?.lastContextTokens || 0);
2832
+ const estimatedContextTokens = messageSummary.estimatedTokens + requestReserveTokens;
2833
+ const compactAt = Number(session?.compaction?.lastChangedAt || session?.compaction?.lastCompactAt || 0);
2834
+ const usageAt = Number(session?.lastContextTokensUpdatedAt || 0);
2835
+ const lastUsageStale = !!lastContextTokens && (
2836
+ session?.lastContextTokensStaleAfterCompact === true
2837
+ || (compactAt > 0 && usageAt > 0 && usageAt <= compactAt)
2838
+ || (compactAt > 0 && usageAt <= 0)
2839
+ );
2840
+ const compactBoundaryTokens = Number(session?.compactBoundaryTokens || session?.compaction?.boundaryTokens || 0);
2841
+ const displayWindow = compactBoundaryTokens || effectiveWindow;
2842
+ const usedTokens = lastUsageStale
2843
+ ? estimatedContextTokens
2844
+ : Math.max(estimatedContextTokens, lastContextTokens || 0);
2845
+ const freeTokens = displayWindow ? Math.max(0, displayWindow - usedTokens) : 0;
2846
+ const autoCompactTokenLimit = Number(session?.autoCompactTokenLimit || 0);
2847
+ const defaultCompactTriggerTokens = compactBoundaryTokens ? Math.max(1, compactBoundaryTokens) : 0;
2848
+ const compactTriggerTokens = autoCompactTokenLimit && compactBoundaryTokens && autoCompactTokenLimit <= compactBoundaryTokens
2849
+ ? autoCompactTokenLimit
2850
+ : Number(session?.compaction?.triggerTokens || defaultCompactTriggerTokens || 0);
2851
+ const compactBufferTokens = Number(session?.compaction?.bufferTokens || (compactBoundaryTokens && compactTriggerTokens ? Math.max(0, compactBoundaryTokens - compactTriggerTokens) : 0));
2852
+ const value = {
2853
+ sessionId: session?.id || null,
2854
+ provider: route.provider,
2855
+ model: route.model,
2856
+ cwd: currentCwd,
2857
+ toolMode: mode,
2858
+ bridgeMode,
2859
+ contextWindow: displayWindow || effectiveWindow || null,
2860
+ effectiveContextWindow: effectiveWindow || null,
2861
+ rawContextWindow: rawWindow || null,
2862
+ effectiveContextWindowPercent: session?.effectiveContextWindowPercent || null,
2863
+ usedTokens,
2864
+ usedSource: lastContextTokens && !lastUsageStale && lastContextTokens >= estimatedContextTokens
2865
+ ? 'last_api_request'
2866
+ : 'estimated',
2867
+ currentEstimatedTokens: estimatedContextTokens,
2868
+ lastApiRequestTokens: lastContextTokens || 0,
2869
+ lastApiRequestStale: lastUsageStale,
2870
+ freeTokens,
2871
+ compaction: {
2872
+ ...(session?.compaction || {}),
2873
+ boundaryTokens: compactBoundaryTokens || null,
2874
+ triggerTokens: compactTriggerTokens || null,
2875
+ bufferTokens: compactBufferTokens || null,
2876
+ currentEstimatedTokens: estimatedContextTokens,
2877
+ lastApiRequestTokens: lastContextTokens || 0,
2878
+ lastApiRequestStale: lastUsageStale,
2879
+ },
2880
+ messages: messageSummary,
2881
+ request: {
2882
+ toolSchemaTokens,
2883
+ toolSchemaBreakdown,
2884
+ requestOverheadTokens,
2885
+ reserveTokens: requestReserveTokens,
2886
+ },
2887
+ usage: {
2888
+ lastInputTokens: Number(session?.lastInputTokens || 0),
2889
+ lastOutputTokens: Number(session?.lastOutputTokens || 0),
2890
+ lastCachedReadTokens: Number(session?.lastCachedReadTokens || 0),
2891
+ lastCacheWriteTokens: Number(session?.lastCacheWriteTokens || 0),
2892
+ lastContextTokens,
2893
+ totalInputTokens: Number(session?.totalInputTokens || 0),
2894
+ totalOutputTokens: Number(session?.totalOutputTokens || 0),
2895
+ totalCachedReadTokens: Number(session?.totalCachedReadTokens || 0),
2896
+ totalCacheWriteTokens: Number(session?.totalCacheWriteTokens || 0),
2897
+ },
2898
+ };
2899
+ contextStatusCacheKey = cacheKey;
2900
+ contextStatusCacheValue = value;
2901
+ return value;
2902
+ },
2903
+ listProviders() {
2904
+ return renderProviderStatus(cfgMod.loadConfig());
2905
+ },
2906
+ async getProviderSetup() {
2907
+ return await cachedProviderSetup();
2908
+ },
2909
+ async getUsageDashboard(options = {}) {
2910
+ const nextConfig = cfgMod.loadConfig();
2911
+ return await createUsageDashboard(nextConfig, {
2912
+ ...(options || {}),
2913
+ setup: await cachedProviderSetup(),
2914
+ getProvider: (providerId) => reg.getProvider(providerId),
2915
+ log: (message) => {
2916
+ if (process.env.MIXDOG_USAGE_TRACE) {
2917
+ try { process.stderr.write(`[usage] ${message}\n`); } catch {}
2918
+ }
2919
+ },
2920
+ });
2921
+ },
2922
+ getOnboardingStatus() {
2923
+ const nextConfig = cfgMod.loadConfig();
2924
+ return {
2925
+ completed: nextConfig?.onboarding?.completed === true,
2926
+ version: nextConfig?.onboarding?.version || 0,
2927
+ default: nextConfig?.default || null,
2928
+ workflowRoutes: summarizeWorkflowRoutes(nextConfig),
2929
+ };
2930
+ },
2931
+ getAutoClear() {
2932
+ return normalizeAutoClearConfig(config.autoClear);
2933
+ },
2934
+ getSystemShell() {
2935
+ return normalizeSystemShellConfig(config.shell);
2936
+ },
2937
+ setSystemShell(input = {}) {
2938
+ const command = normalizeSystemShellCommand(typeof input === 'string' ? input : input?.command);
2939
+ const nextConfig = cfgMod.loadConfig();
2940
+ cfgMod.saveConfig({
2941
+ ...nextConfig,
2942
+ shell: command ? { ...(nextConfig.shell || {}), command } : {},
2943
+ });
2944
+ config = cfgMod.loadConfig();
2945
+ setConfiguredShell(command);
2946
+ return normalizeSystemShellConfig(config.shell);
2947
+ },
2948
+ setAutoClear(input = {}) {
2949
+ const current = normalizeAutoClearConfig(config.autoClear);
2950
+ const next = { ...current };
2951
+ if (hasOwn(input, 'enabled')) next.enabled = input.enabled !== false;
2952
+ if (hasOwn(input, 'idleMs')) {
2953
+ const idleMs = Number(input.idleMs);
2954
+ if (!Number.isFinite(idleMs) || idleMs <= 0) throw new Error('autoclear idleMs must be a positive number');
2955
+ next.idleMs = Math.max(60_000, Math.round(idleMs));
2956
+ }
2957
+ if (hasOwn(input, 'duration')) {
2958
+ const idleMs = parseDurationMs(input.duration);
2959
+ if (!idleMs) throw new Error('usage: /autoclear [on|off|status|<minutes|1h|90m>]');
2960
+ next.idleMs = idleMs;
2961
+ if (!hasOwn(input, 'enabled')) next.enabled = true;
2962
+ }
2963
+ const nextConfig = cfgMod.loadConfig();
2964
+ cfgMod.saveConfig({ ...nextConfig, autoClear: next });
2965
+ config = cfgMod.loadConfig();
2966
+ return { ...normalizeAutoClearConfig(config.autoClear), label: formatDurationMs(normalizeAutoClearConfig(config.autoClear).idleMs) };
2967
+ },
2968
+ async completeOnboarding(payload = {}) {
2969
+ const defaultRoute = normalizeWorkflowRoute(payload.defaultRoute, route);
2970
+ const workflowInput = payload.workflowRoutes && typeof payload.workflowRoutes === 'object'
2971
+ ? payload.workflowRoutes
2972
+ : {};
2973
+ const nextConfig = cfgMod.loadConfig();
2974
+ let presets = Array.isArray(nextConfig.presets) ? nextConfig.presets.slice() : [];
2975
+ const workflowRoutes = { ...(nextConfig.workflowRoutes || {}) };
2976
+
2977
+ if (defaultRoute) {
2978
+ presets = upsertWorkflowPreset(presets, 'lead', defaultRoute);
2979
+ workflowRoutes.lead = defaultRoute;
2980
+ nextConfig.default = workflowPresetId('lead');
2981
+ }
2982
+
2983
+ for (const slot of WORKFLOW_ROUTE_SLOTS) {
2984
+ const normalized = normalizeWorkflowRoute(workflowInput[slot]);
2985
+ if (!normalized) continue;
2986
+ workflowRoutes[slot] = normalized;
2987
+ presets = upsertWorkflowPreset(presets, slot, normalized);
2988
+ }
2989
+
2990
+ nextConfig.presets = presets;
2991
+ nextConfig.workflowRoutes = workflowRoutes;
2992
+ nextConfig.maintenance = {
2993
+ ...(nextConfig.maintenance || {}),
2994
+ explore: workflowRoutes.explorer ? workflowPresetId('explorer') : (nextConfig.maintenance?.explore || 'haiku'),
2995
+ memory: workflowRoutes.memory ? workflowPresetId('memory') : (nextConfig.maintenance?.memory || 'haiku'),
2996
+ };
2997
+ nextConfig.onboarding = {
2998
+ ...(nextConfig.onboarding || {}),
2999
+ completed: true,
3000
+ version: ONBOARDING_VERSION,
3001
+ completedAt: new Date().toISOString(),
3002
+ };
3003
+
3004
+ cfgMod.saveConfig(nextConfig);
3005
+ config = cfgMod.loadConfig();
3006
+ if (defaultRoute) {
3007
+ route = resolveRoute(config, { provider: defaultRoute.provider, model: defaultRoute.model, effort: defaultRoute.effort });
3008
+ if (session?.id) mgr.closeSession(session.id, 'cli-onboarding-complete');
3009
+ await recreateCurrentSessionIfReady();
3010
+ }
3011
+ return this.getOnboardingStatus();
3012
+ },
3013
+ getChannelSetup() {
3014
+ return channelSetup();
3015
+ },
3016
+ getChannelWorkerStatus() {
3017
+ return channels.status();
3018
+ },
3019
+ saveDiscordToken(token) {
3020
+ const result = saveDiscordToken(token);
3021
+ reloadChannelsSoon();
3022
+ return result;
3023
+ },
3024
+ forgetDiscordToken() {
3025
+ const result = forgetDiscordToken();
3026
+ reloadChannelsSoon();
3027
+ return result;
3028
+ },
3029
+ saveWebhookAuthtoken(token) {
3030
+ const result = saveWebhookAuthtoken(token);
3031
+ reloadChannelsSoon();
3032
+ return result;
3033
+ },
3034
+ forgetWebhookAuthtoken() {
3035
+ const result = forgetWebhookAuthtoken();
3036
+ reloadChannelsSoon();
3037
+ return result;
3038
+ },
3039
+ saveChannel(entry) {
3040
+ const result = saveChannel(entry);
3041
+ reloadChannelsSoon();
3042
+ return result;
3043
+ },
3044
+ deleteChannel(name) {
3045
+ const result = deleteChannel(name);
3046
+ reloadChannelsSoon();
3047
+ return result;
3048
+ },
3049
+ setWebhookConfig(patch) {
3050
+ const result = setWebhookConfig(patch);
3051
+ reloadChannelsSoon();
3052
+ return result;
3053
+ },
3054
+ saveSchedule(entry) {
3055
+ const result = saveSchedule(entry);
3056
+ reloadChannelsSoon();
3057
+ return result;
3058
+ },
3059
+ deleteSchedule(name) {
3060
+ const result = deleteSchedule(name);
3061
+ reloadChannelsSoon();
3062
+ return result;
3063
+ },
3064
+ setScheduleEnabled(name, enabled) {
3065
+ const result = setScheduleEnabled(name, enabled);
3066
+ reloadChannelsSoon();
3067
+ return result;
3068
+ },
3069
+ saveWebhook(entry) {
3070
+ const result = saveWebhook(entry);
3071
+ reloadChannelsSoon();
3072
+ return result;
3073
+ },
3074
+ deleteWebhook(name) {
3075
+ const result = deleteWebhook(name);
3076
+ reloadChannelsSoon();
3077
+ return result;
3078
+ },
3079
+ setWebhookEnabled(name, enabled) {
3080
+ const result = setWebhookEnabled(name, enabled);
3081
+ reloadChannelsSoon();
3082
+ return result;
3083
+ },
3084
+ async authenticateProvider(providerId, secret) {
3085
+ const result = String(secret || '').trim()
3086
+ ? saveProviderApiKey(cfgMod, providerId, secret)
3087
+ : await loginOAuthProvider(cfgMod, providerId);
3088
+ config = cfgMod.loadConfig();
3089
+ invalidateProviderCaches();
3090
+ warmProviderModelCache();
3091
+ return result;
3092
+ },
3093
+ async loginOAuthProvider(providerId) {
3094
+ const result = await loginOAuthProvider(cfgMod, providerId);
3095
+ config = cfgMod.loadConfig();
3096
+ invalidateProviderCaches();
3097
+ warmProviderModelCache();
3098
+ return result;
3099
+ },
3100
+ async beginOAuthProviderLogin(providerId) {
3101
+ const result = await beginOAuthProviderLogin(cfgMod, providerId);
3102
+ config = cfgMod.loadConfig();
3103
+ return {
3104
+ ...result,
3105
+ completeCode: async (code) => {
3106
+ const completed = await result.completeCode(code);
3107
+ config = cfgMod.loadConfig();
3108
+ invalidateProviderCaches();
3109
+ warmProviderModelCache();
3110
+ return completed;
3111
+ },
3112
+ };
3113
+ },
3114
+ saveProviderApiKey(providerId, secret) {
3115
+ const result = saveProviderApiKey(cfgMod, providerId, secret);
3116
+ config = cfgMod.loadConfig();
3117
+ invalidateProviderCaches();
3118
+ warmProviderModelCache();
3119
+ return result;
3120
+ },
3121
+ saveOpenAIUsageSessionKey(secret) {
3122
+ const result = saveOpenAIUsageSessionKey(cfgMod, secret);
3123
+ config = cfgMod.loadConfig();
3124
+ invalidateProviderCaches();
3125
+ return result;
3126
+ },
3127
+ saveOpenCodeGoUsageAuth(opts) {
3128
+ const result = saveOpenCodeGoUsageAuth(cfgMod, opts);
3129
+ config = cfgMod.loadConfig();
3130
+ invalidateProviderCaches();
3131
+ return result;
3132
+ },
3133
+ setLocalProvider(providerId, opts) {
3134
+ const result = setLocalProvider(cfgMod, providerId, opts);
3135
+ config = cfgMod.loadConfig();
3136
+ invalidateProviderCaches();
3137
+ warmProviderModelCache();
3138
+ return result;
3139
+ },
3140
+ forgetProviderAuth(providerId) {
3141
+ const result = forgetProviderAuth(providerId);
3142
+ config = cfgMod.loadConfig();
3143
+ invalidateProviderCaches();
3144
+ warmProviderModelCache();
3145
+ return result;
3146
+ },
3147
+ listPresets() {
3148
+ return cfgMod.listPresets(cfgMod.loadConfig());
3149
+ },
3150
+ async listProviderModels(options = {}) {
3151
+ return await collectProviderModels({ force: options.force === true || options.refresh === true });
3152
+ },
3153
+ listAgents() {
3154
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
3155
+ return FIXED_AGENT_SLOTS.map((agent) => ({
3156
+ ...agent,
3157
+ locked: true,
3158
+ route: agentRouteFromConfig(config, agent.id, dataDir),
3159
+ definition: loadAgentDefinition(dataDir, agent.id),
3160
+ }));
3161
+ },
3162
+ listWorkflows() {
3163
+ const currentConfig = cfgMod.loadConfig();
3164
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
3165
+ const active = activeWorkflowId(currentConfig);
3166
+ return listWorkflowPacks(dataDir).map((workflow) => ({
3167
+ id: workflow.id,
3168
+ name: workflow.name,
3169
+ description: workflow.description,
3170
+ source: workflow.source,
3171
+ active: workflow.id === active,
3172
+ agents: workflow.agents,
3173
+ }));
3174
+ },
3175
+ getOutputStyle() {
3176
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
3177
+ return outputStyleStatus(dataDir);
3178
+ },
3179
+ listOutputStyles() {
3180
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
3181
+ return outputStyleStatus(dataDir);
3182
+ },
3183
+ async setOutputStyle(value) {
3184
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
3185
+ const before = outputStyleStatus(dataDir);
3186
+ const selected = findOutputStyle(value, before.styles);
3187
+ if (!selected) {
3188
+ const names = before.styles.map((style) => style.label || style.id).join(', ') || 'Default';
3189
+ throw new Error(`output style must be one of ${names}`);
3190
+ }
3191
+ if (typeof sharedCfgMod.updateConfig !== 'function') throw new Error('output style config writer unavailable');
3192
+ sharedCfgMod.updateConfig((root) => {
3193
+ const next = { ...(root || {}), outputStyle: selected.id };
3194
+ if (next.agent && typeof next.agent === 'object' && !Array.isArray(next.agent)) {
3195
+ const agent = { ...next.agent };
3196
+ delete agent.outputStyle;
3197
+ next.agent = agent;
3198
+ }
3199
+ return next;
3200
+ });
3201
+ const hasConversation = sessionHasConversationMessages(session);
3202
+ let appliedToCurrentSession = !hasConversation;
3203
+ if (session?.id && !hasConversation) {
3204
+ mgr.closeSession(session.id, 'cli-output-style-switch');
3205
+ session = null;
3206
+ await recreateCurrentSessionIfReady();
3207
+ }
3208
+ invalidateContextStatusCache();
3209
+ return { ...outputStyleStatus(dataDir), appliedToCurrentSession };
3210
+ },
3211
+ async setWorkflow(workflowId) {
3212
+ const id = normalizeWorkflowId(workflowId, DEFAULT_WORKFLOW_ID);
3213
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
3214
+ const pack = loadWorkflowPack(dataDir, id);
3215
+ if (!pack || pack.id !== id) throw new Error(`workflow "${workflowId}" not found`);
3216
+ const nextConfig = cfgMod.loadConfig();
3217
+ nextConfig.workflow = { ...(nextConfig.workflow || {}), active: id };
3218
+ cfgMod.saveConfig(nextConfig);
3219
+ config = cfgMod.loadConfig();
3220
+ if (session?.id) {
3221
+ mgr.closeSession(session.id, 'cli-workflow-switch');
3222
+ session = null;
3223
+ }
3224
+ await recreateCurrentSessionIfReady();
3225
+ return { id: pack.id, name: pack.name, description: pack.description, source: pack.source };
3226
+ },
3227
+ async setAgentRoute(agentId, next) {
3228
+ const id = normalizeAgentId(agentId);
3229
+ if (!id) throw new Error(`unknown agent "${agentId}"`);
3230
+ let selectedRoute = resolveRoute(config, { ...(next || {}) });
3231
+ await reg.initProviders(ensureProviderEnabled(config, selectedRoute.provider));
3232
+ const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
3233
+ const fastCapable = fastCapableFor(selectedRoute.provider, modelMeta);
3234
+ selectedRoute = { ...selectedRoute, fast: fastCapable ? selectedRoute.fast === true : false };
3235
+ config = saveModelSettings(cfgMod, selectedRoute, { fastCapable });
3236
+
3237
+ const routeToSave = normalizeWorkflowRoute(selectedRoute);
3238
+ if (!routeToSave) throw new Error('agent route requires provider and model');
3239
+ const agent = FIXED_AGENT_SLOTS.find((item) => item.id === id);
3240
+ const nextConfig = cfgMod.loadConfig();
3241
+ nextConfig.agents = {
3242
+ ...(nextConfig.agents || {}),
3243
+ [id]: routeToSave,
3244
+ };
3245
+ nextConfig.presets = upsertWorkflowPreset(nextConfig.presets, agentPresetSlot(id), routeToSave);
3246
+ if (agent?.workflowSlot) {
3247
+ nextConfig.workflowRoutes = {
3248
+ ...(nextConfig.workflowRoutes || {}),
3249
+ [agent.workflowSlot]: routeToSave,
3250
+ };
3251
+ nextConfig.presets = upsertWorkflowPreset(nextConfig.presets, agent.workflowSlot, routeToSave);
3252
+ nextConfig.maintenance = {
3253
+ ...(nextConfig.maintenance || {}),
3254
+ ...(id === 'explore' ? { explore: workflowPresetId('explorer') } : {}),
3255
+ ...(id === 'maintainer' ? { memory: workflowPresetId('memory') } : {}),
3256
+ };
3257
+ }
3258
+ cfgMod.saveConfig(nextConfig);
3259
+ config = cfgMod.loadConfig();
3260
+ return routeToSave;
3261
+ },
3262
+ async ask(prompt, options = {}) {
3263
+ activeTurnCount += 1;
3264
+ const startedAt = Date.now();
3265
+ try {
3266
+ await refreshSessionForCwdIfNeeded('cwd-change');
3267
+ if (!session?.id) await createCurrentSession('turn');
3268
+ hooks.emit('turn:start', { sessionId: session.id, prompt, cwd: currentCwd });
3269
+ const turnContext = [options.context || ''].map((part) => String(part || '').trim()).filter(Boolean).join('\n\n');
3270
+ const result = await mgr.askSession(
3271
+ session.id,
3272
+ prompt,
3273
+ turnContext || null,
3274
+ async (iter, calls) => {
3275
+ for (const call of calls || []) {
3276
+ hooks.emit('tool:planned', {
3277
+ sessionId: session.id,
3278
+ name: call?.name || 'tool',
3279
+ callId: call?.id || null,
3280
+ });
3281
+ }
3282
+ if (typeof options.onToolCall === 'function') {
3283
+ return await options.onToolCall(iter, calls);
3284
+ }
3285
+ return undefined;
3286
+ },
3287
+ currentCwd,
3288
+ options.prefetch || null,
3289
+ {
3290
+ onTextDelta: options.onTextDelta,
3291
+ onReasoningDelta: options.onReasoningDelta,
3292
+ onUsageDelta: options.onUsageDelta,
3293
+ onToolResult: options.onToolResult,
3294
+ onStageChange: options.onStageChange,
3295
+ onStreamDelta: options.onStreamDelta,
3296
+ drainSteering: options.drainSteering,
3297
+ onSteerMessage: options.onSteerMessage,
3298
+ },
3299
+ );
3300
+ session = mgr.getSession(session.id) || session;
3301
+ hooks.emit('turn:end', { sessionId: session.id, elapsedMs: Date.now() - startedAt });
3302
+ return { result, session };
3303
+ } catch (error) {
3304
+ hooks.emit('turn:error', { sessionId: session?.id || null, elapsedMs: Date.now() - startedAt, error: error?.message || String(error) });
3305
+ throw error;
3306
+ } finally {
3307
+ activeTurnCount = Math.max(0, activeTurnCount - 1);
3308
+ if (!firstTurnCompleted) {
3309
+ firstTurnCompleted = true;
3310
+ scheduleProviderModelWarmup();
3311
+ }
3312
+ }
3313
+ },
3314
+ async clear(options = {}) {
3315
+ if (!session?.id) return false;
3316
+ const cleared = await mgr.clearSessionMessages(session.id);
3317
+ if (!cleared) return false;
3318
+ session = typeof cleared === 'object' ? cleared : (mgr.getSession(session.id) || session);
3319
+ if (options.recoverBridge === true) {
3320
+ try { bridge.recoverWorkers?.({ clientHostPid: session?.clientHostPid || process.pid }); } catch {}
3321
+ }
3322
+ invalidateContextStatusCache();
3323
+ return true;
3324
+ },
3325
+ async compact(options = {}) {
3326
+ if (!session?.id) return null;
3327
+ const result = await mgr.compactSessionMessages(session.id);
3328
+ session = mgr.getSession(session.id) || session;
3329
+ if (options.recoverBridge === true) {
3330
+ try { bridge.recoverWorkers?.({ clientHostPid: session?.clientHostPid || process.pid }); } catch {}
3331
+ }
3332
+ invalidateContextStatusCache();
3333
+ return result;
3334
+ },
3335
+ async setToolMode(nextMode) {
3336
+ mode = normalizeToolMode(nextMode);
3337
+ invalidatePreSessionToolSurface();
3338
+ if (session?.id) mgr.closeSession(session.id, 'cli-mode-switch');
3339
+ await recreateCurrentSessionIfReady();
3340
+ return mode;
3341
+ },
3342
+ setBridgeMode(nextMode) {
3343
+ const applied = bridge.setDefaultMode(nextMode);
3344
+ try { sharedCfgMod.updateSection('agent', (s) => ({ ...(s || {}), bridgeMode: applied })); } catch {}
3345
+ return applied;
3346
+ },
3347
+ toggleBridgeMode() {
3348
+ const applied = bridge.toggleDefaultMode();
3349
+ try { sharedCfgMod.updateSection('agent', (s) => ({ ...(s || {}), bridgeMode: applied })); } catch {}
3350
+ return applied;
3351
+ },
3352
+ bridgeStatus() {
3353
+ return bridgeStatusState();
3354
+ },
3355
+ get clientHostPid() {
3356
+ return session?.clientHostPid || process.pid;
3357
+ },
3358
+ bridgeControl(args = {}) {
3359
+ const callerSessionId = session?.id || null;
3360
+ return bridge.execute(args, {
3361
+ callerCwd: currentCwd,
3362
+ invocationSource: 'user-command',
3363
+ callerSessionId,
3364
+ clientHostPid: session?.clientHostPid || process.pid,
3365
+ notifyFn: notifyFnForSession(callerSessionId),
3366
+ });
3367
+ },
3368
+ onNotification(listener) {
3369
+ if (typeof listener !== 'function') return () => {};
3370
+ notificationListeners.add(listener);
3371
+ return () => notificationListeners.delete(listener);
3372
+ },
3373
+ toolsStatus(query = '') {
3374
+ const surface = activeToolSurface();
3375
+ const catalog = Array.isArray(surface?.deferredToolCatalog)
3376
+ ? surface.deferredToolCatalog
3377
+ : (Array.isArray(surface?.tools) ? surface.tools : []);
3378
+ const activeNames = new Set((surface?.tools || []).map((tool) => tool?.name).filter(Boolean));
3379
+ const needle = clean(query).toLowerCase();
3380
+ const rows = catalog.map((tool) => toolRow(tool, activeNames)).filter((row) => row.name);
3381
+ const tools = needle
3382
+ ? rows.filter((row) => toolSearchMatches(row, needle))
3383
+ : rows;
3384
+ return {
3385
+ mode,
3386
+ count: rows.length,
3387
+ activeCount: rows.filter((row) => row.active).length,
3388
+ tools,
3389
+ activeTools: sortedNamesByMeasuredUsage(activeNames),
3390
+ };
3391
+ },
3392
+ selectTools(names) {
3393
+ const list = Array.isArray(names) ? names : String(names || '').split(/[,\s]+/);
3394
+ const result = selectDeferredTools(activeToolSurface(), list, mode);
3395
+ return { ...result, status: this.toolsStatus() };
3396
+ },
3397
+ setCwd(path) {
3398
+ applyResolvedCwd(resolveCwdPath(currentCwd, path));
3399
+ return currentCwd;
3400
+ },
3401
+ mcpStatus() {
3402
+ return mcpStatus();
3403
+ },
3404
+ async reconnectMcp() {
3405
+ config = cfgMod.loadConfig();
3406
+ const status = await connectConfiguredMcp({ reset: true });
3407
+ invalidatePreSessionToolSurface();
3408
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-reconnect');
3409
+ await recreateCurrentSessionIfReady();
3410
+ return status;
3411
+ },
3412
+ async addMcpServer(input = {}) {
3413
+ const { name, config: serverConfig } = normalizeMcpServerInput(input);
3414
+ const nextConfig = cfgMod.loadConfig();
3415
+ nextConfig.mcpServers = {
3416
+ ...(nextConfig.mcpServers || {}),
3417
+ [name]: serverConfig,
3418
+ };
3419
+ cfgMod.saveConfig(nextConfig);
3420
+ config = cfgMod.loadConfig();
3421
+ const status = await connectConfiguredMcp({ reset: true });
3422
+ invalidatePreSessionToolSurface();
3423
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-add');
3424
+ await recreateCurrentSessionIfReady();
3425
+ return { name, status };
3426
+ },
3427
+ async removeMcpServer(name) {
3428
+ const serverName = clean(name);
3429
+ if (!serverName) throw new Error('MCP server name is required');
3430
+ const nextConfig = cfgMod.loadConfig();
3431
+ const current = nextConfig.mcpServers && typeof nextConfig.mcpServers === 'object'
3432
+ ? { ...nextConfig.mcpServers }
3433
+ : {};
3434
+ if (!Object.prototype.hasOwnProperty.call(current, serverName)) {
3435
+ throw new Error(`MCP server not configured: ${serverName}`);
3436
+ }
3437
+ delete current[serverName];
3438
+ cfgMod.saveConfig({ ...nextConfig, mcpServers: current });
3439
+ config = cfgMod.loadConfig();
3440
+ const status = await connectConfiguredMcp({ reset: true });
3441
+ invalidatePreSessionToolSurface();
3442
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-remove');
3443
+ await recreateCurrentSessionIfReady();
3444
+ return status;
3445
+ },
3446
+ async setMcpServerEnabled(name, enabled) {
3447
+ const serverName = clean(name);
3448
+ if (!serverName) throw new Error('MCP server name is required');
3449
+ const nextConfig = cfgMod.loadConfig();
3450
+ const current = nextConfig.mcpServers && typeof nextConfig.mcpServers === 'object'
3451
+ ? { ...nextConfig.mcpServers }
3452
+ : {};
3453
+ if (!Object.prototype.hasOwnProperty.call(current, serverName)) {
3454
+ throw new Error(`MCP server not configured: ${serverName}`);
3455
+ }
3456
+ current[serverName] = { ...(current[serverName] || {}), enabled: enabled !== false };
3457
+ cfgMod.saveConfig({ ...nextConfig, mcpServers: current });
3458
+ config = cfgMod.loadConfig();
3459
+ const status = await connectConfiguredMcp({ reset: true });
3460
+ invalidatePreSessionToolSurface();
3461
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-toggle');
3462
+ await recreateCurrentSessionIfReady();
3463
+ return status;
3464
+ },
3465
+ skillsStatus() {
3466
+ return skillsStatus();
3467
+ },
3468
+ skillContent(name) {
3469
+ return skillContent(name);
3470
+ },
3471
+ async addSkill(input = {}) {
3472
+ const skill = addProjectSkill(input);
3473
+ if (session?.id) mgr.closeSession(session.id, 'cli-skill-add');
3474
+ await recreateCurrentSessionIfReady();
3475
+ return { skill, status: skillsStatus() };
3476
+ },
3477
+ async reloadSkills() {
3478
+ if (session?.id) mgr.closeSession(session.id, 'cli-skills-reload');
3479
+ await recreateCurrentSessionIfReady();
3480
+ return skillsStatus();
3481
+ },
3482
+ pluginsStatus() {
3483
+ return pluginsStatus();
3484
+ },
3485
+ async reloadPlugins() {
3486
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugins-reload');
3487
+ await recreateCurrentSessionIfReady();
3488
+ return pluginsStatus();
3489
+ },
3490
+ async addPlugin(source) {
3491
+ const dataDir = cfgMod.getPluginData?.();
3492
+ const plugin = registryAddPlugin(source, { dataDir });
3493
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-add');
3494
+ await recreateCurrentSessionIfReady();
3495
+ return { plugin, status: pluginsStatus() };
3496
+ },
3497
+ async updatePlugin(plugin = {}) {
3498
+ const key = clean(plugin.id || plugin.name || plugin);
3499
+ const dataDir = cfgMod.getPluginData?.();
3500
+ const updated = registryUpdatePlugin(key, { dataDir });
3501
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-update');
3502
+ await recreateCurrentSessionIfReady();
3503
+ return { plugin: updated, status: pluginsStatus() };
3504
+ },
3505
+ async removePlugin(plugin = {}) {
3506
+ const key = clean(plugin.id || plugin.name || plugin);
3507
+ const dataDir = cfgMod.getPluginData?.();
3508
+ const removed = registryRemovePlugin(key, { dataDir });
3509
+ const nextConfig = cfgMod.loadConfig();
3510
+ const serverName = pluginMcpServerName(plugin);
3511
+ if (nextConfig.mcpServers && Object.prototype.hasOwnProperty.call(nextConfig.mcpServers, serverName)) {
3512
+ const current = { ...nextConfig.mcpServers };
3513
+ delete current[serverName];
3514
+ cfgMod.saveConfig({ ...nextConfig, mcpServers: current });
3515
+ config = cfgMod.loadConfig();
3516
+ await connectConfiguredMcp({ reset: true });
3517
+ invalidatePreSessionToolSurface();
3518
+ }
3519
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-remove');
3520
+ await recreateCurrentSessionIfReady();
3521
+ return { plugin: removed, status: pluginsStatus() };
3522
+ },
3523
+ async enablePluginMcp(plugin = {}) {
3524
+ const root = clean(plugin.root);
3525
+ const script = clean(plugin.mcpScript);
3526
+ if (!root || !script) throw new Error('plugin has no MCP script');
3527
+ const scriptPath = join(root, script);
3528
+ if (!existsSync(scriptPath)) throw new Error(`plugin MCP script not found: ${scriptPath}`);
3529
+ const serverName = pluginMcpServerName(plugin);
3530
+ const nextConfig = cfgMod.loadConfig();
3531
+ nextConfig.mcpServers = {
3532
+ ...(nextConfig.mcpServers || {}),
3533
+ [serverName]: {
3534
+ command: 'node',
3535
+ args: [scriptPath],
3536
+ cwd: root,
3537
+ env: {
3538
+ MIXDOG_PLUGIN_ROOT: root,
3539
+ MIXDOG_PLUGIN_DATA: join(cfgMod.getPluginData?.() || STANDALONE_DATA_DIR, 'plugins', 'data', clean(plugin.id || plugin.name || serverName)),
3540
+ },
3541
+ },
3542
+ };
3543
+ cfgMod.saveConfig(nextConfig);
3544
+ config = cfgMod.loadConfig();
3545
+ const status = await connectConfiguredMcp({ reset: true });
3546
+ invalidatePreSessionToolSurface();
3547
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-mcp-enable');
3548
+ await recreateCurrentSessionIfReady();
3549
+ return { serverName, status };
3550
+ },
3551
+ hooksStatus() {
3552
+ return hooks.status();
3553
+ },
3554
+ addHookRule(rule) {
3555
+ return hooks.addRule(rule);
3556
+ },
3557
+ setHookRuleEnabled(index, enabled) {
3558
+ return hooks.setRuleEnabled(index, enabled);
3559
+ },
3560
+ deleteHookRule(index) {
3561
+ return hooks.deleteRule(index);
3562
+ },
3563
+ async memoryControl(args = {}) {
3564
+ const memoryMod = await getMemoryModule();
3565
+ if (!memoryMod?.handleToolCall) throw new Error('memory runtime is not available');
3566
+ return toolResponseText(await memoryMod.handleToolCall('memory', args || {}));
3567
+ },
3568
+ async recall(query, args = {}) {
3569
+ const baseQuery = query || args?.query || '';
3570
+ if (args?.currentSession !== false && session?.id) {
3571
+ const currentText = currentSessionRecallRows(session, baseQuery, { limit: args?.limit });
3572
+ if (!isEmptyRecallText(currentText)) return currentText;
3573
+ }
3574
+ const memoryMod = await getMemoryModule();
3575
+ if (!memoryMod?.handleToolCall) throw new Error('memory runtime is not available');
3576
+ const baseArgs = {
3577
+ ...(args || {}),
3578
+ query: baseQuery,
3579
+ cwd: args?.cwd || currentCwd,
3580
+ };
3581
+ let result = '(no results)';
3582
+ if (session?.id && args?.currentSession !== false && args?.forceCycleOnEmpty !== false) {
3583
+ const messages = Array.isArray(session.messages) ? session.messages : [];
3584
+ if (messages.length > 0) {
3585
+ await memoryMod.handleToolCall('memory', {
3586
+ action: 'ingest_session',
3587
+ sessionId: session.id,
3588
+ cwd: currentCwd,
3589
+ messages,
3590
+ });
3591
+ await memoryMod.handleToolCall('memory', {
3592
+ action: 'cycle1',
3593
+ min_batch: 1,
3594
+ session_cap: 1,
3595
+ batch_size: Math.max(1, Math.min(100, messages.length)),
3596
+ });
3597
+ result = toolResponseText(await memoryMod.handleToolCall('recall', {
3598
+ ...baseArgs,
3599
+ sessionId: session.id,
3600
+ currentSession: true,
3601
+ projectScope: baseArgs.projectScope || 'all',
3602
+ includeRaw: baseArgs.includeRaw !== false,
3603
+ includeArchived: baseArgs.includeArchived !== false,
3604
+ }));
3605
+ }
3606
+ }
3607
+ if (isEmptyRecallText(result)) {
3608
+ result = toolResponseText(await memoryMod.handleToolCall('recall', baseArgs));
3609
+ }
3610
+ return result;
3611
+ },
3612
+ async setRoute(next) {
3613
+ const requested = { ...(next || {}) };
3614
+ if (requested.effort === undefined && !requested.provider && !requested.model && hasOwn(route, 'effort')) {
3615
+ requested.effort = route.effort;
3616
+ }
3617
+ if (!requested.provider && requested.model && !findPreset(config, requested.model)) {
3618
+ requested.provider = route.provider;
3619
+ }
3620
+ let selectedRoute = resolveRoute(config, requested);
3621
+ await reg.initProviders(ensureProviderEnabled(config, selectedRoute.provider));
3622
+ const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
3623
+ const fastCapable = fastCapableFor(selectedRoute.provider, modelMeta);
3624
+ selectedRoute = { ...selectedRoute, fast: fastCapable ? selectedRoute.fast === true : false };
3625
+ config = saveModelSettings(cfgMod, selectedRoute, { fastCapable });
3626
+ const leadRoute = persistLeadRoute(selectedRoute);
3627
+ route = resolveRoute(config, leadRoute
3628
+ ? { model: workflowPresetId('lead') }
3629
+ : selectedRoute);
3630
+ await refreshRouteEffort(modelMeta);
3631
+ if (session) {
3632
+ const updated = mgr.updateSessionRoute?.(session.id, {
3633
+ provider: route.provider,
3634
+ model: route.model,
3635
+ fast: route.fast === true,
3636
+ effort: route.effectiveEffort || null,
3637
+ });
3638
+ if (updated) session = updated;
3639
+ else {
3640
+ session.provider = route.provider;
3641
+ session.model = route.model;
3642
+ session.fast = route.fast === true;
3643
+ session.effort = route.effectiveEffort || null;
3644
+ }
3645
+ statusRoutes?.writeGatewaySessionRoute?.(session.id, routeForStatusline(route));
3646
+ invalidateContextStatusCache();
3647
+ }
3648
+ return route;
3649
+ },
3650
+
3651
+ async setFast(value) {
3652
+ const enabled = value === true;
3653
+ const modelMeta = await lookupModelMeta(route.provider, route.model);
3654
+ const fastCapable = fastCapableFor(route.provider, modelMeta);
3655
+ if (enabled && !fastCapable) {
3656
+ throw new Error(`fast mode is not available for ${route.provider}/${route.model}`);
3657
+ }
3658
+ route = resolveRoute(config, { provider: route.provider, model: route.model, effort: route.effort, fast: fastCapable ? enabled : false });
3659
+ config = saveModelSettings(cfgMod, route, { fastCapable });
3660
+ const leadRoute = persistLeadRoute(route);
3661
+ if (leadRoute) route = resolveRoute(config, { model: workflowPresetId('lead') });
3662
+ await refreshRouteEffort(modelMeta);
3663
+ if (session) {
3664
+ session.fast = route.fast === true;
3665
+ session.effort = route.effectiveEffort || null;
3666
+ statusRoutes?.writeGatewaySessionRoute?.(session.id, routeForStatusline(route));
3667
+ invalidateContextStatusCache();
3668
+ }
3669
+ return route.fast === true;
3670
+ },
3671
+
3672
+ async toggleFast() {
3673
+ return await this.setFast(!(route.fast === true));
3674
+ },
3675
+
3676
+ async setEffort(value) {
3677
+ const normalized = normalizeEffortInput(value);
3678
+ route = { ...route, effort: normalized };
3679
+ config = saveModelSettings(cfgMod, route, { fastCapable: route.fastCapable !== false });
3680
+ const leadRoute = persistLeadRoute(route);
3681
+ if (leadRoute) {
3682
+ route = resolveRoute(config, { model: workflowPresetId('lead') });
3683
+ }
3684
+ await refreshRouteEffort();
3685
+ if (session) {
3686
+ session.effort = route.effectiveEffort || null;
3687
+ statusRoutes?.writeGatewaySessionRoute?.(session.id, routeForStatusline(route));
3688
+ invalidateContextStatusCache();
3689
+ }
3690
+ return route;
3691
+ },
3692
+ async close(reason = 'cli-exit', options = {}) {
3693
+ const detach = options?.detach === true || options?.wait === false || options?.waitForExit === false;
3694
+ closeRequested = true;
3695
+ if (channelStartTimer) {
3696
+ clearTimeout(channelStartTimer);
3697
+ channelStartTimer = null;
3698
+ }
3699
+ if (providerWarmupTimer) {
3700
+ clearTimeout(providerWarmupTimer);
3701
+ providerWarmupTimer = null;
3702
+ }
3703
+ if (providerModelWarmupTimer) {
3704
+ clearTimeout(providerModelWarmupTimer);
3705
+ providerModelWarmupTimer = null;
3706
+ }
3707
+ try { cancelBackgroundTasks({ reason, notify: false }); } catch {}
3708
+ const channelStop = channels.stop(reason, detach ? { waitForExit: false } : undefined);
3709
+ try { bridge.closeAll(reason); } catch {}
3710
+ let mcpStop = null;
3711
+ try { mcpStop = mcpClient.disconnectAll?.(); } catch {}
3712
+ const openaiWsStop = globalThis.__mixdogOpenaiWsRuntimeLoaded === true
3713
+ ? import('./runtime/agent/orchestrator/providers/openai-oauth-ws.mjs')
3714
+ .then((mod) => mod?.drainOpenaiWsPool?.(reason))
3715
+ .catch(() => {})
3716
+ : null;
3717
+ const patchStop = closePatchRuntimeIfLoaded(detach ? { waitForExit: false } : undefined);
3718
+ let ok = false;
3719
+ if (session?.id) {
3720
+ statusRoutes?.clearGatewaySessionRoute?.(session.id);
3721
+ ok = mgr.closeSession(session.id, reason);
3722
+ session = null;
3723
+ }
3724
+ if (detach) {
3725
+ try { await withTeardownDeadline(channelStop, 300, false); } catch {}
3726
+ for (const stop of [mcpStop, openaiWsStop, patchStop]) {
3727
+ Promise.resolve(stop).catch(() => {});
3728
+ }
3729
+ return ok;
3730
+ }
3731
+ await Promise.allSettled([
3732
+ withTeardownDeadline(channelStop, 5500, false),
3733
+ withTeardownDeadline(mcpStop, 1500, false),
3734
+ withTeardownDeadline(openaiWsStop, 1500, false),
3735
+ withTeardownDeadline(patchStop, 1500, false),
3736
+ ]);
3737
+ return ok;
3738
+ },
3739
+ abort(reason = 'cli-abort') {
3740
+ if (!session?.id) return false;
3741
+ return mgr.abortSessionTurn(session.id, reason);
3742
+ },
3743
+ listSessions() {
3744
+ return mgr.listSessions({}).map(s => {
3745
+ const owner = clean(s.owner || 'user').toLowerCase();
3746
+ if (owner && !['cli', 'user', 'mixdog', 'legacy'].includes(owner)) return null;
3747
+ const sourceType = clean(s.sourceType || '').toLowerCase();
3748
+ const sourceName = clean(s.sourceName || '').toLowerCase();
3749
+ const role = clean(s.role || '').toLowerCase();
3750
+ const leadish = role === 'lead'
3751
+ || sourceType === 'lead'
3752
+ || (sourceType === 'cli' && (!sourceName || sourceName === 'main'))
3753
+ || (!sourceType && !sourceName && owner !== 'bridge');
3754
+ if (!leadish) return null;
3755
+ let preview = cleanSessionPreview(s.preview || '');
3756
+ let messageCount = Math.max(0, Number(s.messageCount) || 0);
3757
+ if (!preview && Array.isArray(s.messages)) {
3758
+ const msgs = s.messages || [];
3759
+ const userPreviews = msgs
3760
+ .filter(m => m && m.role === 'user')
3761
+ .map(m => cleanSessionPreview(sessionMessageText(m.content)))
3762
+ .filter(text => !isSessionPreviewNoise(text));
3763
+ preview = userPreviews[userPreviews.length - 1] || userPreviews[0] || '';
3764
+ messageCount = msgs.filter(m => m && (m.role === 'user' || m.role === 'assistant')).length;
3765
+ }
3766
+ if (!preview) return null;
3767
+ return {
3768
+ id: s.id,
3769
+ updatedAt: s.updatedAt,
3770
+ cwd: s.cwd || '',
3771
+ model: s.model,
3772
+ provider: s.provider,
3773
+ messageCount,
3774
+ preview,
3775
+ };
3776
+ }).filter(Boolean);
3777
+ },
3778
+ async newSession() {
3779
+ if (session?.id) mgr.closeSession(session.id, 'cli-new');
3780
+ await createCurrentSession();
3781
+ return session.id;
3782
+ },
3783
+ async resume(id) {
3784
+ const previousId = session?.id || null;
3785
+ const resumed = await mgr.resumeSession(id, toolSpecForMode(mode));
3786
+ if (!resumed) return null;
3787
+ if (previousId && previousId !== resumed.id) {
3788
+ statusRoutes?.clearGatewaySessionRoute?.(previousId);
3789
+ mgr.closeSession(previousId, 'cli-resume');
3790
+ }
3791
+ session = resumed;
3792
+ currentCwd = resumed.cwd || currentCwd;
3793
+ applyResolvedCwd(currentCwd, { markRefresh: false });
3794
+ const resumeEffort = hasOwn(route, 'effort') ? route.effort : resumed.effort;
3795
+ route = resolveRoute(config, { provider: resumed.provider, model: resumed.model, effort: resumeEffort });
3796
+ await refreshRouteEffort();
3797
+ session.effort = route.effectiveEffort || null;
3798
+ session.cwd = currentCwd;
3799
+ applyDeferredToolSurface(session, mode, standaloneTools);
3800
+ invalidatePreSessionToolSurface();
3801
+ invalidateContextStatusCache();
3802
+ sessionNeedsCwdRefresh = false;
3803
+ statusRoutes?.writeGatewaySessionRoute?.(session.id, routeForStatusline(route));
3804
+ return {
3805
+ id: resumed.id,
3806
+ messages: resumed.messages || [],
3807
+ cwd: currentCwd,
3808
+ provider: resumed.provider,
3809
+ model: resumed.model,
3810
+ };
3811
+ },
3812
+ };
3813
+ }