mixdog 0.7.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 (404) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.gitattributes +34 -0
  4. package/.mcp.json +14 -0
  5. package/ARCHITECTURE.md +77 -0
  6. package/CHANGELOG.md +7 -0
  7. package/CONTRIBUTING.md +45 -0
  8. package/DATA-FLOW.md +79 -0
  9. package/LICENSE +21 -0
  10. package/README.md +389 -0
  11. package/SECURITY.md +138 -0
  12. package/UNINSTALL.md +112 -0
  13. package/agents/maintenance.md +5 -0
  14. package/agents/memory-classification.md +30 -0
  15. package/agents/scheduler-task.md +18 -0
  16. package/agents/webhook-handler.md +27 -0
  17. package/agents/worker.md +24 -0
  18. package/bin/bridge +133 -0
  19. package/bin/statusline-launcher.mjs +78 -0
  20. package/bin/statusline-lib.mjs +550 -0
  21. package/bin/statusline.mjs +607 -0
  22. package/bun.lock +802 -0
  23. package/commands/config.md +16 -0
  24. package/commands/doctor.md +13 -0
  25. package/commands/setup.md +17 -0
  26. package/defaults/cycle3-review-prompt.md +90 -0
  27. package/defaults/hidden-roles.json +65 -0
  28. package/defaults/memory-chunk-prompt.md +63 -0
  29. package/defaults/memory-promote-prompt.md +135 -0
  30. package/defaults/mixdog-config.template.json +27 -0
  31. package/defaults/user-workflow.json +8 -0
  32. package/defaults/user-workflow.md +12 -0
  33. package/hooks/hooks.json +73 -0
  34. package/hooks/lib/active-instance.cjs +77 -0
  35. package/hooks/lib/permission-evaluator.cjs +411 -0
  36. package/hooks/lib/permission-route.cjs +63 -0
  37. package/hooks/lib/permission-rules.cjs +170 -0
  38. package/hooks/lib/settings-loader.cjs +116 -0
  39. package/hooks/post-tool-use.cjs +84 -0
  40. package/hooks/pre-mcp-sandbox.cjs +158 -0
  41. package/hooks/pre-tool-subagent.cjs +253 -0
  42. package/hooks/session-start.cjs +1372 -0
  43. package/hooks/turn-timer.cjs +82 -0
  44. package/lib/claude-md-writer.cjs +386 -0
  45. package/lib/config-cjs.cjs +61 -0
  46. package/lib/hook-pipe-path.cjs +10 -0
  47. package/lib/keychain-cjs.cjs +263 -0
  48. package/lib/plugin-paths.cjs +61 -0
  49. package/lib/rules-builder.cjs +241 -0
  50. package/lib/text-utils.cjs +61 -0
  51. package/native/README.md +117 -0
  52. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  53. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  54. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  55. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  56. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  57. package/package.json +107 -0
  58. package/prompts/code-review.txt +16 -0
  59. package/prompts/security-audit.txt +17 -0
  60. package/rules/bridge/00-common.md +39 -0
  61. package/rules/bridge/20-skip-protocol.md +18 -0
  62. package/rules/bridge/30-explorer.md +33 -0
  63. package/rules/bridge/40-cycle1-agent.md +52 -0
  64. package/rules/bridge/41-cycle2-agent.md +62 -0
  65. package/rules/bridge/42-cycle3-agent.md +44 -0
  66. package/rules/lead/00-tool-lead.md +61 -0
  67. package/rules/lead/01-general.md +23 -0
  68. package/rules/lead/02-channels.md +49 -0
  69. package/rules/lead/03-team.md +27 -0
  70. package/rules/lead/04-workflow.md +20 -0
  71. package/rules/shared/00-language.md +14 -0
  72. package/rules/shared/01-tool.md +138 -0
  73. package/scripts/bootstrap.mjs +184 -0
  74. package/scripts/bridge-unify-smoke.mjs +308 -0
  75. package/scripts/build-runtime-linux.sh +348 -0
  76. package/scripts/build-runtime-macos.sh +217 -0
  77. package/scripts/build-runtime-windows.ps1 +242 -0
  78. package/scripts/builtin-utils-smoke.mjs +392 -0
  79. package/scripts/check-json.mjs +45 -0
  80. package/scripts/check-syntax-changed.mjs +102 -0
  81. package/scripts/check-syntax.mjs +58 -0
  82. package/scripts/code-graph-batch.test.mjs +33 -0
  83. package/scripts/config-preserve-smoke.mjs +180 -0
  84. package/scripts/doctor.mjs +484 -0
  85. package/scripts/edit-normalize-fuzz.mjs +130 -0
  86. package/scripts/edit-normalize-smoke.mjs +401 -0
  87. package/scripts/edit-operation-smoke.mjs +369 -0
  88. package/scripts/edit2-smoke.mjs +63 -0
  89. package/scripts/fuzzy-e2e.mjs +28 -0
  90. package/scripts/fuzzy-smoke.mjs +26 -0
  91. package/scripts/generate-runtime-manifest.mjs +166 -0
  92. package/scripts/guard-smoke.mjs +66 -0
  93. package/scripts/hidden-role-schema-smoke.mjs +162 -0
  94. package/scripts/hook-routing-smoke.mjs +29 -0
  95. package/scripts/inject-input.ps1 +204 -0
  96. package/scripts/io-complex-smoke.mjs +667 -0
  97. package/scripts/io-explore-bench.mjs +424 -0
  98. package/scripts/io-guardrails-smoke.mjs +205 -0
  99. package/scripts/io-mini-bench-baseline.json +11 -0
  100. package/scripts/io-mini-bench.mjs +216 -0
  101. package/scripts/io-route-harness.mjs +933 -0
  102. package/scripts/io-telemetry-report.mjs +691 -0
  103. package/scripts/mutation-bench.mjs +564 -0
  104. package/scripts/mutation-io-smoke.mjs +1081 -0
  105. package/scripts/native-patch-bridge-smoke.mjs +288 -0
  106. package/scripts/native-patch-smoke.mjs +304 -0
  107. package/scripts/patch-interior-context-smoke.mjs +49 -0
  108. package/scripts/patch-newline-utf8-smoke.mjs +157 -0
  109. package/scripts/perf-hook-smoke.mjs +71 -0
  110. package/scripts/permission-eval-smoke.mjs +426 -0
  111. package/scripts/prep-patch.mjs +53 -0
  112. package/scripts/prep-shim.mjs +96 -0
  113. package/scripts/provider-cache-smoke.mjs +687 -0
  114. package/scripts/report-runtime-health.mjs +132 -0
  115. package/scripts/run-mcp.mjs +1547 -0
  116. package/scripts/salvage-v4a-shatter.test.mjs +58 -0
  117. package/scripts/scoped-cache-io-smoke.mjs +103 -0
  118. package/scripts/shell-policy-round3-smoke.mjs +46 -0
  119. package/scripts/smoke-runtime-negative.ps1 +100 -0
  120. package/scripts/smoke-runtime-negative.sh +95 -0
  121. package/scripts/stall-policy-smoke.mjs +50 -0
  122. package/scripts/start-memory-worker.mjs +23 -0
  123. package/scripts/statusline-launcher-smoke.mjs +82 -0
  124. package/scripts/stress-atomic-write.mjs +1028 -0
  125. package/scripts/test-config-rmw-restore.mjs +122 -0
  126. package/scripts/test-fault-inject.mjs +164 -0
  127. package/scripts/test-large-file.mjs +174 -0
  128. package/scripts/tool-edge-smoke.mjs +209 -0
  129. package/scripts/uninstall.mjs +201 -0
  130. package/scripts/webhook-selfheal-smoke.mjs +29 -0
  131. package/scripts/write-overwrite-guard-smoke.mjs +56 -0
  132. package/server-main.mjs +3055 -0
  133. package/server.mjs +468 -0
  134. package/setup/config-merge.mjs +254 -0
  135. package/setup/install.mjs +120 -0
  136. package/setup/launch-core.mjs +507 -0
  137. package/setup/launch.mjs +101 -0
  138. package/setup/setup-server.mjs +3206 -0
  139. package/setup/setup.html +3693 -0
  140. package/skills/retro-skill-proposer/SKILL.md +92 -0
  141. package/skills/schedule-add/SKILL.md +77 -0
  142. package/skills/setup/SKILL.md +346 -0
  143. package/skills/webhook-add/SKILL.md +81 -0
  144. package/src/agent/bridge-stall-watchdog.mjs +337 -0
  145. package/src/agent/index.mjs +2138 -0
  146. package/src/agent/orchestrator/activity-bus.mjs +38 -0
  147. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
  148. package/src/agent/orchestrator/bridge-retry.mjs +220 -0
  149. package/src/agent/orchestrator/bridge-trace.mjs +583 -0
  150. package/src/agent/orchestrator/cache-mtime.mjs +58 -0
  151. package/src/agent/orchestrator/config.mjs +358 -0
  152. package/src/agent/orchestrator/context/collect.mjs +651 -0
  153. package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
  154. package/src/agent/orchestrator/drain-registry.mjs +50 -0
  155. package/src/agent/orchestrator/explore-validator.mjs +8 -0
  156. package/src/agent/orchestrator/internal-roles.mjs +118 -0
  157. package/src/agent/orchestrator/internal-tools.mjs +88 -0
  158. package/src/agent/orchestrator/jobs.mjs +116 -0
  159. package/src/agent/orchestrator/mcp/client.mjs +364 -0
  160. package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
  161. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
  162. package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
  163. package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
  164. package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
  165. package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
  166. package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
  167. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
  168. package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
  169. package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
  170. package/src/agent/orchestrator/providers/registry.mjs +192 -0
  171. package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
  172. package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
  173. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  174. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  175. package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
  176. package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
  177. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
  178. package/src/agent/orchestrator/session/cache/util.mjs +49 -0
  179. package/src/agent/orchestrator/session/loop.mjs +1478 -0
  180. package/src/agent/orchestrator/session/manager.mjs +1975 -0
  181. package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
  182. package/src/agent/orchestrator/session/result-classification.mjs +65 -0
  183. package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
  184. package/src/agent/orchestrator/session/store.mjs +624 -0
  185. package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
  186. package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  187. package/src/agent/orchestrator/session/trim.mjs +491 -0
  188. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
  189. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
  190. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
  191. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
  192. package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
  193. package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
  194. package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
  195. package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
  196. package/src/agent/orchestrator/stall-policy.mjs +195 -0
  197. package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
  198. package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
  199. package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
  200. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  201. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
  202. package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
  203. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
  204. package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
  205. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
  206. package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
  207. package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
  208. package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
  209. package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
  210. package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
  211. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
  212. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
  213. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
  214. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
  215. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
  216. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
  217. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
  218. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
  219. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
  220. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
  221. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
  222. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
  223. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
  224. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
  225. package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
  226. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
  227. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
  228. package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
  229. package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
  230. package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
  231. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
  232. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
  233. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
  234. package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
  235. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
  236. package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
  237. package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
  238. package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
  239. package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
  240. package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
  241. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  242. package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
  243. package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
  244. package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
  245. package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
  246. package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
  247. package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
  248. package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
  249. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
  250. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
  251. package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
  252. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  253. package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
  254. package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
  255. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
  256. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
  257. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
  258. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
  259. package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
  260. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
  261. package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
  262. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
  263. package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
  264. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
  265. package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
  266. package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
  267. package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
  268. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
  269. package/src/agent/orchestrator/tools/builtin.mjs +500 -0
  270. package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
  271. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  272. package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
  273. package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
  274. package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  275. package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
  276. package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
  277. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
  278. package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
  279. package/src/agent/orchestrator/tools/host-input.mjs +204 -0
  280. package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
  281. package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
  282. package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
  283. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
  284. package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
  285. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
  286. package/src/agent/orchestrator/tools/patch.mjs +2754 -0
  287. package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
  288. package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
  289. package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
  290. package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
  291. package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
  292. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
  293. package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
  294. package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
  295. package/src/agent/orchestrator/workflow-store.mjs +93 -0
  296. package/src/agent/tool-defs.mjs +103 -0
  297. package/src/channels/backends/discord.mjs +784 -0
  298. package/src/channels/data/voice-runtime-manifest.json +138 -0
  299. package/src/channels/index.mjs +3229 -0
  300. package/src/channels/lib/cli-worker-host.mjs +12 -0
  301. package/src/channels/lib/config-lock.mjs +13 -0
  302. package/src/channels/lib/config.mjs +292 -0
  303. package/src/channels/lib/drop-trace.mjs +71 -0
  304. package/src/channels/lib/event-pipeline.mjs +81 -0
  305. package/src/channels/lib/event-queue.mjs +345 -0
  306. package/src/channels/lib/executor.mjs +168 -0
  307. package/src/channels/lib/format.mjs +188 -0
  308. package/src/channels/lib/holidays.mjs +138 -0
  309. package/src/channels/lib/hook-pipe-server.mjs +802 -0
  310. package/src/channels/lib/interaction-workflows.mjs +184 -0
  311. package/src/channels/lib/memory-client.mjs +149 -0
  312. package/src/channels/lib/output-forwarder.mjs +765 -0
  313. package/src/channels/lib/runtime-paths.mjs +479 -0
  314. package/src/channels/lib/scheduler.mjs +723 -0
  315. package/src/channels/lib/session-control.mjs +36 -0
  316. package/src/channels/lib/session-discovery.mjs +103 -0
  317. package/src/channels/lib/settings.mjs +11 -0
  318. package/src/channels/lib/state-file.mjs +68 -0
  319. package/src/channels/lib/status-snapshot.mjs +219 -0
  320. package/src/channels/lib/tool-format.mjs +140 -0
  321. package/src/channels/lib/transcript-discovery.mjs +195 -0
  322. package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
  323. package/src/channels/lib/webhook.mjs +1179 -0
  324. package/src/channels/lib/whisper-server.mjs +477 -0
  325. package/src/channels/tool-defs.mjs +170 -0
  326. package/src/daemon/host.mjs +118 -0
  327. package/src/daemon/mcp-transport.mjs +47 -0
  328. package/src/daemon/session.mjs +100 -0
  329. package/src/daemon/thin-client.mjs +71 -0
  330. package/src/daemon/transport.mjs +163 -0
  331. package/src/memory/data/runtime-manifest.json +40 -0
  332. package/src/memory/index.mjs +3305 -0
  333. package/src/memory/lib/agent-ipc.mjs +93 -0
  334. package/src/memory/lib/bridge-trace-queries.mjs +120 -0
  335. package/src/memory/lib/core-memory-store.mjs +330 -0
  336. package/src/memory/lib/embedding-provider.mjs +269 -0
  337. package/src/memory/lib/embedding-worker.mjs +323 -0
  338. package/src/memory/lib/llm-worker-host.mjs +17 -0
  339. package/src/memory/lib/memory-cycle.mjs +11 -0
  340. package/src/memory/lib/memory-cycle1.mjs +641 -0
  341. package/src/memory/lib/memory-cycle2.mjs +1284 -0
  342. package/src/memory/lib/memory-cycle3.mjs +540 -0
  343. package/src/memory/lib/memory-embed.mjs +299 -0
  344. package/src/memory/lib/memory-extraction.mjs +5 -0
  345. package/src/memory/lib/memory-maintenance-store.mjs +32 -0
  346. package/src/memory/lib/memory-ops-policy.mjs +190 -0
  347. package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
  348. package/src/memory/lib/memory-recall-read-query.mjs +7 -0
  349. package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
  350. package/src/memory/lib/memory-recall-store.mjs +621 -0
  351. package/src/memory/lib/memory-retrievers.mjs +112 -0
  352. package/src/memory/lib/memory-score.mjs +71 -0
  353. package/src/memory/lib/memory-text-utils.mjs +58 -0
  354. package/src/memory/lib/memory.mjs +412 -0
  355. package/src/memory/lib/model-profile.mjs +85 -0
  356. package/src/memory/lib/pg/adapter.mjs +308 -0
  357. package/src/memory/lib/pg/process.mjs +360 -0
  358. package/src/memory/lib/pg/supervisor.mjs +396 -0
  359. package/src/memory/lib/project-id-resolver.mjs +86 -0
  360. package/src/memory/lib/runtime-fetcher.mjs +442 -0
  361. package/src/memory/lib/trace-store.mjs +728 -0
  362. package/src/memory/tool-defs.mjs +79 -0
  363. package/src/search/index.mjs +1173 -0
  364. package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
  365. package/src/search/lib/backends/exa.mjs +50 -0
  366. package/src/search/lib/backends/firecrawl.mjs +61 -0
  367. package/src/search/lib/backends/gemini-api.mjs +83 -0
  368. package/src/search/lib/backends/grok-oauth.mjs +86 -0
  369. package/src/search/lib/backends/index.mjs +150 -0
  370. package/src/search/lib/backends/openai-api.mjs +144 -0
  371. package/src/search/lib/backends/openai-oauth.mjs +98 -0
  372. package/src/search/lib/backends/openai-web-search.mjs +76 -0
  373. package/src/search/lib/backends/tavily.mjs +55 -0
  374. package/src/search/lib/backends/xai-api.mjs +113 -0
  375. package/src/search/lib/cache.mjs +131 -0
  376. package/src/search/lib/config.mjs +192 -0
  377. package/src/search/lib/formatter.mjs +115 -0
  378. package/src/search/lib/provider-usage.mjs +67 -0
  379. package/src/search/lib/providers.mjs +47 -0
  380. package/src/search/lib/search-intent.mjs +109 -0
  381. package/src/search/lib/setup-handler.mjs +261 -0
  382. package/src/search/lib/state.mjs +201 -0
  383. package/src/search/lib/web-tools.mjs +1207 -0
  384. package/src/search/tool-defs.mjs +83 -0
  385. package/src/setup/defender-exclusion.mjs +183 -0
  386. package/src/shared/abort-controller.mjs +15 -0
  387. package/src/shared/atomic-file.mjs +420 -0
  388. package/src/shared/config.mjs +350 -0
  389. package/src/shared/daemon-recycle.mjs +108 -0
  390. package/src/shared/disable-claude-builtins.mjs +88 -0
  391. package/src/shared/err-text.mjs +12 -0
  392. package/src/shared/llm/cost.mjs +66 -0
  393. package/src/shared/llm/http-agent.mjs +123 -0
  394. package/src/shared/llm/index.mjs +41 -0
  395. package/src/shared/llm/pid-cleanup.mjs +27 -0
  396. package/src/shared/llm/usage-log.mjs +47 -0
  397. package/src/shared/plugin-paths.mjs +58 -0
  398. package/src/shared/schedules-store.mjs +70 -0
  399. package/src/shared/seed.mjs +119 -0
  400. package/src/shared/user-cwd.mjs +213 -0
  401. package/src/shared/user-data-guard.mjs +238 -0
  402. package/src/status/aggregator.mjs +584 -0
  403. package/src/status/server.mjs +413 -0
  404. package/tools.json +1653 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Response formatter — strips metadata, returns human-readable text.
3
+ */
4
+
5
+ function formatSearchResults(data) {
6
+ // data may be the full jsonText payload: { tool, providers, response, cache, ... }
7
+ // response.results is the array we care about
8
+ const response = data.response || data
9
+ const results = response.results || []
10
+ const answer = String(response.answer || '').trim()
11
+ const warnings = Array.isArray(response.warnings) ? response.warnings : []
12
+
13
+ if (!results.length && !answer) {
14
+ return '(no search results)'
15
+ }
16
+
17
+ // Explicit display caps — callers that need full payload should use raw JSON output.
18
+ const TITLE_CAP = 200
19
+ const SNIPPET_CAP = 600
20
+ const ANSWER_CAP = 4000
21
+ const clip = (text, cap) => text.length > cap ? `${text.slice(0, cap)}…` : text
22
+ const blocks = []
23
+ if (warnings.length) blocks.push(`Warnings: ${warnings.join('; ')}`)
24
+ if (answer) blocks.push(clip(answer, ANSWER_CAP))
25
+ if (!results.length) return blocks.join('\n\n')
26
+ blocks.push(results
27
+ .map((r, i) => {
28
+ const num = i + 1
29
+ const title = clip(r.title || '(no title)', TITLE_CAP)
30
+ const url = r.url || ''
31
+ const date = r.publishedDate || ''
32
+ const snippet = clip((r.snippet || '').trim(), SNIPPET_CAP)
33
+
34
+ const urlPart = [url, date].filter(Boolean).join(' — ')
35
+ const lines = [`${num}. ${title}`]
36
+ if (urlPart) lines.push(` ${urlPart}`)
37
+ if (snippet) lines.push(` ${snippet}`)
38
+ return lines.join('\n')
39
+ })
40
+ .join('\n\n'))
41
+ return blocks.join('\n\n')
42
+ }
43
+
44
+ function formatCrawl(data) {
45
+ // data: { tool, pages: [{ url, depth, title, excerpt, extractor } | { url, depth, error }] }
46
+ const pages = data.pages || []
47
+
48
+ if (!pages.length) {
49
+ return '(no crawl results)'
50
+ }
51
+
52
+ return pages
53
+ .map(page => {
54
+ const url = page.url || ''
55
+ const title = page.title || ''
56
+ const excerpt = (page.excerpt || '').trim()
57
+ const error = page.error
58
+
59
+ if (error) {
60
+ return `[${url}]\n(failed)`
61
+ }
62
+
63
+ const header = title ? `[${title}] ${url}` : `[${url}]`
64
+ return `${header}\n${excerpt || '(no content)'}`
65
+ })
66
+ .join('\n\n---\n\n')
67
+ }
68
+
69
+ function formatFetch(data) {
70
+ const results = data.results || []
71
+ if (!results.length) return '(no fetch results)'
72
+ const cappedNote = data.urlsTruncated ? `[fetched first ${results.length} of ${data.urlsTruncated} URLs; raise FETCH_URL_CAP for more]\n\n` : ''
73
+
74
+ return cappedNote + results
75
+ .map(item => {
76
+ const url = item.url || ''
77
+ if (item.status === 'error' || item.error) {
78
+ return `[${url}]\n(error: ${item.error || 'unknown error'})`
79
+ }
80
+ const meta = []
81
+ if (Number.isFinite(item.bytes)) meta.push(`${item.bytes} bytes`)
82
+ if (Number.isFinite(item.totalLength) && item.range) {
83
+ meta.push(`range=${item.range.startIndex}..${item.range.endIndex}/${item.totalLength}`)
84
+ }
85
+ if (item.hasMore && item.nextStartIndex != null) {
86
+ meta.push(`next startIndex=${item.nextStartIndex}`)
87
+ }
88
+ if (Number.isFinite(item.durationMs)) meta.push(`${item.durationMs}ms`)
89
+ const header = `${url}${meta.length ? ` (${meta.join(', ')})` : ''}`
90
+ const titleRaw = String(item.title || '').replace(/\s+/g, ' ').trim()
91
+ const titleLine = titleRaw ? `title: ${titleRaw}` : ''
92
+ const body = String(item.content || '').trim() || '(no content)'
93
+ return titleLine ? `${header}\n${titleLine}\n${body}` : `${header}\n${body}`
94
+ })
95
+ .join('\n\n---\n\n')
96
+ }
97
+
98
+ /**
99
+ * Format a tool response into human-readable text.
100
+ * @param {string} tool - Tool name (search, fetch, crawl)
101
+ * @param {object} rawResult - The raw result object that was previously passed to jsonText()
102
+ * @returns {string} Formatted text
103
+ */
104
+ export function formatResponse(tool, rawResult) {
105
+ switch (tool) {
106
+ case 'search':
107
+ return formatSearchResults(rawResult)
108
+ case 'crawl':
109
+ return formatCrawl(rawResult)
110
+ case 'fetch':
111
+ return formatFetch(rawResult)
112
+ default:
113
+ return JSON.stringify(rawResult, null, 2)
114
+ }
115
+ }
@@ -0,0 +1,67 @@
1
+ async function fetchJson(url, apiKey) {
2
+ const response = await fetch(url, {
3
+ signal: AbortSignal.timeout(10_000),
4
+ headers: {
5
+ Authorization: `Bearer ${apiKey}`,
6
+ },
7
+ })
8
+
9
+ if (!response.ok) {
10
+ // Drain/cancel the body before throwing so the underlying socket isn't
11
+ // held open until GC.
12
+ try { await response.body?.cancel() } catch {}
13
+ throw new Error(`Usage request failed: ${response.status}`)
14
+ }
15
+
16
+ return response.json()
17
+ }
18
+
19
+ async function fetchFirecrawlUsage(apiKey) {
20
+ if (!apiKey) return null
21
+ const payload = await fetchJson('https://api.firecrawl.dev/v2/team/credit-usage', apiKey)
22
+ const data = payload?.data
23
+ if (!data) return null
24
+
25
+ return {
26
+ remaining: typeof data.remainingCredits === 'number' ? data.remainingCredits : null,
27
+ limit:
28
+ typeof data.planCredits === 'number' && data.planCredits > 0
29
+ ? data.planCredits
30
+ : null,
31
+ resetAt: data.billingPeriodEnd || null,
32
+ }
33
+ }
34
+
35
+ async function fetchTavilyUsage(apiKey) {
36
+ if (!apiKey) return null
37
+ const payload = await fetchJson('https://api.tavily.com/usage', apiKey)
38
+ const key = payload?.key
39
+ if (!key) return null
40
+
41
+ const usage = typeof key.usage === 'number' ? key.usage : null
42
+ const limit =
43
+ typeof key.limit === 'number' && key.limit > 0
44
+ ? key.limit
45
+ : null
46
+
47
+ return {
48
+ remaining: usage !== null && limit !== null ? Math.max(limit - usage, 0) : null,
49
+ limit,
50
+ resetAt: null,
51
+ }
52
+ }
53
+
54
+ export async function fetchProviderUsageSnapshot(provider, env = process.env) {
55
+ switch (provider) {
56
+ case 'firecrawl':
57
+ return fetchFirecrawlUsage(env.FIRECRAWL_API_KEY)
58
+ case 'tavily':
59
+ return fetchTavilyUsage(env.TAVILY_API_KEY)
60
+ default:
61
+ // No telemetry endpoint just means "no usage patch to apply" — it does
62
+ // NOT mean the provider is unavailable. Returning {available:false}
63
+ // here would spread over the {available:true} baseline in
64
+ // writeStartupSnapshot and mark a configured provider as down.
65
+ return null
66
+ }
67
+ }
@@ -0,0 +1,47 @@
1
+ export const PROVIDER_OVERRIDE_ENUM = [
2
+ 'anthropic-oauth',
3
+ 'openai-oauth',
4
+ 'openai-api',
5
+ 'gemini-api',
6
+ 'xai-api',
7
+ 'grok-oauth',
8
+ 'tavily',
9
+ 'firecrawl',
10
+ 'exa',
11
+ ]
12
+
13
+ export const RAW_PROVIDER_CAPABILITIES = {
14
+ firecrawl: {
15
+ usageSupport: {
16
+ available: true,
17
+ timestamps: true,
18
+ cost: false,
19
+ quota: true,
20
+ },
21
+ },
22
+ tavily: {
23
+ usageSupport: {
24
+ available: true,
25
+ timestamps: true,
26
+ cost: false,
27
+ quota: true,
28
+ },
29
+ },
30
+ 'xai-api': {
31
+ usageSupport: {
32
+ available: true,
33
+ timestamps: true,
34
+ cost: true,
35
+ quota: false,
36
+ },
37
+ },
38
+ }
39
+
40
+ export function getProvidersWithApiKeys(env = process.env) {
41
+ const providers = []
42
+ if (env.FIRECRAWL_API_KEY) providers.push('firecrawl')
43
+ if (env.TAVILY_API_KEY) providers.push('tavily')
44
+ if (env.EXA_API_KEY) providers.push('exa')
45
+ if (env.XAI_API_KEY || env.GROK_API_KEY) providers.push('xai-api')
46
+ return providers
47
+ }
@@ -0,0 +1,109 @@
1
+ const SEARCH_TYPES = Object.freeze(['web', 'news', 'images'])
2
+ const SEARCH_CONTEXT_SIZES = Object.freeze(['low', 'medium', 'high'])
3
+
4
+ function normalizeLocale(value) {
5
+ if (!value) return null
6
+ if (typeof value === 'object' && !Array.isArray(value)) {
7
+ const locale = {
8
+ country: value.country ? String(value.country).trim().toUpperCase() : undefined,
9
+ language: value.language ? String(value.language).trim().toLowerCase() : undefined,
10
+ region: value.region ? String(value.region).trim() : undefined,
11
+ city: value.city ? String(value.city).trim() : undefined,
12
+ timezone: value.timezone ? String(value.timezone).trim() : undefined,
13
+ explicit: true,
14
+ }
15
+ return Object.fromEntries(Object.entries(locale).filter(([, v]) => v !== undefined && v !== ''))
16
+ }
17
+ const raw = String(value || '').trim().replace(/_/g, '-')
18
+ if (!raw) return null
19
+ const parts = raw.split('-').filter(Boolean)
20
+ if (parts.length >= 2) {
21
+ return { language: parts[0].toLowerCase(), country: parts[1].toUpperCase(), explicit: true }
22
+ }
23
+ const single = parts[0] || raw
24
+ if (/^[A-Z]{2}$/.test(single)) return { country: single, explicit: true }
25
+ if (/^[a-z]{2}$/.test(single)) return { language: single, explicit: true }
26
+ return { country: single.toUpperCase(), explicit: true }
27
+ }
28
+
29
+ function clampMaxResults(value, fallback = 5, max = 20) {
30
+ const n = Number(value)
31
+ const base = Number.isFinite(n) ? Math.floor(n) : Number(fallback)
32
+ const safe = Number.isFinite(base) && base > 0 ? base : 5
33
+ return Math.max(1, Math.min(max, safe))
34
+ }
35
+
36
+ function normalizeContextSize(value, fallback = 'low') {
37
+ const raw = String(value || '').trim().toLowerCase()
38
+ if (SEARCH_CONTEXT_SIZES.includes(raw)) return raw
39
+ const fb = String(fallback || '').trim().toLowerCase()
40
+ return SEARCH_CONTEXT_SIZES.includes(fb) ? fb : 'low'
41
+ }
42
+
43
+ function normalizeSite(site) {
44
+ const raw = String(site || '').trim()
45
+ if (!raw) return ''
46
+ const withoutPrefix = raw.replace(/^site:/i, '').trim()
47
+ try {
48
+ const parsed = new URL(withoutPrefix.includes('://') ? withoutPrefix : `https://${withoutPrefix}`)
49
+ return parsed.hostname.replace(/^www\./i, '').toLowerCase()
50
+ } catch {
51
+ return withoutPrefix
52
+ .replace(/^https?:\/\//i, '')
53
+ .replace(/^www\./i, '')
54
+ .replace(/\/.*$/, '')
55
+ .trim()
56
+ .toLowerCase()
57
+ }
58
+ }
59
+
60
+ export function normalizeSearchIntent(raw = {}, { caps = {}, defaultMaxResults = 5 } = {}) {
61
+ const rawQuery = String(raw.query ?? raw.keywords ?? '').trim()
62
+ if (!rawQuery) throw new Error('query is required')
63
+
64
+ const supportedTypes = Array.isArray(caps.searchTypes) && caps.searchTypes.length
65
+ ? caps.searchTypes
66
+ : ['web']
67
+ const requestedType = SEARCH_TYPES.includes(String(raw.type || '').trim())
68
+ ? String(raw.type).trim()
69
+ : 'web'
70
+ const type = supportedTypes.includes(requestedType) ? requestedType : 'web'
71
+ const site = normalizeSite(raw.site)
72
+ const queryWithSite = site && !/\bsite:/i.test(rawQuery)
73
+ ? `${rawQuery} site:${site}`
74
+ : rawQuery
75
+ const warnings = []
76
+ if (requestedType !== type) {
77
+ warnings.push(`search type "${requestedType}" is not supported by this provider; used "${type}"`)
78
+ }
79
+ const locale = normalizeLocale(raw.locale)
80
+ if (locale && caps.localeMode === 'none') {
81
+ warnings.push('locale is not supported by this provider; ignored')
82
+ }
83
+
84
+ return {
85
+ query: rawQuery,
86
+ queryWithSite,
87
+ rawQuery,
88
+ site,
89
+ type,
90
+ requestedType,
91
+ maxResults: clampMaxResults(raw.maxResults, defaultMaxResults),
92
+ contextSize: normalizeContextSize(raw.contextSize ?? raw.searchContextSize, raw.defaultContextSize || 'low'),
93
+ locale,
94
+ warnings,
95
+ }
96
+ }
97
+
98
+ export function tavilyCountryName(country) {
99
+ const code = String(country || 'US').toUpperCase()
100
+ const names = {
101
+ CN: 'china',
102
+ JP: 'japan',
103
+ KR: 'south korea',
104
+ RU: 'russia',
105
+ SA: 'saudi arabia',
106
+ US: 'united states',
107
+ }
108
+ return names[code] || 'united states'
109
+ }
@@ -0,0 +1,261 @@
1
+ import { loadConfig, saveConfig } from './config.mjs'
2
+ import { getSearchApiKey, saveSecret, SECRET_ACCOUNTS } from '../../shared/config.mjs'
3
+ import { DEFAULT_MODELS } from './config.mjs'
4
+ import { PROVIDER_OVERRIDE_ENUM } from './providers.mjs'
5
+
6
+ const PROVIDER_CHOICES = PROVIDER_OVERRIDE_ENUM
7
+
8
+ const SEARCH_KEY_PROVIDERS = ['firecrawl', 'tavily', 'exa']
9
+
10
+ function mask(key) {
11
+ if (!key) return ' not set'
12
+ return ' ****' + key.slice(-4)
13
+ }
14
+
15
+ function icon(key) {
16
+ return key ? '●' : '○'
17
+ }
18
+
19
+ function statusBlock(config) {
20
+ const activeProvider = config.provider || 'anthropic-oauth'
21
+ const models = config.models || {}
22
+
23
+ const lines = [
24
+ '',
25
+ ' ╭───────────────────────────────────────╮',
26
+ ' │ mixdog-search config │',
27
+ ' ╰───────────────────────────────────────╯',
28
+ '',
29
+ ' Active Backend',
30
+ ' ────────────────────────────────────────',
31
+ ` provider ${activeProvider}`,
32
+ ]
33
+ if (activeProvider === 'openai-oauth' || activeProvider === 'openai-api') {
34
+ lines.push(` openai model ${models.openai || 'gpt-5.5'}`)
35
+ } else if (activeProvider === 'gemini-api') {
36
+ lines.push(` gemini model ${models.gemini || DEFAULT_MODELS.gemini}`)
37
+ } else if (activeProvider === 'xai-api') {
38
+ lines.push(` xai model ${models.xai || 'grok-4.3'}`)
39
+ } else if (activeProvider === 'anthropic-oauth') {
40
+ lines.push(` model claude-haiku-4-5 (fixed)`)
41
+ }
42
+
43
+ lines.push('')
44
+ lines.push(' Search-Specific Keys')
45
+ lines.push(' ────────────────────────────────────────')
46
+ for (const p of SEARCH_KEY_PROVIDERS) {
47
+ const key = getSearchApiKey(p)
48
+ lines.push(` ${icon(key)} ${p.padEnd(12)}${mask(key)}`)
49
+ }
50
+ lines.push('')
51
+ lines.push(' Options')
52
+ lines.push(' ────────────────────────────────────────')
53
+ lines.push(` max results ${config.rawSearch?.maxResults || 10}`)
54
+ lines.push(` crawl ${config.crawl?.maxPages || 10} pages / depth ${config.crawl?.maxDepth || 1}`)
55
+ lines.push('')
56
+ return lines.join('\n')
57
+ }
58
+
59
+ function sectionHeader(config) {
60
+ const activeProvider = config.provider || 'anthropic-oauth'
61
+ const total = SEARCH_KEY_PROVIDERS.filter(p => !!getSearchApiKey(p)).length
62
+ return [
63
+ ' ╭───────────────────────────────────────╮',
64
+ ' │ mixdog-search setup │',
65
+ ' ╰───────────────────────────────────────╯',
66
+ '',
67
+ ` active provider: ${activeProvider}`,
68
+ ` ${total > 0 ? '●' : '○'} ${total} search-specific key(s) configured`,
69
+ '',
70
+ ].join('\n')
71
+ }
72
+
73
+ function keysHeader(title, entries) {
74
+ const lines = [
75
+ ' ╭───────────────────────────────────────╮',
76
+ ` │ ${title.padEnd(37)}│`,
77
+ ' ╰───────────────────────────────────────╯',
78
+ '',
79
+ ' empty = keep current / "clear" = remove',
80
+ '',
81
+ ]
82
+ for (const [name, key] of entries) {
83
+ lines.push(` ${icon(key)} ${name.padEnd(12)}${mask(key)}`)
84
+ }
85
+ return lines.join('\n')
86
+ }
87
+
88
+ function applyKeys(config, data) {
89
+ for (const [provider, value] of Object.entries(data)) {
90
+ if (value == null) continue
91
+ const trimmed = String(value).trim()
92
+ if (!trimmed) continue
93
+ const isClear = trimmed.toLowerCase() === 'clear'
94
+ if (isClear) {
95
+ saveSecret(SECRET_ACCOUNTS.searchApiKey(provider), '')
96
+ } else {
97
+ // Validate: API keys must be non-empty printable ASCII, no whitespace
98
+ if (!/^[\x21-\x7E]+$/.test(trimmed)) {
99
+ throw new Error(`[setup] invalid API key for ${provider}: must be printable ASCII with no whitespace`)
100
+ }
101
+ saveSecret(SECRET_ACCOUNTS.searchApiKey(provider), trimmed)
102
+ }
103
+ // Remove plaintext copy from config object before save
104
+ if (config.rawSearch?.credentials?.[provider]?.apiKey != null) {
105
+ delete config.rawSearch.credentials[provider].apiKey
106
+ }
107
+ }
108
+ }
109
+
110
+ function save(config) {
111
+ saveConfig(config)
112
+ }
113
+
114
+ export async function handleSetup(server) {
115
+ const config = loadConfig()
116
+
117
+ const step1 = await server.elicitInput({
118
+ message: sectionHeader(config),
119
+ requestedSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ section: {
123
+ type: 'string',
124
+ title: 'Section',
125
+ enum: ['provider', 'search-keys', 'options', 'status'],
126
+ },
127
+ },
128
+ required: ['section'],
129
+ },
130
+ })
131
+
132
+ if (step1.action !== 'accept') {
133
+ return { content: [{ type: 'text', text: statusBlock(config) }] }
134
+ }
135
+
136
+ const section = step1.content.section
137
+
138
+ if (section === 'status') {
139
+ return { content: [{ type: 'text', text: statusBlock(config) }] }
140
+ }
141
+
142
+ if (section === 'provider') {
143
+ const result = await server.elicitInput({
144
+ message: [
145
+ ' ╭───────────────────────────────────────╮',
146
+ ' │ Active Search Provider │',
147
+ ' ╰───────────────────────────────────────╯',
148
+ '',
149
+ ` current: ${config.provider || 'anthropic-oauth'}`,
150
+ '',
151
+ ' OAuth backends reuse the matching agent.providers',
152
+ ' OAuth tokens; search-specific backends (firecrawl,',
153
+ ' tavily, exa) use the keys set in `search-keys`.',
154
+ '',
155
+ ].join('\n'),
156
+ requestedSchema: {
157
+ type: 'object',
158
+ properties: {
159
+ provider: {
160
+ type: 'string',
161
+ title: 'Search Provider',
162
+ enum: PROVIDER_CHOICES,
163
+ },
164
+ openaiModel: {
165
+ type: 'string',
166
+ title: 'OpenAI model preset (used when provider=openai-oauth/openai-api)',
167
+ enum: ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-4o', 'gpt-4.1'],
168
+ },
169
+ geminiModel: {
170
+ type: 'string',
171
+ title: 'Gemini model preset (used when provider=gemini-api)',
172
+ enum: ['gemini-3-flash-preview', 'gemini-3.1-flash', 'gemini-3-flash', 'gemini-2.5-flash', 'gemini-2.5-pro'],
173
+ },
174
+ xaiModel: {
175
+ type: 'string',
176
+ title: 'xAI model preset (used when provider=xai-api)',
177
+ enum: ['grok-4.3', 'grok-4.20-fast', 'grok-composer-2.5-fast'],
178
+ },
179
+ },
180
+ },
181
+ })
182
+ if (result.action === 'accept' && result.content) {
183
+ const d = result.content
184
+ if (typeof d.provider === 'string' && PROVIDER_CHOICES.includes(d.provider)) {
185
+ config.provider = d.provider
186
+ }
187
+ if (!config.models) config.models = {}
188
+ if (typeof d.openaiModel === 'string' && d.openaiModel.trim()) config.models.openai = d.openaiModel.trim()
189
+ if (typeof d.geminiModel === 'string' && d.geminiModel.trim()) config.models.gemini = d.geminiModel.trim()
190
+ if (typeof d.xaiModel === 'string' && d.xaiModel.trim()) config.models.xai = d.xaiModel.trim()
191
+ save(config)
192
+ return { content: [{ type: 'text', text: ' ✓ Provider saved.\n' + statusBlock(loadConfig()) }] }
193
+ }
194
+ return { content: [{ type: 'text', text: ' ⏎ Cancelled.' }] }
195
+ }
196
+
197
+ if (section === 'search-keys') {
198
+ const result = await server.elicitInput({
199
+ message: keysHeader('Search-Specific Keys', [
200
+ ['firecrawl', getSearchApiKey('firecrawl')],
201
+ ['tavily', getSearchApiKey('tavily')],
202
+ ['exa', getSearchApiKey('exa')],
203
+ ]),
204
+ requestedSchema: {
205
+ type: 'object',
206
+ properties: {
207
+ firecrawl: { type: 'string', title: 'Firecrawl' },
208
+ tavily: { type: 'string', title: 'Tavily' },
209
+ exa: { type: 'string', title: 'Exa' },
210
+ },
211
+ },
212
+ })
213
+
214
+ if (result.action === 'accept' && result.content) {
215
+ try {
216
+ applyKeys(config, result.content)
217
+ } catch (err) {
218
+ const message = err instanceof Error ? err.message : String(err)
219
+ return { content: [{ type: 'text', text: ` ✗ Search keys not saved: ${message}` }] }
220
+ }
221
+ save(config)
222
+ return { content: [{ type: 'text', text: ' ✓ Search keys saved.\n' + statusBlock(loadConfig()) }] }
223
+ }
224
+ return { content: [{ type: 'text', text: ' ⏎ Cancelled.' }] }
225
+ }
226
+
227
+ if (section === 'options') {
228
+ const result = await server.elicitInput({
229
+ message: [
230
+ ' ╭───────────────────────────────────────╮',
231
+ ' │ Search Options │',
232
+ ' ╰───────────────────────────────────────╯',
233
+ '',
234
+ ` max results ${config.rawSearch?.maxResults || 10}`,
235
+ ` crawl pages ${config.crawl?.maxPages || 10}`,
236
+ ` crawl depth ${config.crawl?.maxDepth || 1}`,
237
+ ` same domain ${config.crawl?.sameDomainOnly ?? true}`,
238
+ ].join('\n'),
239
+ requestedSchema: {
240
+ type: 'object',
241
+ properties: {
242
+ maxResults: { type: 'integer', title: 'Max search results' },
243
+ crawlMaxPages: { type: 'integer', title: 'Crawl max pages' },
244
+ crawlMaxDepth: { type: 'integer', title: 'Crawl max depth' },
245
+ sameDomainOnly: { type: 'boolean', title: 'Same domain only' },
246
+ },
247
+ },
248
+ })
249
+
250
+ if (result.action === 'accept' && result.content) {
251
+ const d = result.content
252
+ if (d.maxResults != null) { if (!config.rawSearch) config.rawSearch = {}; config.rawSearch.maxResults = d.maxResults }
253
+ if (d.crawlMaxPages != null) { if (!config.crawl) config.crawl = {}; config.crawl.maxPages = d.crawlMaxPages }
254
+ if (d.crawlMaxDepth != null) { if (!config.crawl) config.crawl = {}; config.crawl.maxDepth = d.crawlMaxDepth }
255
+ if (d.sameDomainOnly != null) { if (!config.crawl) config.crawl = {}; config.crawl.sameDomainOnly = d.sameDomainOnly }
256
+ save(config)
257
+ return { content: [{ type: 'text', text: ' ✓ Options saved.\n' + statusBlock(loadConfig()) }] }
258
+ }
259
+ return { content: [{ type: 'text', text: ' ⏎ Cancelled.' }] }
260
+ }
261
+ }