@aws-blocks/bb-agent 0.3.4 → 0.4.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/DESIGN.md +65 -16
- package/README.md +73 -6
- package/dist/agent.aws.d.ts +15 -1
- package/dist/agent.aws.d.ts.map +1 -1
- package/dist/agent.aws.js +49 -0
- package/dist/agent.d.ts +82 -14
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +238 -55
- package/dist/agentcore-bundle.d.ts +12 -0
- package/dist/agentcore-bundle.d.ts.map +1 -0
- package/dist/agentcore-bundle.js +150 -0
- package/dist/agentcore-bundle.test.d.ts +2 -0
- package/dist/agentcore-bundle.test.d.ts.map +1 -0
- package/dist/agentcore-bundle.test.js +46 -0
- package/dist/agentcore-entry.d.ts +21 -0
- package/dist/agentcore-entry.d.ts.map +1 -0
- package/dist/agentcore-entry.js +120 -0
- package/dist/agentcore-runtime.cdk.d.ts +27 -0
- package/dist/agentcore-runtime.cdk.d.ts.map +1 -0
- package/dist/agentcore-runtime.cdk.js +168 -0
- package/dist/index.aws.d.ts +1 -0
- package/dist/index.aws.d.ts.map +1 -1
- package/dist/index.cdk.d.ts +10 -4
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +27 -26
- package/dist/index.cdk.test.d.ts +2 -0
- package/dist/index.cdk.test.d.ts.map +1 -0
- package/dist/index.cdk.test.js +143 -0
- package/dist/index.mock.d.ts +1 -0
- package/dist/index.mock.d.ts.map +1 -1
- package/dist/index.test.js +404 -1
- package/dist/model-factory.d.ts +2 -2
- package/dist/model-factory.d.ts.map +1 -1
- package/dist/model-factory.js +2 -2
- package/dist/providers/canned.d.ts +8 -1
- package/dist/providers/canned.d.ts.map +1 -1
- package/dist/providers/canned.js +127 -42
- package/dist/types.d.ts +63 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +16 -9
- package/src/agent.aws.ts +58 -1
- package/src/agent.ts +269 -54
- package/src/agentcore-bundle.test.ts +52 -0
- package/src/agentcore-bundle.ts +162 -0
- package/src/agentcore-entry.ts +134 -0
- package/src/agentcore-runtime.cdk.ts +203 -0
- package/src/index.aws.ts +3 -0
- package/src/index.cdk.test.ts +167 -0
- package/src/index.cdk.ts +29 -29
- package/src/index.mock.ts +3 -0
- package/src/index.test.ts +449 -1
- package/src/model-factory.ts +3 -3
- package/src/providers/canned.ts +131 -36
- package/src/types.ts +64 -1
- package/src/version.ts +1 -1
package/src/providers/canned.ts
CHANGED
|
@@ -17,11 +17,18 @@
|
|
|
17
17
|
import { Model } from '@strands-agents/sdk';
|
|
18
18
|
import type { Message, ModelStreamEvent, StreamOptions } from '@strands-agents/sdk';
|
|
19
19
|
import { ToolResultBlock } from '@strands-agents/sdk';
|
|
20
|
+
import type { CannedToolHints } from '../types.js';
|
|
20
21
|
|
|
21
22
|
interface CannedConfig {
|
|
22
23
|
modelId: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
interface CannedProviderOptions {
|
|
27
|
+
modelId?: string;
|
|
28
|
+
/** Per-tool hints (examples, triggers) keyed by tool name. */
|
|
29
|
+
hints?: Map<string, CannedToolHints>;
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
const CANNED_RESPONSES: Record<string, string> = {
|
|
26
33
|
weather: 'The weather is 22°C and sunny. [canned response]',
|
|
27
34
|
order: 'Order #12345 has been shipped and is on its way. [canned response]',
|
|
@@ -30,28 +37,48 @@ const CANNED_RESPONSES: Record<string, string> = {
|
|
|
30
37
|
|
|
31
38
|
const DEFAULT_RESPONSE = 'This is a canned mock response. No real model was called. [canned]';
|
|
32
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Pick a canned text response by keyword, matched on word boundaries for the same reason
|
|
42
|
+
* tool matching is: substring matching fired `order` inside "reorder" and `help` inside
|
|
43
|
+
* "helper", the same false-positive class the tool matcher avoids.
|
|
44
|
+
*/
|
|
33
45
|
function matchResponse(prompt: string): string {
|
|
34
46
|
const lower = prompt.toLowerCase();
|
|
35
47
|
for (const [keyword, response] of Object.entries(CANNED_RESPONSES)) {
|
|
36
|
-
if (lower
|
|
48
|
+
if (promptMentionsWord(lower, keyword)) return response;
|
|
37
49
|
}
|
|
38
50
|
return DEFAULT_RESPONSE;
|
|
39
51
|
}
|
|
40
52
|
|
|
53
|
+
const wordPatternCache = new Map<string, RegExp>();
|
|
54
|
+
|
|
41
55
|
/**
|
|
42
|
-
*
|
|
56
|
+
* Compile a word-boundary matcher for a word or phrase, cached by phrase.
|
|
43
57
|
* Uses `\b...\b` rather than substring `includes()` so a tool word like "cat"
|
|
44
58
|
* (from `getCat`) is NOT triggered by an unrelated word like "category", and
|
|
45
|
-
* "pass" (from `getPass`) is not triggered by "password".
|
|
46
|
-
* escaped so punctuation in tool names can't break the pattern
|
|
59
|
+
* "pass" (from `getPass`) is not triggered by "password". Regex metacharacters are
|
|
60
|
+
* escaped so punctuation in tool names can't break the pattern, and internal
|
|
61
|
+
* whitespace becomes `\s+` so a multi-word phrase tolerates irregular spacing.
|
|
62
|
+
* Compiled patterns are cached because matching re-runs for every tool on every
|
|
63
|
+
* `stream()` call, and the key space is bounded by the agent's tool and trigger set.
|
|
47
64
|
*/
|
|
65
|
+
function wordBoundaryPattern(phrase: string): RegExp {
|
|
66
|
+
let pattern = wordPatternCache.get(phrase);
|
|
67
|
+
if (!pattern) {
|
|
68
|
+
const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+');
|
|
69
|
+
pattern = new RegExp(`\\b${escaped}\\b`);
|
|
70
|
+
wordPatternCache.set(phrase, pattern);
|
|
71
|
+
}
|
|
72
|
+
return pattern;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Match a word against an already-lowercased prompt on word boundaries. */
|
|
48
76
|
function promptMentionsWord(lowerPrompt: string, word: string): boolean {
|
|
49
|
-
|
|
50
|
-
return new RegExp(`\\b${escaped}\\b`).test(lowerPrompt);
|
|
77
|
+
return wordBoundaryPattern(word).test(lowerPrompt);
|
|
51
78
|
}
|
|
52
79
|
|
|
53
80
|
/** Find ALL tools mentioned in the prompt (for parallel tool calls). */
|
|
54
|
-
function findAllToolMatches(prompt: string, toolSpecs?: { name: string }[]): string[] {
|
|
81
|
+
function findAllToolMatches(prompt: string, toolSpecs?: { name: string }[], hints?: Map<string, CannedToolHints>): string[] {
|
|
55
82
|
if (!toolSpecs?.length) return [];
|
|
56
83
|
const lower = prompt.toLowerCase();
|
|
57
84
|
return toolSpecs.filter(t => {
|
|
@@ -60,7 +87,15 @@ function findAllToolMatches(prompt: string, toolSpecs?: { name: string }[]): str
|
|
|
60
87
|
// Split camelCase into words (getWeather -> "get weather") and match each
|
|
61
88
|
// on word boundaries. Skip short words (<=2 chars) to avoid noise.
|
|
62
89
|
const words = t.name.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase().split(' ');
|
|
63
|
-
|
|
90
|
+
if (words.some(w => w.length > 2 && promptMentionsWord(lower, w))) return true;
|
|
91
|
+
// Extra trigger keywords declared via `cannedTriggers`. Single and multi-word triggers
|
|
92
|
+
// both match on word boundaries (consistent with tool-name matching), so "log in" is not
|
|
93
|
+
// triggered by "backlog in" and internal whitespace is flexible (matches one-or-more spaces).
|
|
94
|
+
const triggers = hints?.get(t.name)?.triggers;
|
|
95
|
+
return triggers?.some(tr => {
|
|
96
|
+
const low = tr.trim().toLowerCase();
|
|
97
|
+
return low ? wordBoundaryPattern(low).test(lower) : false;
|
|
98
|
+
}) ?? false;
|
|
64
99
|
}).map(t => t.name);
|
|
65
100
|
}
|
|
66
101
|
|
|
@@ -87,45 +122,107 @@ function getToolResultText(messages: Message[]): string {
|
|
|
87
122
|
return results.join(' | ');
|
|
88
123
|
}
|
|
89
124
|
|
|
125
|
+
/** Sentinel for a property whose shape carries no usable signal (distinct from a legitimate `null`/`0`/`false`). */
|
|
126
|
+
const NO_PLACEHOLDER = Symbol('no-placeholder');
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolve one property's placeholder value, or `NO_PLACEHOLDER` if its shape gives no signal.
|
|
130
|
+
* Order matters: an authored `default` (from Zod `.default()`) is the most realistic value, then
|
|
131
|
+
* a fixed `const`/`enum` member, then a union variant, then a per-type placeholder. `!== undefined`
|
|
132
|
+
* rather than truthiness so a `default` of `0`, `false`, or `''` is honored.
|
|
133
|
+
*/
|
|
134
|
+
function placeholderForProperty(prop: any): any {
|
|
135
|
+
if (prop?.default !== undefined) return prop.default;
|
|
136
|
+
if (prop?.const !== undefined) return prop.const;
|
|
137
|
+
if (prop?.enum?.length) return prop.enum[0];
|
|
138
|
+
// Zod unions (`z.union`, `z.discriminatedUnion`) surface as anyOf/oneOf; any one
|
|
139
|
+
// satisfying variant is enough for a mock, so take the first that resolves.
|
|
140
|
+
const variants = prop?.anyOf ?? prop?.oneOf;
|
|
141
|
+
if (Array.isArray(variants)) {
|
|
142
|
+
for (const variant of variants) {
|
|
143
|
+
const resolved = placeholderForProperty(variant);
|
|
144
|
+
if (resolved !== NO_PLACEHOLDER) return resolved;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
switch (prop?.type) {
|
|
148
|
+
case 'string': return 'sample';
|
|
149
|
+
case 'number':
|
|
150
|
+
case 'integer': return 1;
|
|
151
|
+
case 'boolean': return true;
|
|
152
|
+
case 'array': return [];
|
|
153
|
+
case 'object': return generatePlaceholderInput(prop);
|
|
154
|
+
default: return NO_PLACEHOLDER;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
90
158
|
/** Generate placeholder input from a JSON Schema. Produces values that pass validation. */
|
|
91
159
|
function generatePlaceholderInput(schema: any): any {
|
|
92
160
|
if (!schema || typeof schema !== 'object') return {};
|
|
93
|
-
if (schema.type
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
result[key] = generatePlaceholderInput(prop);
|
|
107
|
-
}
|
|
161
|
+
if (schema.type !== 'object' || !schema.properties) return {};
|
|
162
|
+
const required: unknown[] = Array.isArray(schema.required) ? schema.required : [];
|
|
163
|
+
const result: Record<string, any> = {};
|
|
164
|
+
for (const [key, prop] of Object.entries(schema.properties) as [string, any][]) {
|
|
165
|
+
const value = placeholderForProperty(prop);
|
|
166
|
+
if (value !== NO_PLACEHOLDER) {
|
|
167
|
+
result[key] = value;
|
|
168
|
+
} else if (required.includes(key)) {
|
|
169
|
+
// An unrecognized shape (untyped, or a union of only unrecognized variants) yields no
|
|
170
|
+
// placeholder. Omitting a *required* field makes the emitted call fail validation before
|
|
171
|
+
// the tool ever runs, so fall back to a string. Optional fields stay omitted: absence is
|
|
172
|
+
// valid there, and inventing a wrong-typed value would break calls that used to work.
|
|
173
|
+
result[key] = 'sample';
|
|
108
174
|
}
|
|
109
|
-
return result;
|
|
110
175
|
}
|
|
111
|
-
return
|
|
176
|
+
return result;
|
|
112
177
|
}
|
|
113
178
|
|
|
114
|
-
|
|
115
|
-
|
|
179
|
+
const warnedExampleKeys = new Set<string>();
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Warn (once per tool+field) when a `cannedExamples` key isn't a field of the tool's
|
|
183
|
+
* inputSchema — almost always a typo in the hint. Only ever reached through the canned
|
|
184
|
+
* provider, which is local-dev-only, so this never warns in a deployed agent. The value
|
|
185
|
+
* is still merged through and nothing throws: a bad hint must not break local dev.
|
|
186
|
+
* Skipped when the schema exposes no `properties`, where unknown keys are unknowable.
|
|
187
|
+
*/
|
|
188
|
+
function warnUnknownExampleKeys(toolName: string, examples: Record<string, unknown>, inputSchema?: any): void {
|
|
189
|
+
const properties = inputSchema?.properties;
|
|
190
|
+
if (!properties) return;
|
|
191
|
+
for (const key of Object.keys(examples)) {
|
|
192
|
+
if (key in properties) continue;
|
|
193
|
+
const seen = `${toolName}.${key}`;
|
|
194
|
+
if (warnedExampleKeys.has(seen)) continue;
|
|
195
|
+
warnedExampleKeys.add(seen);
|
|
196
|
+
console.warn(
|
|
197
|
+
`[canned] cannedExamples for tool "${toolName}" sets "${key}", which is not a field of its ` +
|
|
198
|
+
`parameters schema (${Object.keys(properties).join(', ') || 'none'}). Check for a typo — the value is still sent.`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Look up a tool's inputSchema from toolSpecs and generate placeholder input, shallow-merging
|
|
205
|
+
* any `cannedExamples` (from hints) on top so realistic values win over generated placeholders.
|
|
206
|
+
*/
|
|
207
|
+
function getToolInput(toolName: string, toolSpecs?: { name: string; inputSchema?: any }[], hints?: Map<string, CannedToolHints>): string {
|
|
116
208
|
const spec = toolSpecs?.find(t => t.name === toolName);
|
|
117
|
-
|
|
118
|
-
|
|
209
|
+
const base = spec?.inputSchema ? generatePlaceholderInput(spec.inputSchema) : {};
|
|
210
|
+
const examples = hints?.get(toolName)?.examples;
|
|
211
|
+
if (!examples) return JSON.stringify(base);
|
|
212
|
+
warnUnknownExampleKeys(toolName, examples, spec?.inputSchema);
|
|
213
|
+
return JSON.stringify({ ...base, ...examples });
|
|
119
214
|
}
|
|
120
215
|
|
|
121
216
|
let toolCallCounter = 0;
|
|
122
217
|
|
|
123
218
|
export class CannedProvider extends Model<CannedConfig> {
|
|
124
219
|
private config: CannedConfig;
|
|
220
|
+
private hints: Map<string, CannedToolHints>;
|
|
125
221
|
|
|
126
|
-
constructor(
|
|
222
|
+
constructor(options?: CannedProviderOptions) {
|
|
127
223
|
super();
|
|
128
|
-
this.config = { modelId:
|
|
224
|
+
this.config = { modelId: options?.modelId ?? 'canned-mock' };
|
|
225
|
+
this.hints = options?.hints ?? new Map();
|
|
129
226
|
}
|
|
130
227
|
|
|
131
228
|
updateConfig(config: Partial<CannedConfig>): void {
|
|
@@ -150,7 +247,7 @@ export class CannedProvider extends Model<CannedConfig> {
|
|
|
150
247
|
}
|
|
151
248
|
|
|
152
249
|
// Check if prompt mentions tool names — trigger tool call(s)
|
|
153
|
-
const toolMatches = findAllToolMatches(prompt, options?.toolSpecs);
|
|
250
|
+
const toolMatches = findAllToolMatches(prompt, options?.toolSpecs, this.hints);
|
|
154
251
|
if (toolMatches.length > 1) {
|
|
155
252
|
yield* this.emitParallelToolCalls(toolMatches, options?.toolSpecs);
|
|
156
253
|
return;
|
|
@@ -161,8 +258,6 @@ export class CannedProvider extends Model<CannedConfig> {
|
|
|
161
258
|
return;
|
|
162
259
|
}
|
|
163
260
|
|
|
164
|
-
// Default: keyword-based text response
|
|
165
|
-
|
|
166
261
|
// Default: keyword-based text response
|
|
167
262
|
yield* this.emitText(matchResponse(prompt));
|
|
168
263
|
}
|
|
@@ -185,7 +280,7 @@ export class CannedProvider extends Model<CannedConfig> {
|
|
|
185
280
|
for (const toolName of toolNames) {
|
|
186
281
|
const toolUseId = `canned-tool-${++toolCallCounter}`;
|
|
187
282
|
yield { type: 'modelContentBlockStartEvent', start: { type: 'toolUseStart', name: toolName, toolUseId } };
|
|
188
|
-
yield { type: 'modelContentBlockDeltaEvent', delta: { type: 'toolUseInputDelta', input: getToolInput(toolName, toolSpecs) } };
|
|
283
|
+
yield { type: 'modelContentBlockDeltaEvent', delta: { type: 'toolUseInputDelta', input: getToolInput(toolName, toolSpecs, this.hints) } };
|
|
189
284
|
yield { type: 'modelContentBlockStopEvent' };
|
|
190
285
|
}
|
|
191
286
|
yield { type: 'modelMessageStopEvent', stopReason: 'toolUse' };
|
|
@@ -197,7 +292,7 @@ export class CannedProvider extends Model<CannedConfig> {
|
|
|
197
292
|
const toolUseId = `canned-tool-${++toolCallCounter}`;
|
|
198
293
|
yield { type: 'modelMessageStartEvent', role: 'assistant' };
|
|
199
294
|
yield { type: 'modelContentBlockStartEvent', start: { type: 'toolUseStart', name: toolName, toolUseId } };
|
|
200
|
-
yield { type: 'modelContentBlockDeltaEvent', delta: { type: 'toolUseInputDelta', input: getToolInput(toolName, toolSpecs) } };
|
|
295
|
+
yield { type: 'modelContentBlockDeltaEvent', delta: { type: 'toolUseInputDelta', input: getToolInput(toolName, toolSpecs, this.hints) } };
|
|
201
296
|
yield { type: 'modelContentBlockStopEvent' };
|
|
202
297
|
yield { type: 'modelMessageStopEvent', stopReason: 'toolUse' };
|
|
203
298
|
yield { type: 'modelMetadataEvent', usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }, metrics: { latencyMs: 0 } };
|
package/src/types.ts
CHANGED
|
@@ -85,7 +85,39 @@ export interface AgentConfig<TContext = DefaultToolContext> {
|
|
|
85
85
|
*/
|
|
86
86
|
toolContextSchema?: z.ZodType<TContext>;
|
|
87
87
|
conversation?: ConversationManagerConfig;
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Safety cap on the number of **model (Bedrock) invocations per turn**.
|
|
90
|
+
*
|
|
91
|
+
* The agent runs a reason→act loop where each iteration is one model call,
|
|
92
|
+
* optionally followed by tool calls; a model call that requests no tools ends
|
|
93
|
+
* the turn. Model calls are the unit Bedrock bills for, so this is the most
|
|
94
|
+
* direct guard against a runaway agent that loops indefinitely — and because
|
|
95
|
+
* every tool round needs a model call, it transitively bounds tool loops too.
|
|
96
|
+
*
|
|
97
|
+
* When the cap is hit the turn is stopped and the client receives an `error`
|
|
98
|
+
* chunk instead of `done`. Raise it for agents that legitimately reason over
|
|
99
|
+
* many steps, or set it to `false` to disable the cap entirely. This bounds
|
|
100
|
+
* call *count*, not tokens or wall-clock — pair it with a billing/CloudWatch
|
|
101
|
+
* alarm for defense in depth.
|
|
102
|
+
*
|
|
103
|
+
* Scope: the count covers the whole turn, including across a human-in-the-loop
|
|
104
|
+
* interrupt — it is kept in the agent's session state, so `resume()` continues
|
|
105
|
+
* on the same budget rather than starting a fresh one. Must be a positive
|
|
106
|
+
* integer or `false`; anything else throws `InvalidModelConfigException`.
|
|
107
|
+
*/
|
|
108
|
+
maxLlmCalls?: number | false;
|
|
109
|
+
/**
|
|
110
|
+
* Safety cap on the number of **tool calls per turn**.
|
|
111
|
+
*
|
|
112
|
+
* Bounds tool-loop runaways specifically (a turn that keeps invoking tools).
|
|
113
|
+
* Parallel tool batches count each individual call. Raise it for agents that
|
|
114
|
+
* legitimately chain many tools in a single turn, or set it to `false` to
|
|
115
|
+
* disable the cap entirely. When the cap is hit the turn is stopped and the
|
|
116
|
+
* client receives an `error` chunk instead of `done`. Like `maxLlmCalls`, the
|
|
117
|
+
* count covers the whole turn (it survives `resume()` after an interrupt) and
|
|
118
|
+
* must be a positive integer or `false`.
|
|
119
|
+
*/
|
|
120
|
+
maxToolIterations?: number | false;
|
|
89
121
|
/** Controls how text chunks are published to the client via Realtime.
|
|
90
122
|
* - `'token'`: publish every text delta immediately
|
|
91
123
|
* - `'block'` (default): buffer text and publish when a full content block completes
|
|
@@ -106,6 +138,12 @@ export interface AgentConfig<TContext = DefaultToolContext> {
|
|
|
106
138
|
* Ignored by the mock and browser runtimes.
|
|
107
139
|
*/
|
|
108
140
|
removalPolicy?: 'destroy' | 'retain';
|
|
141
|
+
/**
|
|
142
|
+
* @internal Pre-built AgentCore code-asset directory to use instead of co-bundling the app
|
|
143
|
+
* backend at synth. Set by unit tests and apps that pre-bundle; normal apps leave this unset
|
|
144
|
+
* and the backend is co-bundled automatically. Ignored by the mock/browser runtimes.
|
|
145
|
+
*/
|
|
146
|
+
agentcoreAssetPath?: string;
|
|
109
147
|
/** Optional logger for internal operations. When omitted, a default Logger at error level is created. */
|
|
110
148
|
logger?: ChildLogger;
|
|
111
149
|
}
|
|
@@ -175,6 +213,31 @@ export interface ToolDefinition<TContext = DefaultToolContext, TParams extends z
|
|
|
175
213
|
* - `interrupt` — pause the agent for human input
|
|
176
214
|
*/
|
|
177
215
|
handler: (args: ToolHandlerArgs<z.infer<TParams>, TContext>) => Promise<JSONValue>;
|
|
216
|
+
/**
|
|
217
|
+
* Local-dev only. Realistic tool input for the `canned` mock provider. Shallow-merged
|
|
218
|
+
* over the generated placeholder input (your fields win; unspecified fields fall back to
|
|
219
|
+
* schema defaults / generic placeholders). The merge is one level deep — a nested-object
|
|
220
|
+
* example replaces that whole generated sub-object rather than deep-merging into it.
|
|
221
|
+
* Ignored by the bedrock/openai providers.
|
|
222
|
+
*/
|
|
223
|
+
cannedExamples?: Record<string, JSONValue>;
|
|
224
|
+
/**
|
|
225
|
+
* Local-dev only. Extra keyword phrases that make the `canned` mock provider select this
|
|
226
|
+
* tool, in addition to the tool name and its camelCase words. Single and multi-word phrases
|
|
227
|
+
* match on word boundaries (so "log in" is not triggered by "backlog in"). Ignored by the
|
|
228
|
+
* bedrock/openai providers.
|
|
229
|
+
*/
|
|
230
|
+
cannedTriggers?: string[];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Local-dev hints for the `canned` mock provider, keyed by tool name and threaded from
|
|
235
|
+
* `ToolDefinition.cannedExamples`/`cannedTriggers` into the provider (Strands strips these
|
|
236
|
+
* fields when converting tools, so they're plumbed explicitly). Ignored by real providers.
|
|
237
|
+
*/
|
|
238
|
+
export interface CannedToolHints {
|
|
239
|
+
examples?: Record<string, JSONValue>;
|
|
240
|
+
triggers?: string[];
|
|
178
241
|
}
|
|
179
242
|
|
|
180
243
|
/**
|
package/src/version.ts
CHANGED