@namzu/sdk 1.0.0 → 1.2.0
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/CHANGELOG.md +253 -0
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +5 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts +22 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +47 -14
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/supervisor-ledger.test.d.ts +2 -0
- package/dist/agents/__tests__/supervisor-ledger.test.d.ts.map +1 -0
- package/dist/agents/__tests__/supervisor-ledger.test.js +131 -0
- package/dist/agents/__tests__/supervisor-ledger.test.js.map +1 -0
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +2 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +4 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/config/runtime.d.ts +24 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +9 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/builtins/webhook.test.js +17 -0
- package/dist/connector/builtins/webhook.test.js.map +1 -1
- package/dist/connector/index.d.ts +1 -0
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +1 -0
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +4 -0
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +1 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +1 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/streamable-http.d.ts +22 -0
- package/dist/connector/mcp/streamable-http.d.ts.map +1 -0
- package/dist/connector/mcp/streamable-http.js +128 -0
- package/dist/connector/mcp/streamable-http.js.map +1 -0
- package/dist/connector/mcp/streamable-http.test.d.ts +2 -0
- package/dist/connector/mcp/streamable-http.test.d.ts.map +1 -0
- package/dist/connector/mcp/streamable-http.test.js +132 -0
- package/dist/connector/mcp/streamable-http.test.js.map +1 -0
- package/dist/{runtime/query → constants}/continuation.d.ts +5 -0
- package/dist/constants/continuation.d.ts.map +1 -0
- package/dist/{runtime/query → constants}/continuation.js +5 -0
- package/dist/constants/continuation.js.map +1 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +11 -0
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +30 -1
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/persona/assembler.d.ts.map +1 -1
- package/dist/persona/assembler.js +18 -9
- package/dist/persona/assembler.js.map +1 -1
- package/dist/provider/capabilities.d.ts +27 -0
- package/dist/provider/capabilities.d.ts.map +1 -0
- package/dist/provider/capabilities.js +29 -0
- package/dist/provider/capabilities.js.map +1 -0
- package/dist/provider/collect.d.ts.map +1 -1
- package/dist/provider/collect.js +4 -1
- package/dist/provider/collect.js.map +1 -1
- package/dist/provider/index.d.ts +2 -0
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +1 -0
- package/dist/provider/index.js.map +1 -1
- package/dist/public-runtime.d.ts +4 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +3 -3
- package/dist/public-runtime.js.map +1 -1
- package/dist/registry/tool/execute.d.ts +27 -0
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +124 -21
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/execute.test.d.ts +9 -6
- package/dist/registry/tool/execute.test.d.ts.map +1 -1
- package/dist/registry/tool/execute.test.js +72 -10
- package/dist/registry/tool/execute.test.js.map +1 -1
- package/dist/registry/toolset/catalog.test.js +62 -0
- package/dist/registry/toolset/catalog.test.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +8 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.js +184 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.js.map +1 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.js +259 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.js.map +1 -0
- package/dist/runtime/query/__tests__/checkpoint.test.js +9 -3
- package/dist/runtime/query/__tests__/checkpoint.test.js.map +1 -1
- package/dist/runtime/query/__tests__/deferred-tools.test.js +43 -5
- package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -1
- package/dist/runtime/query/__tests__/long-document-flow.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.js +154 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js +110 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js.map +1 -0
- package/dist/runtime/query/__tests__/prompt.test.js +15 -11
- package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts +16 -2
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +21 -7
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/context.d.ts +7 -0
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +1 -0
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/index.d.ts +29 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +98 -8
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +3 -55
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +260 -656
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.js +24 -2
- package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +8 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +84 -3
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +13 -2
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js +53 -0
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js +73 -0
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js +120 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/tool-review.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +7 -2
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts +19 -0
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js +177 -0
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/working-memory.d.ts +31 -0
- package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/working-memory.js +81 -0
- package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts +35 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.js +341 -0
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -0
- package/dist/runtime/query/replay/list.d.ts +17 -1
- package/dist/runtime/query/replay/list.d.ts.map +1 -1
- package/dist/runtime/query/replay/list.js +21 -4
- package/dist/runtime/query/replay/list.js.map +1 -1
- package/dist/runtime/query/replay/prepare.d.ts +10 -0
- package/dist/runtime/query/replay/prepare.d.ts.map +1 -1
- package/dist/runtime/query/replay/prepare.js +20 -4
- package/dist/runtime/query/replay/prepare.js.map +1 -1
- package/dist/session/workspace/__tests__/shared-run.test.js +1 -1
- package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -1
- package/dist/session/workspace/shared-run.js +1 -0
- package/dist/session/workspace/shared-run.js.map +1 -1
- package/dist/skills/loader.test.js +8 -8
- package/dist/skills/loader.test.js.map +1 -1
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +1 -0
- package/dist/store/index.js.map +1 -1
- package/dist/store/run/checkpoint-disk.d.ts +28 -0
- package/dist/store/run/checkpoint-disk.d.ts.map +1 -0
- package/dist/store/run/checkpoint-disk.js +55 -0
- package/dist/store/run/checkpoint-disk.js.map +1 -0
- package/dist/streaming/coalesce.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/payload-budget.test.js +48 -0
- package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/read-file.test.js +9 -0
- package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +50 -2
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/search-tools.d.ts.map +1 -1
- package/dist/tools/builtins/search-tools.js +32 -7
- package/dist/tools/builtins/search-tools.js.map +1 -1
- package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts +2 -0
- package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/approve-plan.test.js +114 -0
- package/dist/tools/coordinator/__tests__/approve-plan.test.js.map +1 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts +2 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.js +309 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +8 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +195 -15
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/types/agent/index.d.ts +1 -0
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/index.js +1 -0
- package/dist/types/agent/index.js.map +1 -1
- package/dist/types/agent/reactive.d.ts +15 -0
- package/dist/types/agent/reactive.d.ts.map +1 -1
- package/dist/types/agent/supervisor.d.ts +18 -0
- package/dist/types/agent/supervisor.d.ts.map +1 -1
- package/dist/types/agent/working-memory.d.ts +29 -0
- package/dist/types/agent/working-memory.d.ts.map +1 -0
- package/dist/types/agent/working-memory.js +2 -0
- package/dist/types/agent/working-memory.js.map +1 -0
- package/dist/types/common/index.d.ts +10 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +18 -0
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/common/usage-merge.test.d.ts +2 -0
- package/dist/types/common/usage-merge.test.d.ts.map +1 -0
- package/dist/types/common/usage-merge.test.js +48 -0
- package/dist/types/common/usage-merge.test.js.map +1 -0
- package/dist/types/connector/mcp.d.ts +9 -2
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/hitl/index.d.ts +42 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js +11 -0
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +9 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +18 -0
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/interface.d.ts +10 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/run/checkpoint-store.d.ts +64 -0
- package/dist/types/run/checkpoint-store.d.ts.map +1 -0
- package/dist/types/run/checkpoint-store.js +12 -0
- package/dist/types/run/checkpoint-store.js.map +1 -0
- package/dist/types/run/config.d.ts +27 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +6 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/index.d.ts +1 -0
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +1 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +25 -4
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/ReactiveAgent.ts +5 -0
- package/src/agents/SupervisorAgent.ts +56 -16
- package/src/agents/__tests__/supervisor-ledger.test.ts +185 -0
- package/src/bridge/a2a/mapper.ts +3 -0
- package/src/bridge/sse/mapper.ts +5 -0
- package/src/config/runtime.ts +9 -0
- package/src/connector/builtins/webhook.test.ts +21 -0
- package/src/connector/index.ts +1 -0
- package/src/connector/mcp/client.ts +4 -0
- package/src/connector/mcp/index.ts +1 -0
- package/src/connector/mcp/streamable-http.test.ts +169 -0
- package/src/connector/mcp/streamable-http.ts +152 -0
- package/src/{runtime/query → constants}/continuation.ts +5 -0
- package/src/constants/index.ts +1 -0
- package/src/manager/run/persistence.ts +34 -1
- package/src/persona/assembler.ts +18 -6
- package/src/provider/capabilities.ts +47 -0
- package/src/provider/collect.ts +4 -1
- package/src/provider/index.ts +5 -0
- package/src/public-runtime.ts +5 -0
- package/src/registry/tool/execute.test.ts +85 -10
- package/src/registry/tool/execute.ts +124 -21
- package/src/registry/toolset/catalog.test.ts +68 -0
- package/src/run/reporter.ts +9 -0
- package/src/runtime/query/__tests__/capability-negotiation.test.ts +248 -0
- package/src/runtime/query/__tests__/checkpoint-store.test.ts +322 -0
- package/src/runtime/query/__tests__/checkpoint.test.ts +15 -6
- package/src/runtime/query/__tests__/deferred-tools.test.ts +52 -5
- package/src/runtime/query/__tests__/long-document-flow.test.ts +180 -0
- package/src/runtime/query/__tests__/orphan-task-turn-end.test.ts +127 -0
- package/src/runtime/query/__tests__/prompt.test.ts +15 -11
- package/src/runtime/query/checkpoint.ts +23 -9
- package/src/runtime/query/context.ts +9 -0
- package/src/runtime/query/index.ts +148 -29
- package/src/runtime/query/iteration/index.ts +316 -831
- package/src/runtime/query/iteration/phases/checkpoint.ts +32 -2
- package/src/runtime/query/iteration/phases/compaction.ts +89 -3
- package/src/runtime/query/iteration/phases/context.ts +59 -3
- package/src/runtime/query/iteration/phases/hitl-abort-race.test.ts +86 -0
- package/src/runtime/query/iteration/phases/hitl-answer-question.test.ts +139 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +7 -3
- package/src/runtime/query/iteration/phases/working-memory-compaction.test.ts +215 -0
- package/src/runtime/query/iteration/phases/working-memory.ts +86 -0
- package/src/runtime/query/iteration/stream-turn.ts +396 -0
- package/src/runtime/query/replay/list.ts +38 -4
- package/src/runtime/query/replay/prepare.ts +37 -4
- package/src/session/workspace/__tests__/shared-run.test.ts +1 -1
- package/src/session/workspace/shared-run.ts +1 -0
- package/src/skills/loader.test.ts +8 -8
- package/src/store/index.ts +1 -0
- package/src/store/run/checkpoint-disk.ts +67 -0
- package/src/streaming/coalesce.test.ts +3 -3
- package/src/tools/builtins/__tests__/payload-budget.test.ts +63 -0
- package/src/tools/builtins/__tests__/read-file.test.ts +12 -0
- package/src/tools/builtins/read-file.ts +50 -2
- package/src/tools/builtins/search-tools.ts +38 -7
- package/src/tools/coordinator/__tests__/approve-plan.test.ts +139 -0
- package/src/tools/coordinator/__tests__/ask-user-question.test.ts +370 -0
- package/src/tools/coordinator/index.ts +227 -17
- package/src/types/agent/index.ts +1 -0
- package/src/types/agent/reactive.ts +17 -0
- package/src/types/agent/supervisor.ts +20 -0
- package/src/types/agent/working-memory.ts +29 -0
- package/src/types/common/index.ts +19 -0
- package/src/types/common/usage-merge.test.ts +53 -0
- package/src/types/connector/mcp.ts +14 -2
- package/src/types/hitl/index.ts +52 -1
- package/src/types/provider/chat.ts +10 -0
- package/src/types/provider/config.ts +18 -0
- package/src/types/provider/interface.ts +11 -0
- package/src/types/run/checkpoint-store.ts +72 -0
- package/src/types/run/config.ts +30 -0
- package/src/types/run/events.ts +13 -0
- package/src/types/run/index.ts +1 -0
- package/src/version.ts +26 -4
- package/dist/runtime/query/continuation.d.ts.map +0 -1
- package/dist/runtime/query/continuation.js.map +0 -1
|
@@ -19,7 +19,35 @@ export type { ToolExecutionResult }
|
|
|
19
19
|
// Tokens too generic to identify a tool by name — ignored when matching a
|
|
20
20
|
// batched `search_tools` query so they can't activate the whole catalog
|
|
21
21
|
// (every bridged tool name shares the `clawtool` prefix, for instance).
|
|
22
|
-
|
|
22
|
+
// Generic CRUD verbs are stopped too: a query like "list deals" must rank
|
|
23
|
+
// by "deals", not token-match every `list_*` tool in the deferred catalog.
|
|
24
|
+
const SEARCH_STOP_TOKENS = new Set([
|
|
25
|
+
'clawtool',
|
|
26
|
+
'tool',
|
|
27
|
+
'tools',
|
|
28
|
+
'mcp',
|
|
29
|
+
'the',
|
|
30
|
+
'and',
|
|
31
|
+
'for',
|
|
32
|
+
'use',
|
|
33
|
+
'list',
|
|
34
|
+
'read',
|
|
35
|
+
'create',
|
|
36
|
+
'update',
|
|
37
|
+
'get',
|
|
38
|
+
'find',
|
|
39
|
+
'delete',
|
|
40
|
+
'search',
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
// Weighted-scoring weights mirroring ToolCatalog.searchTools (the richer,
|
|
44
|
+
// otherwise-unused catalog scorer): exact name 12, name substring 8,
|
|
45
|
+
// description 5 — extended here with argument-name indexing (3), following
|
|
46
|
+
// Anthropic's tool-search practice of searching argument names too.
|
|
47
|
+
const SEARCH_WEIGHT_NAME_EXACT = 12
|
|
48
|
+
const SEARCH_WEIGHT_NAME_PARTIAL = 8
|
|
49
|
+
const SEARCH_WEIGHT_DESCRIPTION = 5
|
|
50
|
+
const SEARCH_WEIGHT_ARGUMENT = 3
|
|
23
51
|
|
|
24
52
|
export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
|
|
25
53
|
private availability: Map<string, ToolAvailability> = new Map()
|
|
@@ -118,28 +146,58 @@ export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
|
|
|
118
146
|
return this.availability.get(name) ?? 'active'
|
|
119
147
|
}
|
|
120
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Ranked lexical search over DEFERRED tools, score-descending (ties broken
|
|
151
|
+
* by name) so callers can cap activation at a top-k. Each meaningful query
|
|
152
|
+
* term (≥3 chars, not a stop token) is scored against the tool name
|
|
153
|
+
* (exact/substring), description, and argument names; only tools with a
|
|
154
|
+
* positive score are returned. Description matching is safe here precisely
|
|
155
|
+
* because the result is RANKED — the `search_tools` builtin activates only
|
|
156
|
+
* the top slice, so a shared word can no longer drag in the whole catalog.
|
|
157
|
+
*
|
|
158
|
+
* PARKED (phase 5 of the tool-loading plan): an embedding-backed semantic
|
|
159
|
+
* upgrade was evaluated and deliberately NOT built — at ≤~35 deferred
|
|
160
|
+
* in-house tools with distinct names, weighted lexical scoring sits inside
|
|
161
|
+
* the literature's safe zone, and a weak retriever underperforms no
|
|
162
|
+
* retriever at all. Revisit only when (a) the deferred catalog grows past
|
|
163
|
+
* ~75-100 tools (realistic driver: connector-MCP growth), or (b) telemetry
|
|
164
|
+
* shows a search_tools miss-rate above ~10%. Sticky activation is also
|
|
165
|
+
* deliberate: activating inserts the schema into the tools array at its
|
|
166
|
+
* registry position (a one-time prompt-cache prefix bust); re-defer/TTL
|
|
167
|
+
* would churn that prefix repeatedly and is rejected.
|
|
168
|
+
*/
|
|
121
169
|
searchDeferred(query: string): ToolDefinition[] {
|
|
122
170
|
const q = query.toLowerCase().trim()
|
|
123
171
|
if (q.length === 0) return []
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
const terms = q.split(/\s+/).filter((tok) => tok.length >= 3 && !SEARCH_STOP_TOKENS.has(tok))
|
|
173
|
+
if (terms.length === 0) return []
|
|
174
|
+
|
|
175
|
+
const scored: Array<{ tool: ToolDefinition; score: number }> = []
|
|
176
|
+
for (const tool of this.getByAvailability(['deferred'])) {
|
|
177
|
+
const name = tool.name.toLowerCase()
|
|
178
|
+
const description = tool.description.toLowerCase()
|
|
179
|
+
const argumentNames = listArgumentNames(tool)
|
|
180
|
+
let score = 0
|
|
181
|
+
for (const term of terms) {
|
|
182
|
+
if (name === term) {
|
|
183
|
+
score += SEARCH_WEIGHT_NAME_EXACT
|
|
184
|
+
} else if (name.includes(term)) {
|
|
185
|
+
score += SEARCH_WEIGHT_NAME_PARTIAL
|
|
186
|
+
}
|
|
187
|
+
if (description.includes(term)) {
|
|
188
|
+
score += SEARCH_WEIGHT_DESCRIPTION
|
|
189
|
+
}
|
|
190
|
+
if (argumentNames.some((arg) => arg.includes(term))) {
|
|
191
|
+
score += SEARCH_WEIGHT_ARGUMENT
|
|
192
|
+
}
|
|
139
193
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
194
|
+
if (score > 0) {
|
|
195
|
+
scored.push({ tool, score })
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
scored.sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name))
|
|
200
|
+
return scored.map((entry) => entry.tool)
|
|
143
201
|
}
|
|
144
202
|
|
|
145
203
|
assignTiers(mapping: Record<string, string>): void {
|
|
@@ -176,12 +234,24 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
176
234
|
</tool_runtime_contract>`
|
|
177
235
|
|
|
178
236
|
if (active.length > 0) {
|
|
179
|
-
|
|
237
|
+
// Name-only: every active tool's full description + JSON schema
|
|
238
|
+
// already rides the runtime tools parameter on each request —
|
|
239
|
+
// repeating descriptions here double-bills the same tokens.
|
|
240
|
+
const entries = active.map((t) => `- ${t.name}`).join('\n')
|
|
180
241
|
parts.push(`<available_tools>\n${entries}\n</available_tools>`)
|
|
181
242
|
}
|
|
182
243
|
|
|
183
244
|
if (deferred.length > 0) {
|
|
184
|
-
|
|
245
|
+
// Name + one-line hint: deferred schemas stay off the wire, so the
|
|
246
|
+
// hint is the model's only signal of what a deferred tool does. A
|
|
247
|
+
// bare name list caused a real discovery failure in production
|
|
248
|
+
// (the agent never found read_document behind search_tools).
|
|
249
|
+
const entries = deferred
|
|
250
|
+
.map((t) => {
|
|
251
|
+
const hint = toolDiscoveryHint(t.description)
|
|
252
|
+
return hint.length > 0 ? `- ${t.name}: ${hint}` : `- ${t.name}`
|
|
253
|
+
})
|
|
254
|
+
.join('\n')
|
|
185
255
|
const deferredIntro =
|
|
186
256
|
this.has('search_tools') && this.getAvailability('search_tools') === 'active'
|
|
187
257
|
? 'Use search_tools to load these before use:'
|
|
@@ -372,6 +442,39 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
372
442
|
}
|
|
373
443
|
}
|
|
374
444
|
|
|
445
|
+
/**
|
|
446
|
+
* One-line discoverability hint for a deferred tool: the first sentence of
|
|
447
|
+
* its description, capped at ~100 chars. Used for the `<deferred_tools>`
|
|
448
|
+
* prompt listing and for `search_tools` near-miss suggestions, where the
|
|
449
|
+
* full description would re-import the token weight deferral avoids.
|
|
450
|
+
*/
|
|
451
|
+
export function toolDiscoveryHint(description: string, maxLength = 100): string {
|
|
452
|
+
const normalized = description.trim().replace(/\s+/g, ' ')
|
|
453
|
+
if (normalized.length === 0) return ''
|
|
454
|
+
const sentenceMatch = normalized.match(/^.*?[.!?](?=\s|$)/)
|
|
455
|
+
const sentence = sentenceMatch ? sentenceMatch[0] : normalized
|
|
456
|
+
if (sentence.length <= maxLength) return sentence
|
|
457
|
+
return `${sentence.slice(0, maxLength - 1).trimEnd()}…`
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Lower-cased argument (property) names of a tool's input schema, used by
|
|
462
|
+
* `searchDeferred` ranking. Walks the JSON-Schema rendering (already a
|
|
463
|
+
* registration dependency) instead of Zod internals; opaque schemas simply
|
|
464
|
+
* contribute no argument matches.
|
|
465
|
+
*/
|
|
466
|
+
function listArgumentNames(tool: ToolDefinition): string[] {
|
|
467
|
+
try {
|
|
468
|
+
const json = zodToJsonSchema(tool.inputSchema, {
|
|
469
|
+
target: 'jsonSchema7',
|
|
470
|
+
$refStrategy: 'none',
|
|
471
|
+
}) as { properties?: Record<string, unknown> }
|
|
472
|
+
return Object.keys(json.properties ?? {}).map((key) => key.toLowerCase())
|
|
473
|
+
} catch {
|
|
474
|
+
return []
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
375
478
|
/**
|
|
376
479
|
* Build a one-sentence "Required: <field>: <type>, <field>: <type>"
|
|
377
480
|
* hint from a Zod schema, used to enrich tool-input validation
|
|
@@ -81,6 +81,74 @@ describe('ToolCatalog', () => {
|
|
|
81
81
|
expect(catalog.toLLMTools().map((t) => t.function.name)).toEqual(['bash'])
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
+
it('scores and reports the matched facets: partial name, source text, toolset text', () => {
|
|
85
|
+
const catalog = new ToolCatalog()
|
|
86
|
+
catalog.registerSource({
|
|
87
|
+
id: 'host',
|
|
88
|
+
kind: 'host_tool',
|
|
89
|
+
name: 'GitHub',
|
|
90
|
+
description: 'repository automation',
|
|
91
|
+
})
|
|
92
|
+
catalog.registerToolset({
|
|
93
|
+
id: 'issues',
|
|
94
|
+
sourceId: 'host',
|
|
95
|
+
name: 'Issue toolset',
|
|
96
|
+
defaultPolicy: { enabled: true, loading: 'eager' },
|
|
97
|
+
})
|
|
98
|
+
catalog.registerTool({
|
|
99
|
+
name: 'create_issue',
|
|
100
|
+
description: 'open a ticket',
|
|
101
|
+
sourceId: 'host',
|
|
102
|
+
toolsetId: 'issues',
|
|
103
|
+
policy: { enabled: true, loading: 'eager' },
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// 'issue' hits the tool NAME (create_issue, partial) AND the TOOLSET name.
|
|
107
|
+
const byIssue = catalog.searchTools('issue')
|
|
108
|
+
expect(byIssue).toHaveLength(1)
|
|
109
|
+
expect([...(byIssue[0]?.matched ?? [])].sort()).toEqual(['name', 'toolset'])
|
|
110
|
+
|
|
111
|
+
// 'github' hits only the SOURCE text.
|
|
112
|
+
const bySource = catalog.searchTools('github')
|
|
113
|
+
expect(bySource[0]?.matched).toEqual(['source'])
|
|
114
|
+
|
|
115
|
+
// An exact name match outscores a partial one.
|
|
116
|
+
const exact = catalog.searchTools('create_issue')
|
|
117
|
+
expect(exact[0]?.matched).toContain('name')
|
|
118
|
+
expect(exact[0]?.score ?? 0).toBeGreaterThan(byIssue[0]?.score ?? 0)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('adds the preferred boost so a preferred tool outranks an equal match', () => {
|
|
122
|
+
const catalog = new ToolCatalog()
|
|
123
|
+
catalog.registerSource({ id: 'host', kind: 'host_tool', name: 'Host' })
|
|
124
|
+
catalog.registerToolset({
|
|
125
|
+
id: 'ts',
|
|
126
|
+
sourceId: 'host',
|
|
127
|
+
name: 'Tools',
|
|
128
|
+
defaultPolicy: { enabled: true, loading: 'eager' },
|
|
129
|
+
})
|
|
130
|
+
catalog.registerTool({
|
|
131
|
+
name: 'search_plain',
|
|
132
|
+
description: 'search things',
|
|
133
|
+
sourceId: 'host',
|
|
134
|
+
toolsetId: 'ts',
|
|
135
|
+
policy: { enabled: true, loading: 'eager' },
|
|
136
|
+
})
|
|
137
|
+
catalog.registerTool({
|
|
138
|
+
name: 'search_pref',
|
|
139
|
+
description: 'search things',
|
|
140
|
+
sourceId: 'host',
|
|
141
|
+
toolsetId: 'ts',
|
|
142
|
+
policy: { enabled: true, loading: 'eager', preferred: true },
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
const results = catalog.searchTools('search')
|
|
146
|
+
expect(results.map((r) => r.tool.name)).toEqual(['search_pref', 'search_plain'])
|
|
147
|
+
const pref = results.find((r) => r.tool.name === 'search_pref')?.score ?? 0
|
|
148
|
+
const plain = results.find((r) => r.tool.name === 'search_plain')?.score ?? 0
|
|
149
|
+
expect(pref - plain).toBe(1)
|
|
150
|
+
})
|
|
151
|
+
|
|
84
152
|
it('preserves registry availability as catalog loading policy', () => {
|
|
85
153
|
const registry = new ToolRegistry()
|
|
86
154
|
registry.register(makeTool('read_file'))
|
package/src/run/reporter.ts
CHANGED
|
@@ -225,6 +225,15 @@ export function createRunReporter(parentLogger?: Logger): RunReporter {
|
|
|
225
225
|
})
|
|
226
226
|
break
|
|
227
227
|
|
|
228
|
+
case 'capability_warning':
|
|
229
|
+
log.warn('Provider capability mismatch', {
|
|
230
|
+
runId: event.runId,
|
|
231
|
+
capability: event.capability,
|
|
232
|
+
providerId: event.providerId,
|
|
233
|
+
message: event.message,
|
|
234
|
+
})
|
|
235
|
+
break
|
|
236
|
+
|
|
228
237
|
default: {
|
|
229
238
|
const _exhaustive: never = event
|
|
230
239
|
throw new Error(`Unhandled run event type: ${(_exhaustive as RunEvent).type}`)
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
PERMISSIVE_PROVIDER_CAPABILITIES,
|
|
9
|
+
resolveProviderCapabilities,
|
|
10
|
+
} from '../../../provider/capabilities.js'
|
|
11
|
+
import { ToolRegistry } from '../../../registry/tool/execute.js'
|
|
12
|
+
import type { SessionId, TenantId } from '../../../types/ids/index.js'
|
|
13
|
+
import { createUserMessage } from '../../../types/message/index.js'
|
|
14
|
+
import type {
|
|
15
|
+
ChatCompletionParams,
|
|
16
|
+
LLMProvider,
|
|
17
|
+
ProviderCapabilities,
|
|
18
|
+
StreamChunk,
|
|
19
|
+
} from '../../../types/provider/index.js'
|
|
20
|
+
import type { RunEvent } from '../../../types/run/index.js'
|
|
21
|
+
import type { ProjectId, ThreadId } from '../../../types/session/ids.js'
|
|
22
|
+
import { drainQuery } from '../index.js'
|
|
23
|
+
|
|
24
|
+
const ZERO_USAGE = {
|
|
25
|
+
promptTokens: 0,
|
|
26
|
+
completionTokens: 0,
|
|
27
|
+
totalTokens: 0,
|
|
28
|
+
cachedTokens: 0,
|
|
29
|
+
cacheWriteTokens: 0,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class CapturingProvider implements LLMProvider {
|
|
33
|
+
readonly id = 'capturing'
|
|
34
|
+
readonly name = 'Capturing Provider'
|
|
35
|
+
readonly capabilities?: ProviderCapabilities
|
|
36
|
+
lastParams?: ChatCompletionParams
|
|
37
|
+
|
|
38
|
+
constructor(capabilities?: ProviderCapabilities) {
|
|
39
|
+
this.capabilities = capabilities
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async *chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk> {
|
|
43
|
+
this.lastParams = params
|
|
44
|
+
yield { id: 'msg_1', delta: { content: 'done' } }
|
|
45
|
+
yield { id: 'msg_1', delta: {}, finishReason: 'stop', usage: ZERO_USAGE }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const NO_TOOLS_CAPABILITIES: ProviderCapabilities = {
|
|
50
|
+
supportsTools: false,
|
|
51
|
+
supportsStreaming: true,
|
|
52
|
+
supportsFunctionCalling: false,
|
|
53
|
+
supportsVision: true,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const NO_VISION_CAPABILITIES: ProviderCapabilities = {
|
|
57
|
+
supportsTools: true,
|
|
58
|
+
supportsStreaming: true,
|
|
59
|
+
supportsFunctionCalling: true,
|
|
60
|
+
supportsVision: false,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function registerEchoTool(tools: ToolRegistry): void {
|
|
64
|
+
tools.register({
|
|
65
|
+
name: 'echo',
|
|
66
|
+
description: 'Echo the text back.',
|
|
67
|
+
inputSchema: z.object({ text: z.string() }),
|
|
68
|
+
execute: async () => ({ success: true, output: 'ok' }),
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function baseParams(provider: LLMProvider, tools: ToolRegistry, workingDirectory: string) {
|
|
73
|
+
return {
|
|
74
|
+
provider,
|
|
75
|
+
tools,
|
|
76
|
+
runConfig: {
|
|
77
|
+
model: 'mock-model',
|
|
78
|
+
timeoutMs: 5_000,
|
|
79
|
+
tokenBudget: 100_000,
|
|
80
|
+
maxIterations: 1,
|
|
81
|
+
maxResponseTokens: 256,
|
|
82
|
+
},
|
|
83
|
+
agentId: 'agent_test',
|
|
84
|
+
agentName: 'Test Agent',
|
|
85
|
+
workingDirectory,
|
|
86
|
+
sessionId: 'ses_capability' as SessionId,
|
|
87
|
+
threadId: 'thd_capability' as ThreadId,
|
|
88
|
+
projectId: 'prj_capability' as ProjectId,
|
|
89
|
+
tenantId: 'tnt_capability' as TenantId,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
describe('resolveProviderCapabilities', () => {
|
|
94
|
+
it('resolves an undeclared provider to the permissive default', () => {
|
|
95
|
+
expect(resolveProviderCapabilities(new CapturingProvider())).toEqual(
|
|
96
|
+
PERMISSIVE_PROVIDER_CAPABILITIES,
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('fills a partial declaration (missing supportsVision) permissively per field', () => {
|
|
101
|
+
const resolved = resolveProviderCapabilities({
|
|
102
|
+
capabilities: {
|
|
103
|
+
supportsTools: false,
|
|
104
|
+
supportsStreaming: true,
|
|
105
|
+
supportsFunctionCalling: false,
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
expect(resolved.supportsTools).toBe(false)
|
|
109
|
+
expect(resolved.supportsVision).toBe(true)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('query() capability negotiation', () => {
|
|
114
|
+
let workdirs: string[] = []
|
|
115
|
+
|
|
116
|
+
afterEach(async () => {
|
|
117
|
+
await Promise.all(workdirs.map((dir) => rm(dir, { recursive: true, force: true })))
|
|
118
|
+
workdirs = []
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
async function mkWorkdir(): Promise<string> {
|
|
122
|
+
const dir = await mkdtemp(join(tmpdir(), 'namzu-capability-'))
|
|
123
|
+
workdirs.push(dir)
|
|
124
|
+
return dir
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
it('strips tool surfaces and emits a capability_warning for a no-tools provider', async () => {
|
|
128
|
+
const provider = new CapturingProvider(NO_TOOLS_CAPABILITIES)
|
|
129
|
+
const tools = new ToolRegistry()
|
|
130
|
+
registerEchoTool(tools)
|
|
131
|
+
const events: RunEvent[] = []
|
|
132
|
+
|
|
133
|
+
const run = await drainQuery(
|
|
134
|
+
{
|
|
135
|
+
...baseParams(provider, tools, await mkWorkdir()),
|
|
136
|
+
messages: [createUserMessage('hello')],
|
|
137
|
+
},
|
|
138
|
+
(event) => {
|
|
139
|
+
events.push(event)
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
expect(run.status).toBe('completed')
|
|
144
|
+
|
|
145
|
+
// The model was never told about tools: no tools param on the wire…
|
|
146
|
+
expect(provider.lastParams?.tools).toBeUndefined()
|
|
147
|
+
// …and no tool section in the system prompt.
|
|
148
|
+
const systemPrompt = (provider.lastParams?.messages ?? [])
|
|
149
|
+
.filter((m) => m.role === 'system')
|
|
150
|
+
.map((m) => m.content)
|
|
151
|
+
.join('\n')
|
|
152
|
+
expect(systemPrompt).not.toContain('<available_tools>')
|
|
153
|
+
expect(systemPrompt).not.toContain('echo')
|
|
154
|
+
|
|
155
|
+
// The host got the machine-readable warning.
|
|
156
|
+
const warning = events.find(
|
|
157
|
+
(e): e is Extract<RunEvent, { type: 'capability_warning' }> =>
|
|
158
|
+
e.type === 'capability_warning',
|
|
159
|
+
)
|
|
160
|
+
expect(warning?.capability).toBe('tools')
|
|
161
|
+
expect(warning?.providerId).toBe('capturing')
|
|
162
|
+
expect(warning?.message).toContain('stripped')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('keeps tool surfaces intact for an undeclared (permissive-default) provider', async () => {
|
|
166
|
+
const provider = new CapturingProvider()
|
|
167
|
+
const tools = new ToolRegistry()
|
|
168
|
+
registerEchoTool(tools)
|
|
169
|
+
|
|
170
|
+
const run = await drainQuery({
|
|
171
|
+
...baseParams(provider, tools, await mkWorkdir()),
|
|
172
|
+
messages: [createUserMessage('hello')],
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
expect(run.status).toBe('completed')
|
|
176
|
+
expect(provider.lastParams?.tools?.map((t) => t.function.name)).toContain('echo')
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('emits a vision capability_warning when attachments hit a no-vision provider', async () => {
|
|
180
|
+
const provider = new CapturingProvider(NO_VISION_CAPABILITIES)
|
|
181
|
+
const events: RunEvent[] = []
|
|
182
|
+
|
|
183
|
+
const run = await drainQuery(
|
|
184
|
+
{
|
|
185
|
+
...baseParams(provider, new ToolRegistry(), await mkWorkdir()),
|
|
186
|
+
messages: [
|
|
187
|
+
createUserMessage('what is in this image?', [
|
|
188
|
+
{ data: 'aGVsbG8=', mediaType: 'image/png' },
|
|
189
|
+
]),
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
(event) => {
|
|
193
|
+
events.push(event)
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
expect(run.status).toBe('completed')
|
|
198
|
+
const warning = events.find(
|
|
199
|
+
(e): e is Extract<RunEvent, { type: 'capability_warning' }> =>
|
|
200
|
+
e.type === 'capability_warning',
|
|
201
|
+
)
|
|
202
|
+
expect(warning?.capability).toBe('vision')
|
|
203
|
+
expect(warning?.providerId).toBe('capturing')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('does not warn when no attachments are present on a no-vision provider', async () => {
|
|
207
|
+
const provider = new CapturingProvider(NO_VISION_CAPABILITIES)
|
|
208
|
+
const events: RunEvent[] = []
|
|
209
|
+
|
|
210
|
+
await drainQuery(
|
|
211
|
+
{
|
|
212
|
+
...baseParams(provider, new ToolRegistry(), await mkWorkdir()),
|
|
213
|
+
messages: [createUserMessage('plain text')],
|
|
214
|
+
},
|
|
215
|
+
(event) => {
|
|
216
|
+
events.push(event)
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
expect(events.some((e) => e.type === 'capability_warning')).toBe(false)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('strictCapabilities: true throws on a tools mismatch instead of degrading', async () => {
|
|
224
|
+
const provider = new CapturingProvider(NO_TOOLS_CAPABILITIES)
|
|
225
|
+
const tools = new ToolRegistry()
|
|
226
|
+
registerEchoTool(tools)
|
|
227
|
+
|
|
228
|
+
await expect(
|
|
229
|
+
drainQuery({
|
|
230
|
+
...baseParams(provider, tools, await mkWorkdir()),
|
|
231
|
+
messages: [createUserMessage('hello')],
|
|
232
|
+
strictCapabilities: true,
|
|
233
|
+
}),
|
|
234
|
+
).rejects.toThrow(/supportsTools: false/)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('strictCapabilities: true throws on a vision mismatch instead of degrading', async () => {
|
|
238
|
+
const provider = new CapturingProvider(NO_VISION_CAPABILITIES)
|
|
239
|
+
|
|
240
|
+
await expect(
|
|
241
|
+
drainQuery({
|
|
242
|
+
...baseParams(provider, new ToolRegistry(), await mkWorkdir()),
|
|
243
|
+
messages: [createUserMessage('describe', [{ data: 'aGVsbG8=', mediaType: 'image/png' }])],
|
|
244
|
+
strictCapabilities: true,
|
|
245
|
+
}),
|
|
246
|
+
).rejects.toThrow(/supportsVision: false/)
|
|
247
|
+
})
|
|
248
|
+
})
|