@strands-agents/sdk 1.2.0 → 1.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/README.md +13 -13
- package/dist/src/__fixtures__/test-sandbox.node.d.ts +15 -0
- package/dist/src/__fixtures__/test-sandbox.node.d.ts.map +1 -0
- package/dist/src/__fixtures__/test-sandbox.node.js +22 -0
- package/dist/src/__fixtures__/test-sandbox.node.js.map +1 -0
- package/dist/src/__tests__/mcp.test.js +14 -14
- package/dist/src/__tests__/mcp.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.test.js +195 -0
- package/dist/src/agent/__tests__/agent.test.js.map +1 -1
- package/dist/src/agent/__tests__/printer.test.js +58 -18
- package/dist/src/agent/__tests__/printer.test.js.map +1 -1
- package/dist/src/agent/__tests__/tool-caller.test.d.ts +2 -0
- package/dist/src/agent/__tests__/tool-caller.test.d.ts.map +1 -0
- package/dist/src/agent/__tests__/tool-caller.test.js +459 -0
- package/dist/src/agent/__tests__/tool-caller.test.js.map +1 -0
- package/dist/src/agent/agent.d.ts +68 -2
- package/dist/src/agent/agent.d.ts.map +1 -1
- package/dist/src/agent/agent.js +125 -66
- package/dist/src/agent/agent.js.map +1 -1
- package/dist/src/agent/printer.d.ts +14 -1
- package/dist/src/agent/printer.d.ts.map +1 -1
- package/dist/src/agent/printer.js +33 -5
- package/dist/src/agent/printer.js.map +1 -1
- package/dist/src/agent/tool-caller.d.ts +149 -0
- package/dist/src/agent/tool-caller.d.ts.map +1 -0
- package/dist/src/agent/tool-caller.js +198 -0
- package/dist/src/agent/tool-caller.js.map +1 -0
- package/dist/src/errors.d.ts +17 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +21 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/hooks/types.d.ts +2 -0
- package/dist/src/hooks/types.d.ts.map +1 -1
- package/dist/src/hooks/types.js +2 -0
- package/dist/src/hooks/types.js.map +1 -1
- package/dist/src/index.d.ts +10 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interventions/__tests__/handler.test.d.ts +2 -0
- package/dist/src/interventions/__tests__/handler.test.d.ts.map +1 -0
- package/dist/src/interventions/__tests__/handler.test.js +35 -0
- package/dist/src/interventions/__tests__/handler.test.js.map +1 -0
- package/dist/src/interventions/__tests__/registry.test.d.ts +2 -0
- package/dist/src/interventions/__tests__/registry.test.d.ts.map +1 -0
- package/dist/src/interventions/__tests__/registry.test.js +692 -0
- package/dist/src/interventions/__tests__/registry.test.js.map +1 -0
- package/dist/src/interventions/actions.d.ts +186 -0
- package/dist/src/interventions/actions.d.ts.map +1 -0
- package/dist/src/interventions/actions.js +56 -0
- package/dist/src/interventions/actions.js.map +1 -0
- package/dist/src/interventions/handler.d.ts +43 -0
- package/dist/src/interventions/handler.d.ts.map +1 -0
- package/dist/src/interventions/handler.js +41 -0
- package/dist/src/interventions/handler.js.map +1 -0
- package/dist/src/interventions/index.d.ts +12 -0
- package/dist/src/interventions/index.d.ts.map +1 -0
- package/dist/src/interventions/index.js +4 -0
- package/dist/src/interventions/index.js.map +1 -0
- package/dist/src/interventions/registry.d.ts +36 -0
- package/dist/src/interventions/registry.d.ts.map +1 -0
- package/dist/src/interventions/registry.js +252 -0
- package/dist/src/interventions/registry.js.map +1 -0
- package/dist/src/mcp.d.ts +20 -15
- package/dist/src/mcp.d.ts.map +1 -1
- package/dist/src/mcp.js +15 -8
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/models/__tests__/anthropic.test.js +72 -8
- package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
- package/dist/src/models/__tests__/bedrock.test.js +142 -0
- package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
- package/dist/src/models/anthropic.d.ts +10 -0
- package/dist/src/models/anthropic.d.ts.map +1 -1
- package/dist/src/models/anthropic.js +24 -8
- package/dist/src/models/anthropic.js.map +1 -1
- package/dist/src/models/bedrock.d.ts +26 -1
- package/dist/src/models/bedrock.d.ts.map +1 -1
- package/dist/src/models/bedrock.js +22 -4
- package/dist/src/models/bedrock.js.map +1 -1
- package/dist/src/models/openai/__tests__/chat.test.js +2 -10
- package/dist/src/models/openai/__tests__/chat.test.js.map +1 -1
- package/dist/src/models/openai/__tests__/errors.test.d.ts +2 -0
- package/dist/src/models/openai/__tests__/errors.test.d.ts.map +1 -0
- package/dist/src/models/openai/__tests__/errors.test.js +30 -0
- package/dist/src/models/openai/__tests__/errors.test.js.map +1 -0
- package/dist/src/models/openai/__tests__/mantle.test.d.ts +2 -0
- package/dist/src/models/openai/__tests__/mantle.test.d.ts.map +1 -0
- package/dist/src/models/openai/__tests__/mantle.test.js +189 -0
- package/dist/src/models/openai/__tests__/mantle.test.js.map +1 -0
- package/dist/src/models/openai/__tests__/responses.test.js +0 -19
- package/dist/src/models/openai/__tests__/responses.test.js.map +1 -1
- package/dist/src/models/openai/errors.d.ts.map +1 -1
- package/dist/src/models/openai/errors.js +5 -3
- package/dist/src/models/openai/errors.js.map +1 -1
- package/dist/src/models/openai/index.d.ts +1 -0
- package/dist/src/models/openai/index.d.ts.map +1 -1
- package/dist/src/models/openai/mantle.d.ts +77 -0
- package/dist/src/models/openai/mantle.d.ts.map +1 -0
- package/dist/src/models/openai/mantle.js +83 -0
- package/dist/src/models/openai/mantle.js.map +1 -0
- package/dist/src/models/openai/model.d.ts.map +1 -1
- package/dist/src/models/openai/model.js +29 -1
- package/dist/src/models/openai/model.js.map +1 -1
- package/dist/src/models/openai/types.d.ts +11 -0
- package/dist/src/models/openai/types.d.ts.map +1 -1
- package/dist/src/multiagent/__tests__/nodes.test.js +50 -0
- package/dist/src/multiagent/__tests__/nodes.test.js.map +1 -1
- package/dist/src/multiagent/nodes.d.ts +23 -2
- package/dist/src/multiagent/nodes.d.ts.map +1 -1
- package/dist/src/multiagent/nodes.js +18 -4
- package/dist/src/multiagent/nodes.js.map +1 -1
- package/dist/src/registry/__tests__/tool-registry.test.js +50 -1
- package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -1
- package/dist/src/registry/tool-registry.d.ts +13 -0
- package/dist/src/registry/tool-registry.d.ts.map +1 -1
- package/dist/src/registry/tool-registry.js +35 -1
- package/dist/src/registry/tool-registry.js.map +1 -1
- package/dist/src/sandbox/__tests__/posix-shell.test.node.d.ts +2 -0
- package/dist/src/sandbox/__tests__/posix-shell.test.node.d.ts.map +1 -0
- package/dist/src/sandbox/__tests__/posix-shell.test.node.js +252 -0
- package/dist/src/sandbox/__tests__/posix-shell.test.node.js.map +1 -0
- package/dist/src/sandbox/base.d.ts +138 -0
- package/dist/src/sandbox/base.d.ts.map +1 -0
- package/dist/src/sandbox/base.js +84 -0
- package/dist/src/sandbox/base.js.map +1 -0
- package/dist/src/sandbox/constants.d.ts +7 -0
- package/dist/src/sandbox/constants.d.ts.map +1 -0
- package/dist/src/sandbox/constants.js +7 -0
- package/dist/src/sandbox/constants.js.map +1 -0
- package/dist/src/sandbox/posix-shell.d.ts +32 -0
- package/dist/src/sandbox/posix-shell.d.ts.map +1 -0
- package/dist/src/sandbox/posix-shell.js +78 -0
- package/dist/src/sandbox/posix-shell.js.map +1 -0
- package/dist/src/sandbox/stream-process.d.ts +32 -0
- package/dist/src/sandbox/stream-process.d.ts.map +1 -0
- package/dist/src/sandbox/stream-process.js +160 -0
- package/dist/src/sandbox/stream-process.js.map +1 -0
- package/dist/src/sandbox/types.d.ts +57 -0
- package/dist/src/sandbox/types.d.ts.map +1 -0
- package/dist/src/sandbox/types.js +8 -0
- package/dist/src/sandbox/types.js.map +1 -0
- package/dist/src/telemetry/__tests__/meter.test.js +11 -0
- package/dist/src/telemetry/__tests__/meter.test.js.map +1 -1
- package/dist/src/telemetry/meter.d.ts +10 -6
- package/dist/src/telemetry/meter.d.ts.map +1 -1
- package/dist/src/telemetry/meter.js +16 -3
- package/dist/src/telemetry/meter.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/types/__tests__/messages.test.js +28 -0
- package/dist/src/types/__tests__/messages.test.js.map +1 -1
- package/dist/src/types/agent.d.ts +51 -0
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/agent.js.map +1 -1
- package/dist/src/types/lifecycle-observer.d.ts +18 -0
- package/dist/src/types/lifecycle-observer.d.ts.map +1 -0
- package/dist/src/types/lifecycle-observer.js +2 -0
- package/dist/src/types/lifecycle-observer.js.map +1 -0
- package/dist/src/types/messages.d.ts +20 -2
- package/dist/src/types/messages.d.ts.map +1 -1
- package/dist/src/types/messages.js +9 -0
- package/dist/src/types/messages.js.map +1 -1
- package/dist/src/utils/shell-quote.d.ts +12 -0
- package/dist/src/utils/shell-quote.d.ts.map +1 -0
- package/dist/src/utils/shell-quote.js +14 -0
- package/dist/src/utils/shell-quote.js.map +1 -0
- package/dist/src/vended-interventions/hitl/__tests__/hitl.test.d.ts +2 -0
- package/dist/src/vended-interventions/hitl/__tests__/hitl.test.d.ts.map +1 -0
- package/dist/src/vended-interventions/hitl/__tests__/hitl.test.js +358 -0
- package/dist/src/vended-interventions/hitl/__tests__/hitl.test.js.map +1 -0
- package/dist/src/vended-interventions/hitl/hitl.d.ts +115 -0
- package/dist/src/vended-interventions/hitl/hitl.d.ts.map +1 -0
- package/dist/src/vended-interventions/hitl/hitl.js +138 -0
- package/dist/src/vended-interventions/hitl/hitl.js.map +1 -0
- package/dist/src/vended-interventions/hitl/index.d.ts +24 -0
- package/dist/src/vended-interventions/hitl/index.d.ts.map +1 -0
- package/dist/src/vended-interventions/hitl/index.js +23 -0
- package/dist/src/vended-interventions/hitl/index.js.map +1 -0
- package/dist/src/vended-interventions/steering/__tests__/handler.test.d.ts +2 -0
- package/dist/src/vended-interventions/steering/__tests__/handler.test.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/__tests__/handler.test.js +163 -0
- package/dist/src/vended-interventions/steering/__tests__/handler.test.js.map +1 -0
- package/dist/src/vended-interventions/steering/__tests__/llm.test.d.ts +2 -0
- package/dist/src/vended-interventions/steering/__tests__/llm.test.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/__tests__/llm.test.js +60 -0
- package/dist/src/vended-interventions/steering/__tests__/llm.test.js.map +1 -0
- package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.d.ts +2 -0
- package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.js +94 -0
- package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.js.map +1 -0
- package/dist/src/vended-interventions/steering/handlers/handler.d.ts +64 -0
- package/dist/src/vended-interventions/steering/handlers/handler.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/handlers/handler.js +71 -0
- package/dist/src/vended-interventions/steering/handlers/handler.js.map +1 -0
- package/dist/src/vended-interventions/steering/handlers/llm.d.ts +72 -0
- package/dist/src/vended-interventions/steering/handlers/llm.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/handlers/llm.js +177 -0
- package/dist/src/vended-interventions/steering/handlers/llm.js.map +1 -0
- package/dist/src/vended-interventions/steering/index.d.ts +31 -0
- package/dist/src/vended-interventions/steering/index.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/index.js +32 -0
- package/dist/src/vended-interventions/steering/index.js.map +1 -0
- package/dist/src/vended-interventions/steering/providers/context-provider.d.ts +55 -0
- package/dist/src/vended-interventions/steering/providers/context-provider.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/providers/context-provider.js +8 -0
- package/dist/src/vended-interventions/steering/providers/context-provider.js.map +1 -0
- package/dist/src/vended-interventions/steering/providers/tool-ledger.d.ts +49 -0
- package/dist/src/vended-interventions/steering/providers/tool-ledger.d.ts.map +1 -0
- package/dist/src/vended-interventions/steering/providers/tool-ledger.js +75 -0
- package/dist/src/vended-interventions/steering/providers/tool-ledger.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js +208 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js.map +1 -1
- package/dist/src/vended-plugins/context-offloader/__tests__/search.test.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/search.test.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/search.test.js +149 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/search.test.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -1
- package/dist/src/vended-plugins/context-offloader/plugin.js +57 -10
- package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -1
- package/dist/src/vended-plugins/context-offloader/search.d.ts +25 -0
- package/dist/src/vended-plugins/context-offloader/search.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/search.js +120 -0
- package/dist/src/vended-plugins/context-offloader/search.js.map +1 -0
- package/dist/src/vended-plugins/index.d.ts +11 -0
- package/dist/src/vended-plugins/index.d.ts.map +1 -0
- package/dist/src/vended-plugins/index.js +11 -0
- package/dist/src/vended-plugins/index.js.map +1 -0
- package/dist/src/vended-tools/index.d.ts +17 -0
- package/dist/src/vended-tools/index.d.ts.map +1 -0
- package/dist/src/vended-tools/index.js +17 -0
- package/dist/src/vended-tools/index.js.map +1 -0
- package/package.json +37 -11
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-in-the-loop intervention for Strands Agents.
|
|
3
|
+
*
|
|
4
|
+
* Pauses agent execution before tool calls to request human approval.
|
|
5
|
+
* Defaults to interrupt/resume mode for stateless deployments.
|
|
6
|
+
* Pass `ask: 'stdio'` for CLI prompting or a custom `ask` function for other UIs.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { Agent } from '@strands-agents/sdk'
|
|
11
|
+
* import { HumanInTheLoop } from '@strands-agents/sdk/vended-interventions/hitl'
|
|
12
|
+
*
|
|
13
|
+
* const agent = new Agent({
|
|
14
|
+
* tools: [deleteTool, readTool],
|
|
15
|
+
* interventions: [new HumanInTheLoop({ allowedTools: ['readTool'] })],
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Default: agent pauses with stopReason 'interrupt', caller resumes with response
|
|
19
|
+
* const result = await agent.invoke('Delete the file')
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export { HumanInTheLoop } from './hitl.js';
|
|
23
|
+
export type { HumanInTheLoopConfig } from './hitl.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/vended-interventions/hitl/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-in-the-loop intervention for Strands Agents.
|
|
3
|
+
*
|
|
4
|
+
* Pauses agent execution before tool calls to request human approval.
|
|
5
|
+
* Defaults to interrupt/resume mode for stateless deployments.
|
|
6
|
+
* Pass `ask: 'stdio'` for CLI prompting or a custom `ask` function for other UIs.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { Agent } from '@strands-agents/sdk'
|
|
11
|
+
* import { HumanInTheLoop } from '@strands-agents/sdk/vended-interventions/hitl'
|
|
12
|
+
*
|
|
13
|
+
* const agent = new Agent({
|
|
14
|
+
* tools: [deleteTool, readTool],
|
|
15
|
+
* interventions: [new HumanInTheLoop({ allowedTools: ['readTool'] })],
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Default: agent pauses with stopReason 'interrupt', caller resumes with response
|
|
19
|
+
* const result = await agent.invoke('Delete the file')
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export { HumanInTheLoop } from './hitl.js';
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/vended-interventions/hitl/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.test.d.ts","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/__tests__/handler.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { Agent } from '../../../agent/agent.js';
|
|
3
|
+
import { HookRegistryImplementation } from '../../../hooks/registry.js';
|
|
4
|
+
import { AfterModelCallEvent, BeforeToolCallEvent } from '../../../hooks/events.js';
|
|
5
|
+
import { Interrupt, InterruptState } from '../../../interrupt.js';
|
|
6
|
+
import { confirm, guide } from '../../../interventions/actions.js';
|
|
7
|
+
import { Message, TextBlock } from '../../../types/messages.js';
|
|
8
|
+
import { SteeringHandler } from '../handlers/handler.js';
|
|
9
|
+
function getHookRegistry(agent) {
|
|
10
|
+
return agent._hooksRegistry;
|
|
11
|
+
}
|
|
12
|
+
describe('SteeringHandler', () => {
|
|
13
|
+
const toolUse = { name: 'searchWeb', toolUseId: 'tu-1', input: { q: 'hi' } };
|
|
14
|
+
function makeBeforeToolCallEvent(agent) {
|
|
15
|
+
return new BeforeToolCallEvent({ agent, toolUse, tool: undefined, invocationState: {} });
|
|
16
|
+
}
|
|
17
|
+
function makeAfterModelCallEvent(agent) {
|
|
18
|
+
return new AfterModelCallEvent({
|
|
19
|
+
agent,
|
|
20
|
+
model: {},
|
|
21
|
+
invocationState: {},
|
|
22
|
+
attemptCount: 0,
|
|
23
|
+
stopData: {
|
|
24
|
+
message: new Message({ role: 'assistant', content: [new TextBlock('response')] }),
|
|
25
|
+
stopReason: 'endTurn',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
it('routes beforeToolCall to subclass override with the event', async () => {
|
|
30
|
+
const seen = {};
|
|
31
|
+
class Spy extends SteeringHandler {
|
|
32
|
+
name = 'spy';
|
|
33
|
+
async beforeToolCall(event) {
|
|
34
|
+
seen.agent = event.agent;
|
|
35
|
+
seen.toolUse = event.toolUse;
|
|
36
|
+
return guide('try again');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const agent = new Agent({ interventions: [new Spy()] });
|
|
40
|
+
await agent.initialize();
|
|
41
|
+
const event = makeBeforeToolCallEvent(agent);
|
|
42
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
43
|
+
expect(seen.agent).toBe(agent);
|
|
44
|
+
expect(seen.toolUse).toEqual(toolUse);
|
|
45
|
+
expect(event.cancel).toContain('GUIDANCE:');
|
|
46
|
+
expect(event.cancel).toContain('try again');
|
|
47
|
+
});
|
|
48
|
+
it('routes afterModelCall to subclass override with the event', async () => {
|
|
49
|
+
const seen = {};
|
|
50
|
+
class Spy extends SteeringHandler {
|
|
51
|
+
name = 'spy';
|
|
52
|
+
async afterModelCall(event) {
|
|
53
|
+
if (!event.stopData)
|
|
54
|
+
return { type: 'proceed' };
|
|
55
|
+
seen.message = event.stopData.message;
|
|
56
|
+
seen.stopReason = event.stopData.stopReason;
|
|
57
|
+
return guide('be terser');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const agent = new Agent({ interventions: [new Spy()] });
|
|
61
|
+
await agent.initialize();
|
|
62
|
+
const event = makeAfterModelCallEvent(agent);
|
|
63
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
64
|
+
expect(seen.message).toBeDefined();
|
|
65
|
+
expect(seen.stopReason).toBe('endTurn');
|
|
66
|
+
expect(event.retry).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it('exposes provider context to subclasses via getSteeringContext', async () => {
|
|
69
|
+
const fakeProvider = {
|
|
70
|
+
name: 'fake',
|
|
71
|
+
observeAgent() { },
|
|
72
|
+
get context() {
|
|
73
|
+
return { type: 'fake', tokens: 42 };
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
let observedContext;
|
|
77
|
+
class ContextReader extends SteeringHandler {
|
|
78
|
+
name = 'context-reader';
|
|
79
|
+
async beforeToolCall() {
|
|
80
|
+
observedContext = this.getSteeringContext();
|
|
81
|
+
return { type: 'proceed' };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const agent = new Agent({ interventions: [new ContextReader({ contextProviders: [fakeProvider] })] });
|
|
85
|
+
await agent.initialize();
|
|
86
|
+
await getHookRegistry(agent).invokeCallbacks(makeBeforeToolCallEvent(agent));
|
|
87
|
+
expect(observedContext).toEqual([{ type: 'fake', tokens: 42 }]);
|
|
88
|
+
});
|
|
89
|
+
it('siblings with distinct names can coexist on one agent', () => {
|
|
90
|
+
class A extends SteeringHandler {
|
|
91
|
+
name = 'steer:tool';
|
|
92
|
+
}
|
|
93
|
+
class B extends SteeringHandler {
|
|
94
|
+
name = 'steer:model';
|
|
95
|
+
}
|
|
96
|
+
expect(() => new Agent({ interventions: [new A(), new B()] })).not.toThrow();
|
|
97
|
+
});
|
|
98
|
+
it('does not invoke afterModelCall body when stopData is missing', async () => {
|
|
99
|
+
const called = vi.fn();
|
|
100
|
+
class Spy extends SteeringHandler {
|
|
101
|
+
name = 'spy';
|
|
102
|
+
async afterModelCall(event) {
|
|
103
|
+
if (event.stopData)
|
|
104
|
+
called();
|
|
105
|
+
return { type: 'proceed' };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const agent = new Agent({ interventions: [new Spy()] });
|
|
109
|
+
await agent.initialize();
|
|
110
|
+
const event = new AfterModelCallEvent({
|
|
111
|
+
agent,
|
|
112
|
+
model: {},
|
|
113
|
+
invocationState: {},
|
|
114
|
+
attemptCount: 0,
|
|
115
|
+
});
|
|
116
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
117
|
+
expect(called).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
it('confirm decision flows through the interrupt system on resume (approved)', async () => {
|
|
120
|
+
class Approver extends SteeringHandler {
|
|
121
|
+
name = 'approver';
|
|
122
|
+
async beforeToolCall() {
|
|
123
|
+
return confirm('approve searchWeb?');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const agent = new Agent({ interventions: [new Approver()] });
|
|
127
|
+
await agent.initialize();
|
|
128
|
+
// Preload an approval response so event.interrupt() returns it instead of pausing
|
|
129
|
+
const interruptId = `hook:beforeToolCall:${toolUse.toolUseId}:approver`;
|
|
130
|
+
const interruptState = agent._interruptState;
|
|
131
|
+
interruptState.interrupts[interruptId] = new Interrupt({
|
|
132
|
+
id: interruptId,
|
|
133
|
+
name: 'approver',
|
|
134
|
+
response: 'yes',
|
|
135
|
+
source: 'hook',
|
|
136
|
+
});
|
|
137
|
+
const event = makeBeforeToolCallEvent(agent);
|
|
138
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
139
|
+
expect(event.cancel).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
it('confirm decision sets cancel when human denies', async () => {
|
|
142
|
+
class Approver extends SteeringHandler {
|
|
143
|
+
name = 'approver';
|
|
144
|
+
async beforeToolCall() {
|
|
145
|
+
return confirm('approve searchWeb?');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const agent = new Agent({ interventions: [new Approver()] });
|
|
149
|
+
await agent.initialize();
|
|
150
|
+
const interruptId = `hook:beforeToolCall:${toolUse.toolUseId}:approver`;
|
|
151
|
+
const interruptState = agent._interruptState;
|
|
152
|
+
interruptState.interrupts[interruptId] = new Interrupt({
|
|
153
|
+
id: interruptId,
|
|
154
|
+
name: 'approver',
|
|
155
|
+
response: 'no',
|
|
156
|
+
source: 'hook',
|
|
157
|
+
});
|
|
158
|
+
const event = makeBeforeToolCallEvent(agent);
|
|
159
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
160
|
+
expect(event.cancel).toBe('CONFIRMATION_FAILED: approve searchWeb?');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
//# sourceMappingURL=handler.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.test.js","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/__tests__/handler.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.test.d.ts","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/__tests__/llm.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { Agent } from '../../../agent/agent.js';
|
|
3
|
+
import { BeforeToolCallEvent } from '../../../hooks/events.js';
|
|
4
|
+
import { HookRegistryImplementation } from '../../../hooks/registry.js';
|
|
5
|
+
import { MockMessageModel } from '../../../__fixtures__/mock-message-model.js';
|
|
6
|
+
import { LLMSteeringHandler } from '../handlers/llm.js';
|
|
7
|
+
function getHookRegistry(agent) {
|
|
8
|
+
return agent._hooksRegistry;
|
|
9
|
+
}
|
|
10
|
+
function structuredOutputModel(decision) {
|
|
11
|
+
return new MockMessageModel().addTurn({
|
|
12
|
+
type: 'toolUseBlock',
|
|
13
|
+
name: 'strands_structured_output',
|
|
14
|
+
toolUseId: 'inner-1',
|
|
15
|
+
input: decision,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
describe('LLMSteeringHandler', () => {
|
|
19
|
+
const toolUse = { name: 'searchWeb', toolUseId: 'tu-1', input: { q: 'hi' } };
|
|
20
|
+
it("defaults to the parent agent's model when none is configured", async () => {
|
|
21
|
+
const model = structuredOutputModel({ type: 'proceed', reason: 'no concerning patterns' });
|
|
22
|
+
const streamSpy = vi.spyOn(model, 'stream');
|
|
23
|
+
const handler = new LLMSteeringHandler({
|
|
24
|
+
systemPrompt: 'You are a steering agent.',
|
|
25
|
+
contextProviders: [],
|
|
26
|
+
});
|
|
27
|
+
const agent = new Agent({ model, interventions: [handler] });
|
|
28
|
+
await agent.initialize();
|
|
29
|
+
const event = new BeforeToolCallEvent({ agent, toolUse, tool: undefined, invocationState: {} });
|
|
30
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
31
|
+
expect(streamSpy).toHaveBeenCalledTimes(1);
|
|
32
|
+
expect(event.cancel).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it('uses the configured model in preference to the agent model', async () => {
|
|
35
|
+
const agentModel = new MockMessageModel().addTurn({ type: 'textBlock', text: 'unused' });
|
|
36
|
+
const configuredModel = structuredOutputModel({ type: 'proceed', reason: 'ok' });
|
|
37
|
+
const agentStreamSpy = vi.spyOn(agentModel, 'stream');
|
|
38
|
+
const configuredStreamSpy = vi.spyOn(configuredModel, 'stream');
|
|
39
|
+
const handler = new LLMSteeringHandler({
|
|
40
|
+
systemPrompt: 'You are a steering agent.',
|
|
41
|
+
model: configuredModel,
|
|
42
|
+
contextProviders: [],
|
|
43
|
+
});
|
|
44
|
+
const agent = new Agent({ model: agentModel, interventions: [handler] });
|
|
45
|
+
await agent.initialize();
|
|
46
|
+
const event = new BeforeToolCallEvent({ agent, toolUse, tool: undefined, invocationState: {} });
|
|
47
|
+
await getHookRegistry(agent).invokeCallbacks(event);
|
|
48
|
+
expect(configuredStreamSpy).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(agentStreamSpy).not.toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
it('throws when no model is configured and the handler has no parent agent', async () => {
|
|
52
|
+
const handler = new LLMSteeringHandler({
|
|
53
|
+
systemPrompt: 'You are a steering agent.',
|
|
54
|
+
contextProviders: [],
|
|
55
|
+
});
|
|
56
|
+
// Detached: never attached to an agent, never observed.
|
|
57
|
+
await expect(handler.beforeToolCall({ toolUse })).rejects.toThrow(/no model/i);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=llm.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.test.js","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/__tests__/llm.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAA;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAEvD,SAAS,eAAe,CAAC,KAAY;IACnC,OAAQ,KAAmE,CAAC,cAAc,CAAA;AAC5F,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAmE;IAChG,OAAO,IAAI,gBAAgB,EAAE,CAAC,OAAO,CAAC;QACpC,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,2BAA2B;QACjC,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAA;AACJ,CAAC;AAED,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,CAAA;IAE5E,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC,CAAA;QAC1F,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QAE3C,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC;YACrC,YAAY,EAAE,2BAA2B;YACzC,gBAAgB,EAAE,EAAE;SACrB,CAAC,CAAA;QACF,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC5D,MAAM,KAAK,CAAC,UAAU,EAAE,CAAA;QAExB,MAAM,KAAK,GAAG,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,CAAA;QAC/F,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;QAEnD,MAAM,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAC1C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,UAAU,GAAG,IAAI,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QACxF,MAAM,eAAe,GAAG,qBAAqB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QAChF,MAAM,cAAc,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;QACrD,MAAM,mBAAmB,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;QAE/D,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC;YACrC,YAAY,EAAE,2BAA2B;YACzC,KAAK,EAAE,eAAe;YACtB,gBAAgB,EAAE,EAAE;SACrB,CAAC,CAAA;QACF,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACxE,MAAM,KAAK,CAAC,UAAU,EAAE,CAAA;QAExB,MAAM,KAAK,GAAG,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,CAAA;QAC/F,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;QAEnD,MAAM,CAAC,mBAAmB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QACpD,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC;YACrC,YAAY,EAAE,2BAA2B;YACzC,gBAAgB,EAAE,EAAE;SACrB,CAAC,CAAA;QAEF,wDAAwD;QACxD,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAoC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAClH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-ledger.test.d.ts","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/__tests__/tool-ledger.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { Agent } from '../../../agent/agent.js';
|
|
3
|
+
import { AfterToolCallEvent, BeforeToolCallEvent } from '../../../hooks/events.js';
|
|
4
|
+
import { TextBlock, ToolResultBlock } from '../../../types/messages.js';
|
|
5
|
+
import { ToolLedgerProvider } from '../providers/tool-ledger.js';
|
|
6
|
+
describe('ToolLedgerProvider', () => {
|
|
7
|
+
const toolUse = { name: 'searchWeb', toolUseId: 'tu-1', input: { q: 'hi' } };
|
|
8
|
+
function setupAgent(provider) {
|
|
9
|
+
const agent = new Agent();
|
|
10
|
+
const hookRegistry = agent._hooksRegistry;
|
|
11
|
+
provider.observeAgent(agent);
|
|
12
|
+
return { agent, hookRegistry };
|
|
13
|
+
}
|
|
14
|
+
function makeBefore(agent) {
|
|
15
|
+
return new BeforeToolCallEvent({ agent, toolUse, tool: undefined, invocationState: {} });
|
|
16
|
+
}
|
|
17
|
+
function makeAfter(agent, status = 'success', error) {
|
|
18
|
+
return new AfterToolCallEvent({
|
|
19
|
+
agent,
|
|
20
|
+
toolUse,
|
|
21
|
+
tool: undefined,
|
|
22
|
+
result: new ToolResultBlock({
|
|
23
|
+
toolUseId: toolUse.toolUseId,
|
|
24
|
+
status,
|
|
25
|
+
content: [new TextBlock('result text')],
|
|
26
|
+
...(error !== undefined && { error }),
|
|
27
|
+
}),
|
|
28
|
+
invocationState: {},
|
|
29
|
+
...(error !== undefined && { error }),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
it('records pending entry on beforeToolCall', async () => {
|
|
33
|
+
const provider = new ToolLedgerProvider();
|
|
34
|
+
const { agent, hookRegistry } = setupAgent(provider);
|
|
35
|
+
expect(provider.context.type).toBe('toolLedger');
|
|
36
|
+
expect(provider.context.calls).toEqual([]);
|
|
37
|
+
await hookRegistry.invokeCallbacks(makeBefore(agent));
|
|
38
|
+
const calls = provider.context.calls;
|
|
39
|
+
expect(calls).toHaveLength(1);
|
|
40
|
+
expect(calls[0]).toMatchObject({
|
|
41
|
+
id: 'tu-1',
|
|
42
|
+
name: 'searchWeb',
|
|
43
|
+
args: { q: 'hi' },
|
|
44
|
+
status: 'pending',
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it('flips pending to success after afterToolCall', async () => {
|
|
48
|
+
const provider = new ToolLedgerProvider();
|
|
49
|
+
const { agent, hookRegistry } = setupAgent(provider);
|
|
50
|
+
await hookRegistry.invokeCallbacks(makeBefore(agent));
|
|
51
|
+
await hookRegistry.invokeCallbacks(makeAfter(agent, 'success'));
|
|
52
|
+
const calls = provider.context.calls;
|
|
53
|
+
expect(calls).toHaveLength(1);
|
|
54
|
+
expect(calls[0]).toMatchObject({
|
|
55
|
+
id: 'tu-1',
|
|
56
|
+
name: 'searchWeb',
|
|
57
|
+
args: { q: 'hi' },
|
|
58
|
+
status: 'success',
|
|
59
|
+
error: null,
|
|
60
|
+
endTime: expect.any(String),
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it('records error status and message', async () => {
|
|
64
|
+
const provider = new ToolLedgerProvider();
|
|
65
|
+
const { agent, hookRegistry } = setupAgent(provider);
|
|
66
|
+
await hookRegistry.invokeCallbacks(makeBefore(agent));
|
|
67
|
+
await hookRegistry.invokeCallbacks(makeAfter(agent, 'error', new Error('boom')));
|
|
68
|
+
const calls = provider.context.calls;
|
|
69
|
+
expect(calls[0]).toMatchObject({
|
|
70
|
+
id: 'tu-1',
|
|
71
|
+
name: 'searchWeb',
|
|
72
|
+
args: { q: 'hi' },
|
|
73
|
+
status: 'error',
|
|
74
|
+
error: 'boom',
|
|
75
|
+
endTime: expect.any(String),
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
it('drops oldest entries when ledger exceeds maxEntries', async () => {
|
|
79
|
+
const provider = new ToolLedgerProvider({ maxEntries: 2 });
|
|
80
|
+
const { agent, hookRegistry } = setupAgent(provider);
|
|
81
|
+
for (const id of ['a', 'b', 'c']) {
|
|
82
|
+
await hookRegistry.invokeCallbacks(new BeforeToolCallEvent({
|
|
83
|
+
agent,
|
|
84
|
+
toolUse: { name: 't', toolUseId: id, input: {} },
|
|
85
|
+
tool: undefined,
|
|
86
|
+
invocationState: {},
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
const calls = provider.context.calls;
|
|
90
|
+
expect(calls).toHaveLength(2);
|
|
91
|
+
expect(calls.map((c) => c.id)).toEqual(['b', 'c']);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=tool-ledger.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-ledger.test.js","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/__tests__/tool-ledger.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Steering handler base class for providing contextual guidance to agents.
|
|
3
|
+
*
|
|
4
|
+
* Subclass {@link SteeringHandler} and override {@link beforeToolCall} and/or
|
|
5
|
+
* {@link afterModelCall}. These carry a narrowed steering contract
|
|
6
|
+
* (Proceed | Guide | Confirm for tool calls, Proceed | Guide for model output)
|
|
7
|
+
* — the wider intervention vocabulary (Deny, Transform) is excluded by the
|
|
8
|
+
* return type, so out-of-contract actions are caught at compile time.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* class MySteeringHandler extends SteeringHandler {
|
|
13
|
+
* override readonly name = 'my-steering'
|
|
14
|
+
*
|
|
15
|
+
* override async beforeToolCall(event) {
|
|
16
|
+
* if (event.toolUse.name === 'dangerous_tool') {
|
|
17
|
+
* return guide('This tool requires extra caution.')
|
|
18
|
+
* }
|
|
19
|
+
* return proceed()
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* const agent = new Agent({ tools: [...], interventions: [new MySteeringHandler()] })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import type { AfterModelCallEvent, BeforeToolCallEvent } from '../../../hooks/events.js';
|
|
27
|
+
import { InterventionHandler, type Awaitable } from '../../../interventions/handler.js';
|
|
28
|
+
import type { LifecycleObserver } from '../../../types/lifecycle-observer.js';
|
|
29
|
+
import { type Confirm, type Guide, type Proceed } from '../../../interventions/actions.js';
|
|
30
|
+
import type { LocalAgent } from '../../../types/agent.js';
|
|
31
|
+
import type { SteeringContextData, SteeringContextProvider } from '../providers/context-provider.js';
|
|
32
|
+
/**
|
|
33
|
+
* Configuration shared by all steering handlers.
|
|
34
|
+
*/
|
|
35
|
+
export interface SteeringHandlerConfig {
|
|
36
|
+
/** Providers that supply evaluation context. */
|
|
37
|
+
contextProviders?: SteeringContextProvider[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Base class for steering handlers that provide contextual guidance to agents.
|
|
41
|
+
*
|
|
42
|
+
* Steering handlers accept context providers that observe agent activity, and
|
|
43
|
+
* use the accumulated context to make guidance decisions. The handler is an
|
|
44
|
+
* {@link InterventionHandler} — pass it via `interventions:` on the agent.
|
|
45
|
+
*
|
|
46
|
+
* Subclasses must declare a `name` (inherited as `abstract` from
|
|
47
|
+
* {@link InterventionHandler}). When attaching multiple steering handlers to
|
|
48
|
+
* one agent, ensure their names are distinct — `InterventionRegistry` rejects
|
|
49
|
+
* duplicates.
|
|
50
|
+
*/
|
|
51
|
+
export declare abstract class SteeringHandler extends InterventionHandler implements LifecycleObserver {
|
|
52
|
+
abstract readonly name: string;
|
|
53
|
+
private readonly _contextProviders;
|
|
54
|
+
constructor(config?: SteeringHandlerConfig);
|
|
55
|
+
beforeToolCall(_event: BeforeToolCallEvent): Awaitable<Proceed | Guide | Confirm>;
|
|
56
|
+
afterModelCall(_event: AfterModelCallEvent): Awaitable<Proceed | Guide>;
|
|
57
|
+
observeAgent(agent: LocalAgent): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Collect context from all registered providers. Subclasses (and tests)
|
|
60
|
+
* may call this to inspect the accumulated provider snapshots.
|
|
61
|
+
*/
|
|
62
|
+
getSteeringContext(): SteeringContextData[];
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/handlers/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AACxF,OAAO,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAA;AACvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAW,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM,mCAAmC,CAAA;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAA;AAEpG;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,uBAAuB,EAAE,CAAA;CAC7C;AAED;;;;;;;;;;;GAWG;AACH,8BAAsB,eAAgB,SAAQ,mBAAoB,YAAW,iBAAiB;IAC5F,kBAA2B,IAAI,EAAE,MAAM,CAAA;IAEvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA2B;gBAEjD,MAAM,CAAC,EAAE,qBAAqB;IASjC,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAIjF,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC,OAAO,GAAG,KAAK,CAAC;IAQ1E,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD;;;OAGG;IACH,kBAAkB,IAAI,mBAAmB,EAAE;CAG5C"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Steering handler base class for providing contextual guidance to agents.
|
|
3
|
+
*
|
|
4
|
+
* Subclass {@link SteeringHandler} and override {@link beforeToolCall} and/or
|
|
5
|
+
* {@link afterModelCall}. These carry a narrowed steering contract
|
|
6
|
+
* (Proceed | Guide | Confirm for tool calls, Proceed | Guide for model output)
|
|
7
|
+
* — the wider intervention vocabulary (Deny, Transform) is excluded by the
|
|
8
|
+
* return type, so out-of-contract actions are caught at compile time.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* class MySteeringHandler extends SteeringHandler {
|
|
13
|
+
* override readonly name = 'my-steering'
|
|
14
|
+
*
|
|
15
|
+
* override async beforeToolCall(event) {
|
|
16
|
+
* if (event.toolUse.name === 'dangerous_tool') {
|
|
17
|
+
* return guide('This tool requires extra caution.')
|
|
18
|
+
* }
|
|
19
|
+
* return proceed()
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* const agent = new Agent({ tools: [...], interventions: [new MySteeringHandler()] })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { InterventionHandler } from '../../../interventions/handler.js';
|
|
27
|
+
import { proceed } from '../../../interventions/actions.js';
|
|
28
|
+
/**
|
|
29
|
+
* Base class for steering handlers that provide contextual guidance to agents.
|
|
30
|
+
*
|
|
31
|
+
* Steering handlers accept context providers that observe agent activity, and
|
|
32
|
+
* use the accumulated context to make guidance decisions. The handler is an
|
|
33
|
+
* {@link InterventionHandler} — pass it via `interventions:` on the agent.
|
|
34
|
+
*
|
|
35
|
+
* Subclasses must declare a `name` (inherited as `abstract` from
|
|
36
|
+
* {@link InterventionHandler}). When attaching multiple steering handlers to
|
|
37
|
+
* one agent, ensure their names are distinct — `InterventionRegistry` rejects
|
|
38
|
+
* duplicates.
|
|
39
|
+
*/
|
|
40
|
+
export class SteeringHandler extends InterventionHandler {
|
|
41
|
+
_contextProviders;
|
|
42
|
+
constructor(config) {
|
|
43
|
+
super();
|
|
44
|
+
this._contextProviders = config?.contextProviders ?? [];
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Steering moments — narrowed return types reject out-of-contract actions.
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
beforeToolCall(_event) {
|
|
50
|
+
return proceed();
|
|
51
|
+
}
|
|
52
|
+
afterModelCall(_event) {
|
|
53
|
+
return proceed();
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Lifecycle observer — forward to providers so they can self-register hooks.
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
async observeAgent(agent) {
|
|
59
|
+
for (const provider of this._contextProviders) {
|
|
60
|
+
await provider.observeAgent(agent);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Collect context from all registered providers. Subclasses (and tests)
|
|
65
|
+
* may call this to inspect the accumulated provider snapshots.
|
|
66
|
+
*/
|
|
67
|
+
getSteeringContext() {
|
|
68
|
+
return this._contextProviders.map((provider) => provider.context);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../../src/vended-interventions/steering/handlers/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,mBAAmB,EAAkB,MAAM,mCAAmC,CAAA;AAEvF,OAAO,EAAE,OAAO,EAA0C,MAAM,mCAAmC,CAAA;AAYnG;;;;;;;;;;;GAWG;AACH,MAAM,OAAgB,eAAgB,SAAQ,mBAAmB;IAG9C,iBAAiB,CAA2B;IAE7D,YAAY,MAA8B;QACxC,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,iBAAiB,GAAG,MAAM,EAAE,gBAAgB,IAAI,EAAE,CAAA;IACzD,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,8EAA8E;IAErE,cAAc,CAAC,MAA2B;QACjD,OAAO,OAAO,EAAE,CAAA;IAClB,CAAC;IAEQ,cAAc,CAAC,MAA2B;QACjD,OAAO,OAAO,EAAE,CAAA;IAClB,CAAC;IAED,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAE9E,KAAK,CAAC,YAAY,CAAC,KAAiB;QAClC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC9C,MAAM,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,kBAAkB;QAChB,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;IACnE,CAAC;CACF"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-based steering handler that uses an LLM to provide contextual guidance.
|
|
3
|
+
*/
|
|
4
|
+
import { type Confirm, type Guide, type Proceed } from '../../../interventions/actions.js';
|
|
5
|
+
import type { Model } from '../../../models/model.js';
|
|
6
|
+
import type { ContentBlock, SystemPrompt } from '../../../types/messages.js';
|
|
7
|
+
import type { ToolUse } from '../../../tools/types.js';
|
|
8
|
+
import type { BeforeToolCallEvent } from '../../../hooks/events.js';
|
|
9
|
+
import type { LocalAgent } from '../../../types/agent.js';
|
|
10
|
+
import type { SteeringContextData, SteeringContextProvider } from '../providers/context-provider.js';
|
|
11
|
+
import { SteeringHandler } from './handler.js';
|
|
12
|
+
/**
|
|
13
|
+
* Builds the evaluation prompt sent to the steering LLM.
|
|
14
|
+
* Return a string for simple prompts, or ContentBlock[] to use cache points.
|
|
15
|
+
*/
|
|
16
|
+
export type PromptBuilder = (context: SteeringContextData[], toolUse?: ToolUse) => string | ContentBlock[];
|
|
17
|
+
/**
|
|
18
|
+
* Configuration for the LLMSteeringHandler.
|
|
19
|
+
*/
|
|
20
|
+
export interface LLMSteeringHandlerConfig {
|
|
21
|
+
/** System prompt defining the steering guidance rules. */
|
|
22
|
+
systemPrompt: SystemPrompt;
|
|
23
|
+
/** Model for steering evaluation. Defaults to the parent agent's model. */
|
|
24
|
+
model?: Model;
|
|
25
|
+
/** Custom prompt builder for evaluation prompts. Defaults to defaultPromptBuilder. */
|
|
26
|
+
promptBuilder?: PromptBuilder;
|
|
27
|
+
/**
|
|
28
|
+
* Context providers for populating steering context.
|
|
29
|
+
* Defaults to [new ToolLedgerProvider()] if undefined. Pass an empty array to disable.
|
|
30
|
+
*/
|
|
31
|
+
contextProviders?: SteeringContextProvider[];
|
|
32
|
+
/**
|
|
33
|
+
* Identifier for this handler instance. Defaults to `'strands:llm-steering-handler'`.
|
|
34
|
+
* Override when attaching multiple LLM steering handlers to the same agent.
|
|
35
|
+
*/
|
|
36
|
+
name?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Steering handler that uses an LLM to provide contextual guidance.
|
|
40
|
+
*
|
|
41
|
+
* Uses natural language prompts to evaluate tool calls and produce an
|
|
42
|
+
* intervention action.
|
|
43
|
+
*
|
|
44
|
+
* Only `beforeToolCall` is implemented — model-output steering is not
|
|
45
|
+
* delegated to the LLM. Subclass and override `afterModelCall` (which
|
|
46
|
+
* carries the narrowed `Proceed | Guide` return) to add LLM-driven
|
|
47
|
+
* evaluation of model responses.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* import { Agent } from '@strands-agents/sdk'
|
|
52
|
+
* import { LLMSteeringHandler } from '@strands-agents/sdk/vended-interventions/steering'
|
|
53
|
+
*
|
|
54
|
+
* const handler = new LLMSteeringHandler({
|
|
55
|
+
* systemPrompt: `You ensure emails maintain a cheerful, positive tone.`,
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* const agent = new Agent({ tools: [sendEmail], interventions: [handler] })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare class LLMSteeringHandler extends SteeringHandler {
|
|
62
|
+
readonly name: string;
|
|
63
|
+
private readonly _promptBuilder;
|
|
64
|
+
private readonly _configuredModel;
|
|
65
|
+
private _agentModel;
|
|
66
|
+
private readonly _systemPrompt;
|
|
67
|
+
constructor(config: LLMSteeringHandlerConfig);
|
|
68
|
+
observeAgent(agent: LocalAgent): Promise<void>;
|
|
69
|
+
beforeToolCall(event: BeforeToolCallEvent): Promise<Proceed | Guide | Confirm>;
|
|
70
|
+
private _invoke;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=llm.d.ts.map
|