mixdog 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,720 @@
1
+ // Deferred-tool catalog: measured-usage ordering, kind/bucket classification,
2
+ // tool_search ranking + auto-selection, and the session tool-surface
3
+ // application/selection logic. Pure module (session objects passed in).
4
+ import { clean } from './session-text.mjs';
5
+ import { estimateToolSchemaTokens } from '../runtime/agent/orchestrator/session/context-utils.mjs';
6
+ import {
7
+ isResponsesFreeformTool,
8
+ toResponsesCustomTool,
9
+ } from '../runtime/agent/orchestrator/providers/custom-tool-wire.mjs';
10
+
11
+ export const MEASURED_TOOL_USAGE = Object.freeze({
12
+ read: 710,
13
+ code_graph: 520,
14
+ grep: 500,
15
+ find: 480,
16
+ glob: 460,
17
+ list: 430,
18
+ apply_patch: 400,
19
+ explore: 360,
20
+ agent: 330,
21
+ shell: 81,
22
+ cwd: 2,
23
+ recall: 2,
24
+ search: 2,
25
+ web_fetch: 2,
26
+ });
27
+ const MEASURED_TOOL_ORDER = Object.freeze(Object.keys(MEASURED_TOOL_USAGE));
28
+ export const DEFERRED_DEFAULT_FULL_TOOLS = Object.freeze([
29
+ 'read',
30
+ 'code_graph',
31
+ 'grep',
32
+ 'find',
33
+ 'glob',
34
+ 'list',
35
+ 'explore',
36
+ 'apply_patch',
37
+ 'Skill',
38
+ 'tool_search',
39
+ ]);
40
+ export const DEFERRED_DEFAULT_READONLY_TOOLS = Object.freeze([
41
+ 'read',
42
+ 'code_graph',
43
+ 'grep',
44
+ 'find',
45
+ 'glob',
46
+ 'list',
47
+ 'explore',
48
+ 'Skill',
49
+ 'tool_search',
50
+ ]);
51
+ export const DEFERRED_DEFAULT_LEAD_TOOLS = Object.freeze([
52
+ 'read',
53
+ 'code_graph',
54
+ 'grep',
55
+ 'find',
56
+ 'glob',
57
+ 'list',
58
+ 'shell',
59
+ 'task',
60
+ 'explore',
61
+ 'apply_patch',
62
+ 'agent',
63
+ 'recall',
64
+ 'search',
65
+ 'web_fetch',
66
+ 'cwd',
67
+ 'Skill',
68
+ 'tool_search',
69
+ ]);
70
+ const READONLY_TOOL_NAMES = new Set([
71
+ 'read',
72
+ 'list',
73
+ 'grep',
74
+ 'find',
75
+ 'glob',
76
+ 'code_graph',
77
+ 'search',
78
+ 'web_fetch',
79
+ 'recall',
80
+ 'memory',
81
+ 'fetch',
82
+ 'Skill',
83
+ ]);
84
+ const DEFERRED_SELECT_ALIASES = {
85
+ filesystem: ['read', 'list', 'grep', 'find', 'glob'],
86
+ search: ['search', 'web_fetch'],
87
+ web: ['web_fetch', 'search'],
88
+ memory: ['memory', 'recall'],
89
+ channels: ['reply', 'fetch'],
90
+ discord: ['reply', 'fetch'],
91
+ explore: ['explore'],
92
+ discovery: ['explore'],
93
+ agent: ['agent'],
94
+ graph: ['code_graph'],
95
+ code: ['code_graph'],
96
+ shell: ['shell', 'task'],
97
+ };
98
+ const TOOL_SEARCH_SAFE_AUTO_ALIASES = new Set([
99
+ 'shell',
100
+ 'web',
101
+ 'search',
102
+ 'agent',
103
+ 'memory',
104
+ ]);
105
+ const TOOL_SEARCH_AMBIGUOUS_AUTO_QUERIES = new Set([
106
+ 'status',
107
+ 'state',
108
+ 'info',
109
+ 'list',
110
+ 'show',
111
+ 'config',
112
+ ]);
113
+ const TOOL_SEARCH_STOP_WORDS = new Set([
114
+ 'a',
115
+ 'an',
116
+ 'and',
117
+ 'are',
118
+ 'as',
119
+ 'for',
120
+ 'from',
121
+ 'how',
122
+ 'i',
123
+ 'in',
124
+ 'is',
125
+ 'me',
126
+ 'need',
127
+ 'of',
128
+ 'on',
129
+ 'please',
130
+ 'the',
131
+ 'to',
132
+ 'tool',
133
+ 'tools',
134
+ 'use',
135
+ 'using',
136
+ 'with',
137
+ ]);
138
+ const TOOL_SEARCH_ROW_ALIASES = Object.freeze({
139
+ agent: ['delegate', 'subagent', 'worker', 'parallel agent', 'background agent', 'reviewer', 'explorer'],
140
+ cwd: ['cwd', 'working directory', 'current directory', 'project root', 'folder'],
141
+ memory: ['save memory', 'store memory', 'delete memory', 'forget memory', 'memory status'],
142
+ recall: ['recall', 'previous work', 'past work', 'prior context', 'history', 'resume context'],
143
+ search: ['web search', 'internet search', 'current info', 'latest info', 'online search', 'docs search'],
144
+ shell: ['run command', 'execute command', 'terminal', 'powershell', 'bash', 'run tests', 'test command', 'build command', 'npm', 'node', 'git'],
145
+ task: ['background task', 'async task', 'wait task', 'cancel task', 'task status'],
146
+ web_fetch: ['fetch url', 'fetch page', 'open url', 'web page', 'read url', 'docs page'],
147
+ });
148
+
149
+ export function toolKind(tool) {
150
+ const name = clean(tool?.name);
151
+ if (name.startsWith('mcp__')) return 'mcp';
152
+ if (name.startsWith('skill:') || tool?.annotations?.mixdogKind === 'skill') return 'skill';
153
+ if (name === 'Skill' || name.startsWith('skill_') || name === 'skills_list' || name === 'skill_view') return 'skill';
154
+ if (tool?.annotations?.agentHidden) return 'control';
155
+ if (['apply_patch', 'shell'].includes(name)) return 'mutation';
156
+ return 'tool';
157
+ }
158
+
159
+ export function toolSchemaBucket(tool) {
160
+ const name = clean(tool?.name);
161
+ const kind = toolKind(tool);
162
+ if (kind === 'mcp') return 'mcp';
163
+ if (kind === 'skill') return 'skills';
164
+ if (name === 'memory' || name === 'recall' || name.includes('memory')) return 'memory';
165
+ if (name === 'search' || name === 'web_fetch') return 'web';
166
+ if (['read', 'grep', 'find', 'glob', 'list', 'code_graph', 'explore'].includes(name)) return 'code';
167
+ if (['shell', 'apply_patch'].includes(name)) return 'mutation';
168
+ if (name === 'agent' || name === 'delegate') return 'agents';
169
+ if (name.includes('channel') || name.includes('discord') || name.includes('webhook')) return 'channels';
170
+ if (name.includes('provider') || name === 'tool_search' || name === 'cwd') return 'setup';
171
+ return 'other';
172
+ }
173
+
174
+ export function estimateToolSchemaBreakdown(tools) {
175
+ const out = {};
176
+ for (const tool of Array.isArray(tools) ? tools : []) {
177
+ const bucket = toolSchemaBucket(tool);
178
+ const row = out[bucket] || { count: 0, tokens: 0 };
179
+ row.count += 1;
180
+ row.tokens += estimateToolSchemaTokens([tool]);
181
+ out[bucket] = row;
182
+ }
183
+ return out;
184
+ }
185
+
186
+ export function measuredToolUsage(name) {
187
+ return MEASURED_TOOL_USAGE[clean(name)] || 0;
188
+ }
189
+
190
+ export function parseToolSelection(value) {
191
+ if (Array.isArray(value)) return value.map(clean).filter(Boolean);
192
+ if (value && typeof value !== 'string' && typeof value[Symbol.iterator] === 'function') {
193
+ return [...value].map(clean).filter(Boolean);
194
+ }
195
+ return String(value || '').replace(/^select\s*:/i, '')
196
+ .split(/[,\s]+/)
197
+ .map(clean)
198
+ .filter(Boolean);
199
+ }
200
+
201
+ export function parseToolSearchQuerySelection(query) {
202
+ const match = clean(query).match(/^select\s*:\s*(.+)$/i);
203
+ return match ? parseToolSelection(match[1]) : [];
204
+ }
205
+
206
+ function measuredToolRank(name) {
207
+ const index = MEASURED_TOOL_ORDER.indexOf(clean(name));
208
+ return index === -1 ? Number.MAX_SAFE_INTEGER : index;
209
+ }
210
+
211
+ export function sortedCatalogByMeasuredUsage(catalog) {
212
+ return (catalog || [])
213
+ .map((tool, index) => ({ tool, index }))
214
+ .sort((a, b) => {
215
+ const au = measuredToolUsage(a.tool?.name);
216
+ const bu = measuredToolUsage(b.tool?.name);
217
+ if (bu !== au) return bu - au;
218
+ const ar = measuredToolRank(a.tool?.name);
219
+ const br = measuredToolRank(b.tool?.name);
220
+ if (ar !== br) return ar - br;
221
+ return a.index - b.index;
222
+ })
223
+ .map((entry) => entry.tool);
224
+ }
225
+
226
+ function activeToolForSurface(tool) {
227
+ if (!tool || typeof tool !== 'object') return tool;
228
+ return JSON.parse(JSON.stringify(tool));
229
+ }
230
+
231
+ function deferredProviderMode(provider) {
232
+ const p = clean(provider).toLowerCase();
233
+ if (p === 'gemini') return 'full';
234
+ if (p === 'anthropic' || p === 'anthropic-oauth'
235
+ || p === 'openai' || p === 'openai-oauth'
236
+ || p === 'xai' || p === 'grok-oauth') {
237
+ return 'native';
238
+ }
239
+ return 'legacy';
240
+ }
241
+
242
+ export function filterDisallowedTools(tools, disallowed = []) {
243
+ if (!Array.isArray(disallowed) || disallowed.length === 0) return tools;
244
+ const deny = new Set(disallowed.map((name) => clean(name)).filter(Boolean));
245
+ if (deny.size === 0) return tools;
246
+ return (tools || []).filter((tool) => !deny.has(clean(tool?.name)));
247
+ }
248
+
249
+ export function sortedNamesByMeasuredUsage(names) {
250
+ return [...(names || [])].sort((a, b) => {
251
+ const au = measuredToolUsage(a);
252
+ const bu = measuredToolUsage(b);
253
+ if (bu !== au) return bu - au;
254
+ const ar = measuredToolRank(a);
255
+ const br = measuredToolRank(b);
256
+ if (ar !== br) return ar - br;
257
+ return String(a).localeCompare(String(b));
258
+ });
259
+ }
260
+
261
+ export function defaultDeferredToolNames(catalog, mode) {
262
+ const available = new Set((catalog || []).map((tool) => clean(tool?.name)).filter(Boolean));
263
+ if (mode === 'lead') {
264
+ return new Set(DEFERRED_DEFAULT_LEAD_TOOLS.filter((name) => available.has(name)));
265
+ }
266
+ if (mode === 'readonly') {
267
+ return new Set(DEFERRED_DEFAULT_READONLY_TOOLS.filter((name) => available.has(name)));
268
+ }
269
+ return new Set(DEFERRED_DEFAULT_FULL_TOOLS.filter((name) => available.has(name)));
270
+ }
271
+
272
+ export function compactToolSearchDescription(value, max = 220) {
273
+ const text = clean(value).replace(/\s+/g, ' ');
274
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
275
+ }
276
+
277
+ export function toolRow(tool, activeNames = new Set()) {
278
+ const name = clean(tool?.name);
279
+ return {
280
+ name,
281
+ kind: toolKind(tool),
282
+ usage: measuredToolUsage(name),
283
+ active: activeNames.has(name),
284
+ description: compactToolSearchDescription(tool?.description),
285
+ };
286
+ }
287
+
288
+ function providerSupportsResponsesCustomTools(provider) {
289
+ const p = clean(provider).toLowerCase();
290
+ if (!p) return true;
291
+ return p === 'openai' || p === 'openai-oauth';
292
+ }
293
+
294
+ function openAILoadableToolSpec(tool, provider = '') {
295
+ if (providerSupportsResponsesCustomTools(provider) && isResponsesFreeformTool(tool)) return toResponsesCustomTool(tool);
296
+ return {
297
+ type: 'function',
298
+ name: clean(tool?.name),
299
+ description: clean(tool?.description),
300
+ defer_loading: true,
301
+ parameters: tool?.inputSchema && typeof tool.inputSchema === 'object'
302
+ ? tool.inputSchema
303
+ : { type: 'object', properties: {} },
304
+ };
305
+ }
306
+
307
+ function toolSearchNativePayload(catalog, names, provider = '') {
308
+ const selected = new Set((names || []).map(clean).filter(Boolean));
309
+ if (!selected.size) return null;
310
+ const tools = [];
311
+ const refs = [];
312
+ for (const tool of catalog || []) {
313
+ const name = clean(tool?.name);
314
+ if (!name || !selected.has(name)) continue;
315
+ refs.push(name);
316
+ tools.push(openAILoadableToolSpec(tool, provider));
317
+ }
318
+ if (!refs.length) return null;
319
+ return {
320
+ toolReferences: refs,
321
+ openaiTools: tools,
322
+ summary: `Loaded deferred tools: ${refs.join(', ')}`,
323
+ };
324
+ }
325
+
326
+ function toolSearchTokens(value) {
327
+ return (clean(value).toLowerCase().match(/[a-z0-9_.-]+/g) || [])
328
+ .map((token) => token.replace(/[-.]+/g, '_'))
329
+ .filter(Boolean);
330
+ }
331
+
332
+ function toolSearchMeaningfulTokens(value) {
333
+ return toolSearchTokens(value).filter((token) => !TOOL_SEARCH_STOP_WORDS.has(token));
334
+ }
335
+
336
+ function toolSearchText(row) {
337
+ const text = `${row.name} ${String(row.name || '').replace(/_/g, ' ')} ${row.kind} ${row.description} ${row.active ? 'active' : 'deferred'}`;
338
+ return `${text} ${text.replace(/[-.]+/g, '_')}`.toLowerCase();
339
+ }
340
+
341
+ function toolSearchRowAliases(name) {
342
+ return TOOL_SEARCH_ROW_ALIASES[clean(name)] || [];
343
+ }
344
+
345
+ function toolSearchRank(row, query) {
346
+ const raw = clean(query).toLowerCase();
347
+ if (!raw) return { score: 0, reasons: [] };
348
+ const name = clean(row?.name).toLowerCase();
349
+ const prettyName = name.replace(/_/g, ' ');
350
+ const haystack = toolSearchText(row);
351
+ const aliases = toolSearchRowAliases(name);
352
+ const aliasText = aliases.join(' ').toLowerCase();
353
+ const queryTokens = toolSearchMeaningfulTokens(raw);
354
+ const nameTokens = new Set(toolSearchTokens(`${name} ${prettyName}`));
355
+ const aliasTokens = new Set(toolSearchTokens(aliasText));
356
+ let score = 0;
357
+ const reasons = [];
358
+ if (raw === name || raw === prettyName) {
359
+ score += 120;
360
+ reasons.push('exact-name');
361
+ }
362
+ if (aliases.some((alias) => raw === alias || raw === alias.replace(/[_-]+/g, ' '))) {
363
+ score += 100;
364
+ reasons.push('exact-alias');
365
+ }
366
+ // Stop-word-only queries (e.g. "tool", "to") have zero meaningful tokens.
367
+ // Without an exact name/alias hit, they must not fall through to the raw
368
+ // substring/haystack checks below — every row description likely contains
369
+ // "tool" somewhere, producing a noisy pseudo-match list instead of "no
370
+ // results". Exact name/alias matches stay intact even when the name is
371
+ // itself a stop word.
372
+ if (!queryTokens.length && !reasons.length) {
373
+ return { score: 0, reasons: [] };
374
+ }
375
+ if (haystack.includes(raw)) {
376
+ score += 34;
377
+ reasons.push('phrase');
378
+ }
379
+ for (const alias of aliases) {
380
+ const normalizedAlias = alias.toLowerCase();
381
+ if (normalizedAlias && raw.includes(normalizedAlias)) {
382
+ score += 58;
383
+ reasons.push('alias-phrase');
384
+ break;
385
+ }
386
+ }
387
+ let matchedTokens = 0;
388
+ for (const token of queryTokens) {
389
+ if (nameTokens.has(token) || name.includes(token)) {
390
+ score += 24;
391
+ matchedTokens += 1;
392
+ continue;
393
+ }
394
+ if (aliasTokens.has(token) || aliasText.includes(token)) {
395
+ score += 18;
396
+ matchedTokens += 1;
397
+ continue;
398
+ }
399
+ if (haystack.includes(token)) {
400
+ score += 7;
401
+ matchedTokens += 1;
402
+ }
403
+ }
404
+ if (queryTokens.length && matchedTokens === queryTokens.length) {
405
+ score += Math.min(18, queryTokens.length * 6);
406
+ reasons.push('all-tokens');
407
+ }
408
+ if (clean(row?.kind).toLowerCase() === raw) score += 10;
409
+ if (score > 0) score += Math.min(6, Math.floor(measuredToolUsage(name) / 150));
410
+ return { score, reasons };
411
+ }
412
+
413
+ export function toolSearchMatches(row, query) {
414
+ const raw = clean(query).toLowerCase();
415
+ if (!raw) return true;
416
+ return toolSearchRank(row, raw).score > 0;
417
+ }
418
+
419
+ function rankedToolSearchRows(rows, query) {
420
+ const raw = clean(query).toLowerCase();
421
+ if (!raw) return rows;
422
+ return rows
423
+ .map((row) => {
424
+ const ranked = toolSearchRank(row, raw);
425
+ return { ...row, score: ranked.score, reasons: ranked.reasons };
426
+ })
427
+ .filter((row) => row.score > 0)
428
+ .sort((a, b) => {
429
+ if (b.score !== a.score) return b.score - a.score;
430
+ if (a.active !== b.active) return a.active ? 1 : -1;
431
+ if (b.usage !== a.usage) return b.usage - a.usage;
432
+ return String(a.name).localeCompare(String(b.name));
433
+ });
434
+ }
435
+
436
+ function isAsciiWordChar(ch) {
437
+ return !!ch && /[A-Za-z0-9]/.test(ch);
438
+ }
439
+
440
+ // Matches `phrase` inside `text` at a word boundary. ASCII letters/digits on
441
+ // either side of the match block it (so "webhook" does not match "web" and
442
+ // "prune" does not match "run"); non-ASCII characters (e.g. Korean, where
443
+ // words are not space-separated) never count as word chars, so Korean
444
+ // substring phrases keep matching as before.
445
+ function phraseMatchesAsWords(text, phrase) {
446
+ if (!phrase) return false;
447
+ let idx = text.indexOf(phrase);
448
+ while (idx !== -1) {
449
+ const before = idx > 0 ? text[idx - 1] : '';
450
+ const after = idx + phrase.length < text.length ? text[idx + phrase.length] : '';
451
+ if (!isAsciiWordChar(before) && !isAsciiWordChar(after)) return true;
452
+ idx = text.indexOf(phrase, idx + 1);
453
+ }
454
+ return false;
455
+ }
456
+
457
+ function queryHasAnyPhrase(query, phrases) {
458
+ const text = clean(query).toLowerCase().replace(/[_-]+/g, ' ');
459
+ return phrases.some((phrase) => phraseMatchesAsWords(text, phrase));
460
+ }
461
+
462
+ const TOOL_SEARCH_AUTO_CATEGORY_BRANCHES = [
463
+ {
464
+ names: ['memory'],
465
+ phrases: ['save memory', 'store memory', 'delete memory', 'forget memory', 'memory status', '기억 저장', '메모리 저장'],
466
+ },
467
+ {
468
+ names: ['shell', 'task'],
469
+ phrases: ['run', 'execute', 'test', 'tests', 'build', 'terminal', 'command', 'powershell', 'bash', 'shell', 'npm', 'node', 'git', '실행', '테스트', '빌드', '터미널', '쉘'],
470
+ },
471
+ {
472
+ names: ['search', 'web_fetch'],
473
+ phrases: ['web', 'internet', 'online', 'current info', 'latest', 'news', 'browse', 'docs', 'documentation', '웹', '인터넷', '최신', '뉴스', '문서'],
474
+ },
475
+ {
476
+ names: ['recall'],
477
+ phrases: ['recall', 'remember', 'memory previous', 'previous', 'history', 'past', 'prior', 'earlier', 'resume', '이전', '기억', '히스토리'],
478
+ },
479
+ {
480
+ names: ['agent'],
481
+ phrases: ['delegate', 'subagent', 'worker', 'parallel agent', 'background agent', 'reviewer', 'explorer', '에이전트', '워커', '병렬'],
482
+ },
483
+ {
484
+ names: ['cwd'],
485
+ phrases: ['working directory', 'project root', 'current directory', 'cwd'],
486
+ },
487
+ ];
488
+
489
+ function autoToolSelectionNames(query, rows) {
490
+ const raw = clean(query).toLowerCase();
491
+ if (!raw || TOOL_SEARCH_AMBIGUOUS_AUTO_QUERIES.has(raw)) return [];
492
+ if (TOOL_SEARCH_SAFE_AUTO_ALIASES.has(raw) && DEFERRED_SELECT_ALIASES[raw]) {
493
+ return DEFERRED_SELECT_ALIASES[raw];
494
+ }
495
+ const matchedBranches = TOOL_SEARCH_AUTO_CATEGORY_BRANCHES.filter((branch) =>
496
+ queryHasAnyPhrase(raw, branch.phrases)
497
+ );
498
+ if (matchedBranches.length === 1) return matchedBranches[0].names;
499
+ // Ambiguous (0 or 2+ category branches matched): never auto-load a
500
+ // category's tools off a possibly-wrong guess. Fall through to the ranked
501
+ // exact-name/alias path below; that path returns [] on its own when there
502
+ // is no exact match, so an ambiguous query still safely resolves to [].
503
+ const ranked = rankedToolSearchRows(rows, raw);
504
+ const top = ranked[0];
505
+ if (!top) return [];
506
+ const reasons = new Set(top.reasons || []);
507
+ if (reasons.has('exact-name') || reasons.has('exact-alias')) return [top.name];
508
+ return [];
509
+ }
510
+
511
+ function expandSelectionNames(names) {
512
+ const out = [];
513
+ for (const raw of names || []) {
514
+ const key = clean(raw);
515
+ if (!key) continue;
516
+ const alias = DEFERRED_SELECT_ALIASES[key.toLowerCase()];
517
+ if (alias) out.push(...alias);
518
+ else out.push(key);
519
+ }
520
+ return [...new Set(out)];
521
+ }
522
+
523
+ function storedDeferredToolNames(session) {
524
+ for (const source of [session?.deferredDiscoveredTools, session?.deferredSelectedTools]) {
525
+ const names = parseToolSelection(source);
526
+ if (names.length) return names;
527
+ }
528
+ return [];
529
+ }
530
+
531
+ function canonicalDeferredToolNames(catalog, names) {
532
+ const byName = new Map();
533
+ for (const tool of catalog || []) {
534
+ const name = clean(tool?.name);
535
+ if (!name) continue;
536
+ byName.set(name, name);
537
+ byName.set(name.toLowerCase(), name);
538
+ }
539
+ const out = [];
540
+ for (const raw of expandSelectionNames(names)) {
541
+ const name = clean(raw);
542
+ const canonical = byName.get(name) || byName.get(name.toLowerCase());
543
+ if (canonical) out.push(canonical);
544
+ }
545
+ return sortedNamesByMeasuredUsage(new Set(out));
546
+ }
547
+
548
+ function setDeferredToolState(session, names) {
549
+ if (!session) return [];
550
+ const selected = sortedNamesByMeasuredUsage(new Set(parseToolSelection(names)));
551
+ session.deferredDiscoveredTools = selected;
552
+ session.deferredSelectedTools = selected;
553
+ return selected;
554
+ }
555
+
556
+ function isReadonlySelectable(tool) {
557
+ const name = clean(tool?.name);
558
+ if (READONLY_TOOL_NAMES.has(name)) return true;
559
+ const annotations = tool?.annotations || {};
560
+ if (annotations.destructiveHint === true) return false;
561
+ if (annotations.readOnlyHint === true) return true;
562
+ return false;
563
+ }
564
+
565
+ export function applyDeferredToolSurface(session, mode, extraTools = [], options = {}) {
566
+ if (!session || !Array.isArray(session.tools)) return session;
567
+ const providerMode = deferredProviderMode(options.provider || session.provider);
568
+ const byName = new Map();
569
+ for (const tool of [...session.tools, ...(extraTools || [])]) {
570
+ const name = clean(tool?.name);
571
+ if (!name || byName.has(name)) continue;
572
+ byName.set(name, activeToolForSurface(tool));
573
+ }
574
+ const catalog = sortedCatalogByMeasuredUsage([...byName.values()]);
575
+ const defaultNames = defaultDeferredToolNames(catalog, mode);
576
+ const storedNames = providerMode === 'native' ? [] : storedDeferredToolNames(session);
577
+ let selectedNames = providerMode === 'full'
578
+ ? sortedNamesByMeasuredUsage(catalog.map((tool) => clean(tool?.name)).filter(Boolean))
579
+ : [];
580
+ if (providerMode !== 'full') {
581
+ selectedNames = storedNames.length ? canonicalDeferredToolNames(catalog, storedNames) : [];
582
+ if (!selectedNames.length || providerMode === 'native') selectedNames = sortedNamesByMeasuredUsage(defaultNames);
583
+ }
584
+ const selected = new Set(selectedNames);
585
+ session.deferredToolCatalog = catalog;
586
+ session.deferredToolUsage = MEASURED_TOOL_USAGE;
587
+ session.deferredDefaultTools = sortedNamesByMeasuredUsage(defaultNames);
588
+ session.deferredProviderMode = providerMode;
589
+ session.deferredNativeTools = providerMode === 'native';
590
+ session.tools.length = 0;
591
+ const active = [];
592
+ for (const tool of catalog) {
593
+ if (!selected.has(clean(tool?.name))) continue;
594
+ if (mode === 'readonly' && !isReadonlySelectable(tool)) continue;
595
+ session.tools.push(tool);
596
+ active.push(clean(tool?.name));
597
+ }
598
+ if (providerMode === 'native') {
599
+ const discovered = canonicalDeferredToolNames(catalog, session.deferredDiscoveredTools || []);
600
+ session.deferredSelectedTools = active;
601
+ session.deferredDiscoveredTools = discovered.filter((name) => !selected.has(name));
602
+ } else {
603
+ setDeferredToolState(session, active);
604
+ }
605
+ return session;
606
+ }
607
+
608
+ export function selectDeferredTools(session, names, mode) {
609
+ const catalog = Array.isArray(session?.deferredToolCatalog)
610
+ ? session.deferredToolCatalog
611
+ : (Array.isArray(session?.tools) ? session.tools : []);
612
+ const active = new Set((session?.tools || []).map((tool) => clean(tool?.name)).filter(Boolean));
613
+ const native = session?.deferredProviderMode === 'native' || session?.deferredNativeTools === true;
614
+ const discovered = new Set(Array.isArray(session?.deferredDiscoveredTools) ? session.deferredDiscoveredTools : []);
615
+ const byName = new Map();
616
+ for (const tool of catalog) {
617
+ const name = clean(tool?.name);
618
+ if (!name) continue;
619
+ byName.set(name, tool);
620
+ byName.set(name.toLowerCase(), tool);
621
+ }
622
+ const added = [];
623
+ const already = [];
624
+ const blocked = [];
625
+ const missing = [];
626
+ for (const rawName of expandSelectionNames(names)) {
627
+ const requestedName = clean(rawName);
628
+ const tool = byName.get(requestedName) || byName.get(requestedName.toLowerCase());
629
+ const name = clean(tool?.name);
630
+ if (!tool) {
631
+ missing.push(requestedName);
632
+ continue;
633
+ }
634
+ if (mode === 'readonly' && !isReadonlySelectable(tool)) {
635
+ blocked.push({ name, reason: 'readonly mode' });
636
+ continue;
637
+ }
638
+ if (active.has(name) || discovered.has(name)) {
639
+ already.push(name);
640
+ continue;
641
+ }
642
+ if (native) {
643
+ discovered.add(name);
644
+ } else {
645
+ session.tools.push(tool);
646
+ active.add(name);
647
+ }
648
+ added.push(name);
649
+ }
650
+ if (native) {
651
+ session.deferredDiscoveredTools = sortedNamesByMeasuredUsage(discovered);
652
+ session.deferredSelectedTools = sortedNamesByMeasuredUsage(active);
653
+ } else {
654
+ setDeferredToolState(session, active);
655
+ }
656
+ return { added, already, blocked, missing, native };
657
+ }
658
+
659
+ export function renderToolSearch(args = {}, session, mode = 'full') {
660
+ const catalog = Array.isArray(session?.deferredToolCatalog)
661
+ ? session.deferredToolCatalog
662
+ : (Array.isArray(session?.tools) ? session.tools : []);
663
+ const rawQuery = clean(args.query);
664
+ const explicitSelectedNames = parseToolSelection(args.select);
665
+ const querySelectedNames = explicitSelectedNames.length ? [] : parseToolSearchQuerySelection(rawQuery);
666
+ const forcedSelectedNames = explicitSelectedNames.length ? explicitSelectedNames : querySelectedNames;
667
+ const query = querySelectedNames.length ? '' : rawQuery.toLowerCase();
668
+ const limit = Math.max(1, Math.min(50, Number(args.limit) || 20));
669
+ const initialActiveNames = new Set((session?.tools || []).map((tool) => clean(tool?.name)).filter(Boolean));
670
+ const initialRows = catalog.map((tool) => toolRow(tool, initialActiveNames)).filter((row) => row.name);
671
+ const autoSelectedNames = (!forcedSelectedNames.length && query)
672
+ ? autoToolSelectionNames(query, initialRows)
673
+ : [];
674
+ const selectedNames = forcedSelectedNames.length ? forcedSelectedNames : autoSelectedNames;
675
+ let toolSelection = selectedNames.length ? selectDeferredTools(session, selectedNames, mode) : null;
676
+ let selectionMode = forcedSelectedNames.length ? 'select' : (autoSelectedNames.length ? 'auto' : null);
677
+ let nextActiveNames = new Set((session?.tools || []).map((tool) => clean(tool?.name)).filter(Boolean));
678
+ let rows = catalog.map((tool) => toolRow(tool, nextActiveNames)).filter((row) => row.name);
679
+ let matches = query
680
+ ? rankedToolSearchRows(rows, query)
681
+ : rows;
682
+ // Query hits must become loadable without a second explicit select turn:
683
+ // if the query produced ranked matches but the forced/auto path above
684
+ // loaded nothing (no exact-name/alias/category hit), fall back to
685
+ // auto-loading the ranked match names themselves (deferred/non-active
686
+ // only, capped at `limit`). Ambiguous queries stay excluded so a guess
687
+ // never silently loads a wrong category.
688
+ const STRONG_MATCH_REASONS = new Set(['exact-name', 'exact-alias', 'alias-phrase', 'phrase', 'all-tokens']);
689
+ const isStrongMatch = (row) => row.score >= 24 || (row.reasons || []).some((reason) => STRONG_MATCH_REASONS.has(reason));
690
+ const queryLoadedNothing = !toolSelection
691
+ || (!toolSelection.added.length && !toolSelection.already.length);
692
+ if (query && !TOOL_SEARCH_AMBIGUOUS_AUTO_QUERIES.has(query) && matches.length && queryLoadedNothing) {
693
+ const fallbackNames = matches.filter((row) => !row.active && isStrongMatch(row)).map((row) => row.name).slice(0, limit);
694
+ if (fallbackNames.length) {
695
+ toolSelection = selectDeferredTools(session, fallbackNames, mode);
696
+ selectionMode = 'query';
697
+ nextActiveNames = new Set((session?.tools || []).map((tool) => clean(tool?.name)).filter(Boolean));
698
+ rows = catalog.map((tool) => toolRow(tool, nextActiveNames)).filter((row) => row.name);
699
+ matches = query ? rankedToolSearchRows(rows, query) : rows;
700
+ }
701
+ }
702
+ const selected = toolSelection
703
+ ? {
704
+ mode: selectionMode,
705
+ tools: toolSelection,
706
+ }
707
+ : null;
708
+ const nativeToolSearch = toolSelection?.native
709
+ ? toolSearchNativePayload(catalog, [...new Set([...toolSelection.added, ...(toolSelection.already || [])])], session?.provider)
710
+ : null;
711
+ return JSON.stringify({
712
+ selected,
713
+ ...(nativeToolSearch ? { nativeToolSearch } : {}),
714
+ totalMatches: matches.length,
715
+ matches: matches.slice(0, limit),
716
+ activeTools: sortedNamesByMeasuredUsage(nextActiveNames),
717
+ discoveredTools: sortedNamesByMeasuredUsage(session?.deferredDiscoveredTools || []),
718
+ note: 'query ranks matches and auto-loads high-confidence deferred tools, including ranked search hits (deferred, non-active) up to limit; already-loaded/discovered tools are re-surfaced on re-selection; select exact tool names, or query select:a,b, to force load.',
719
+ }, null, 2);
720
+ }