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