@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,358 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { HumanInTheLoop } from '../hitl.js';
|
|
3
|
+
import { Agent } from '../../../agent/agent.js';
|
|
4
|
+
import { MockMessageModel } from '../../../__fixtures__/mock-message-model.js';
|
|
5
|
+
import { createMockTool } from '../../../__fixtures__/tool-helpers.js';
|
|
6
|
+
describe('HumanInTheLoop', () => {
|
|
7
|
+
describe('default config (interrupt/resume)', () => {
|
|
8
|
+
it('pauses agent with interrupt on any tool call', async () => {
|
|
9
|
+
const model = new MockMessageModel()
|
|
10
|
+
.addTurn({ type: 'toolUseBlock', name: 'anyTool', toolUseId: 'tool-1', input: { x: 1 } })
|
|
11
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
12
|
+
let toolExecuted = false;
|
|
13
|
+
const tool = createMockTool('anyTool', () => {
|
|
14
|
+
toolExecuted = true;
|
|
15
|
+
return 'result';
|
|
16
|
+
});
|
|
17
|
+
const agent = new Agent({
|
|
18
|
+
model,
|
|
19
|
+
tools: [tool],
|
|
20
|
+
interventions: [new HumanInTheLoop()],
|
|
21
|
+
printer: false,
|
|
22
|
+
});
|
|
23
|
+
const result = await agent.invoke('Do something');
|
|
24
|
+
expect(result.stopReason).toBe('interrupt');
|
|
25
|
+
expect(result.interrupts).toEqual([
|
|
26
|
+
expect.objectContaining({
|
|
27
|
+
name: 'strands:human-in-the-loop',
|
|
28
|
+
reason: expect.stringContaining('anyTool'),
|
|
29
|
+
}),
|
|
30
|
+
]);
|
|
31
|
+
expect(toolExecuted).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('inline mode (with ask callback)', () => {
|
|
35
|
+
it('allows tool execution when approved', async () => {
|
|
36
|
+
const model = new MockMessageModel()
|
|
37
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
38
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
39
|
+
let toolExecuted = false;
|
|
40
|
+
const tool = createMockTool('myTool', () => {
|
|
41
|
+
toolExecuted = true;
|
|
42
|
+
return 'executed';
|
|
43
|
+
});
|
|
44
|
+
const agent = new Agent({
|
|
45
|
+
model,
|
|
46
|
+
tools: [tool],
|
|
47
|
+
interventions: [new HumanInTheLoop({ ask: async () => 'yes' })],
|
|
48
|
+
printer: false,
|
|
49
|
+
});
|
|
50
|
+
const result = await agent.invoke('Run tool');
|
|
51
|
+
expect(result.stopReason).toBe('endTurn');
|
|
52
|
+
expect(toolExecuted).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('denies tool execution when rejected', async () => {
|
|
55
|
+
const model = new MockMessageModel()
|
|
56
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
57
|
+
.addTurn({ type: 'textBlock', text: 'Understood' });
|
|
58
|
+
let toolExecuted = false;
|
|
59
|
+
const tool = createMockTool('myTool', () => {
|
|
60
|
+
toolExecuted = true;
|
|
61
|
+
return 'executed';
|
|
62
|
+
});
|
|
63
|
+
const agent = new Agent({
|
|
64
|
+
model,
|
|
65
|
+
tools: [tool],
|
|
66
|
+
interventions: [new HumanInTheLoop({ ask: async () => 'no' })],
|
|
67
|
+
printer: false,
|
|
68
|
+
});
|
|
69
|
+
const result = await agent.invoke('Run tool');
|
|
70
|
+
expect(result.stopReason).toBe('endTurn');
|
|
71
|
+
expect(toolExecuted).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('allowedTools config', () => {
|
|
75
|
+
it('does not prompt for tools in allowedTools', async () => {
|
|
76
|
+
const model = new MockMessageModel()
|
|
77
|
+
.addTurn({ type: 'toolUseBlock', name: 'readFile', toolUseId: 'tool-1', input: {} })
|
|
78
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
79
|
+
let toolExecuted = false;
|
|
80
|
+
const tool = createMockTool('readFile', () => {
|
|
81
|
+
toolExecuted = true;
|
|
82
|
+
return 'content';
|
|
83
|
+
});
|
|
84
|
+
const agent = new Agent({
|
|
85
|
+
model,
|
|
86
|
+
tools: [tool],
|
|
87
|
+
interventions: [new HumanInTheLoop({ allowedTools: ['readFile'], ask: async () => 'no' })],
|
|
88
|
+
printer: false,
|
|
89
|
+
});
|
|
90
|
+
const result = await agent.invoke('Read it');
|
|
91
|
+
expect(result.stopReason).toBe('endTurn');
|
|
92
|
+
expect(toolExecuted).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it('prompts for tools not in allowedTools', async () => {
|
|
95
|
+
const model = new MockMessageModel()
|
|
96
|
+
.addTurn({ type: 'toolUseBlock', name: 'deleteFile', toolUseId: 'tool-1', input: {} })
|
|
97
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
98
|
+
let toolExecuted = false;
|
|
99
|
+
const tool = createMockTool('deleteFile', () => {
|
|
100
|
+
toolExecuted = true;
|
|
101
|
+
return 'deleted';
|
|
102
|
+
});
|
|
103
|
+
const agent = new Agent({
|
|
104
|
+
model,
|
|
105
|
+
tools: [tool],
|
|
106
|
+
interventions: [new HumanInTheLoop({ allowedTools: ['readFile'], ask: async () => 'no' })],
|
|
107
|
+
printer: false,
|
|
108
|
+
});
|
|
109
|
+
await agent.invoke('Delete it');
|
|
110
|
+
expect(toolExecuted).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
it('allows all tools except negated ones with "!" prefix', async () => {
|
|
113
|
+
const model = new MockMessageModel()
|
|
114
|
+
.addTurn([
|
|
115
|
+
{ type: 'toolUseBlock', name: 'readFile', toolUseId: 'tool-1', input: {} },
|
|
116
|
+
{ type: 'toolUseBlock', name: 'deleteFile', toolUseId: 'tool-2', input: {} },
|
|
117
|
+
])
|
|
118
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
119
|
+
const execLog = [];
|
|
120
|
+
const readTool = createMockTool('readFile', () => {
|
|
121
|
+
execLog.push('read');
|
|
122
|
+
return 'content';
|
|
123
|
+
});
|
|
124
|
+
const deleteTool = createMockTool('deleteFile', () => {
|
|
125
|
+
execLog.push('delete');
|
|
126
|
+
return 'deleted';
|
|
127
|
+
});
|
|
128
|
+
const agent = new Agent({
|
|
129
|
+
model,
|
|
130
|
+
tools: [readTool, deleteTool],
|
|
131
|
+
interventions: [new HumanInTheLoop({ allowedTools: ['*', '!deleteFile'], ask: async () => 'no' })],
|
|
132
|
+
printer: false,
|
|
133
|
+
});
|
|
134
|
+
await agent.invoke('Do both');
|
|
135
|
+
expect(execLog).toContain('read');
|
|
136
|
+
expect(execLog).not.toContain('delete');
|
|
137
|
+
});
|
|
138
|
+
it('allows all tools with wildcard "*"', async () => {
|
|
139
|
+
const model = new MockMessageModel()
|
|
140
|
+
.addTurn({ type: 'toolUseBlock', name: 'dangerousTool', toolUseId: 'tool-1', input: {} })
|
|
141
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
142
|
+
let toolExecuted = false;
|
|
143
|
+
const tool = createMockTool('dangerousTool', () => {
|
|
144
|
+
toolExecuted = true;
|
|
145
|
+
return 'ran';
|
|
146
|
+
});
|
|
147
|
+
const agent = new Agent({
|
|
148
|
+
model,
|
|
149
|
+
tools: [tool],
|
|
150
|
+
interventions: [new HumanInTheLoop({ allowedTools: ['*'], ask: async () => 'no' })],
|
|
151
|
+
printer: false,
|
|
152
|
+
});
|
|
153
|
+
const result = await agent.invoke('Do it');
|
|
154
|
+
expect(result.stopReason).toBe('endTurn');
|
|
155
|
+
expect(toolExecuted).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('ask callback', () => {
|
|
159
|
+
it('passes tool name and input in the prompt', async () => {
|
|
160
|
+
const prompts = [];
|
|
161
|
+
const model = new MockMessageModel()
|
|
162
|
+
.addTurn({ type: 'toolUseBlock', name: 'sendEmail', toolUseId: 'tool-1', input: { to: 'bob@example.com' } })
|
|
163
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
164
|
+
const tool = createMockTool('sendEmail', () => 'sent');
|
|
165
|
+
const agent = new Agent({
|
|
166
|
+
model,
|
|
167
|
+
tools: [tool],
|
|
168
|
+
interventions: [
|
|
169
|
+
new HumanInTheLoop({
|
|
170
|
+
ask: async (prompt) => {
|
|
171
|
+
prompts.push(prompt);
|
|
172
|
+
return 'yes';
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
],
|
|
176
|
+
printer: false,
|
|
177
|
+
});
|
|
178
|
+
await agent.invoke('Send email');
|
|
179
|
+
expect(prompts[0]).toContain('sendEmail');
|
|
180
|
+
expect(prompts[0]).toContain('bob@example.com');
|
|
181
|
+
});
|
|
182
|
+
it('supports custom evaluate function', async () => {
|
|
183
|
+
const model = new MockMessageModel()
|
|
184
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
185
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
186
|
+
let toolExecuted = false;
|
|
187
|
+
const tool = createMockTool('myTool', () => {
|
|
188
|
+
toolExecuted = true;
|
|
189
|
+
return 'executed';
|
|
190
|
+
});
|
|
191
|
+
const agent = new Agent({
|
|
192
|
+
model,
|
|
193
|
+
tools: [tool],
|
|
194
|
+
interventions: [
|
|
195
|
+
new HumanInTheLoop({
|
|
196
|
+
ask: async () => 'magic-word',
|
|
197
|
+
evaluate: (response) => response === 'magic-word',
|
|
198
|
+
}),
|
|
199
|
+
],
|
|
200
|
+
printer: false,
|
|
201
|
+
});
|
|
202
|
+
const result = await agent.invoke('Go');
|
|
203
|
+
expect(result.stopReason).toBe('endTurn');
|
|
204
|
+
expect(toolExecuted).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('trust mode (enableTrust: true)', () => {
|
|
208
|
+
it('trusts a tool for the session when response is "t"', async () => {
|
|
209
|
+
let askCount = 0;
|
|
210
|
+
const model = new MockMessageModel()
|
|
211
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
212
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-2', input: {} })
|
|
213
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
214
|
+
const tool = createMockTool('myTool', () => 'executed');
|
|
215
|
+
const agent = new Agent({
|
|
216
|
+
model,
|
|
217
|
+
tools: [tool],
|
|
218
|
+
interventions: [
|
|
219
|
+
new HumanInTheLoop({
|
|
220
|
+
enableTrust: true,
|
|
221
|
+
ask: async () => {
|
|
222
|
+
askCount++;
|
|
223
|
+
return 't';
|
|
224
|
+
},
|
|
225
|
+
}),
|
|
226
|
+
],
|
|
227
|
+
printer: false,
|
|
228
|
+
});
|
|
229
|
+
await agent.invoke('Run tool twice');
|
|
230
|
+
expect(askCount).toBe(1);
|
|
231
|
+
});
|
|
232
|
+
it('does not trust when enableTrust is false even with "t" response', async () => {
|
|
233
|
+
let askCount = 0;
|
|
234
|
+
const model = new MockMessageModel()
|
|
235
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
236
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-2', input: {} })
|
|
237
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
238
|
+
const tool = createMockTool('myTool', () => 'executed');
|
|
239
|
+
const agent = new Agent({
|
|
240
|
+
model,
|
|
241
|
+
tools: [tool],
|
|
242
|
+
interventions: [
|
|
243
|
+
new HumanInTheLoop({
|
|
244
|
+
enableTrust: false,
|
|
245
|
+
ask: async () => {
|
|
246
|
+
askCount++;
|
|
247
|
+
return 't';
|
|
248
|
+
},
|
|
249
|
+
}),
|
|
250
|
+
],
|
|
251
|
+
printer: false,
|
|
252
|
+
});
|
|
253
|
+
await agent.invoke('Run tool twice');
|
|
254
|
+
// 't' is not recognized as approval when trust is disabled, so tool is denied both times
|
|
255
|
+
// but ask is still called both times (no trust memory)
|
|
256
|
+
expect(askCount).toBe(2);
|
|
257
|
+
});
|
|
258
|
+
it('"t" response also approves the current tool call', async () => {
|
|
259
|
+
const model = new MockMessageModel()
|
|
260
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
261
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
262
|
+
let toolExecuted = false;
|
|
263
|
+
const tool = createMockTool('myTool', () => {
|
|
264
|
+
toolExecuted = true;
|
|
265
|
+
return 'executed';
|
|
266
|
+
});
|
|
267
|
+
const agent = new Agent({
|
|
268
|
+
model,
|
|
269
|
+
tools: [tool],
|
|
270
|
+
interventions: [new HumanInTheLoop({ enableTrust: true, ask: async () => 't' })],
|
|
271
|
+
printer: false,
|
|
272
|
+
});
|
|
273
|
+
const result = await agent.invoke('Run tool');
|
|
274
|
+
expect(result.stopReason).toBe('endTurn');
|
|
275
|
+
expect(toolExecuted).toBe(true);
|
|
276
|
+
});
|
|
277
|
+
it.each(['trust', 'T', 'TRUST'])('trusts when response is "%s"', async (trustResponse) => {
|
|
278
|
+
let askCount = 0;
|
|
279
|
+
const model = new MockMessageModel()
|
|
280
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
281
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-2', input: {} })
|
|
282
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
283
|
+
const tool = createMockTool('myTool', () => 'executed');
|
|
284
|
+
const agent = new Agent({
|
|
285
|
+
model,
|
|
286
|
+
tools: [tool],
|
|
287
|
+
interventions: [
|
|
288
|
+
new HumanInTheLoop({
|
|
289
|
+
enableTrust: true,
|
|
290
|
+
ask: async () => {
|
|
291
|
+
askCount++;
|
|
292
|
+
return trustResponse;
|
|
293
|
+
},
|
|
294
|
+
}),
|
|
295
|
+
],
|
|
296
|
+
printer: false,
|
|
297
|
+
});
|
|
298
|
+
await agent.invoke('Run tool twice');
|
|
299
|
+
expect(askCount).toBe(1);
|
|
300
|
+
});
|
|
301
|
+
it('supports custom evaluateTrust function', async () => {
|
|
302
|
+
let askCount = 0;
|
|
303
|
+
const model = new MockMessageModel()
|
|
304
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-1', input: {} })
|
|
305
|
+
.addTurn({ type: 'toolUseBlock', name: 'myTool', toolUseId: 'tool-2', input: {} })
|
|
306
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
307
|
+
const tool = createMockTool('myTool', () => 'executed');
|
|
308
|
+
const agent = new Agent({
|
|
309
|
+
model,
|
|
310
|
+
tools: [tool],
|
|
311
|
+
interventions: [
|
|
312
|
+
new HumanInTheLoop({
|
|
313
|
+
enableTrust: true,
|
|
314
|
+
evaluateTrust: (r) => r === 'approve-and-remember',
|
|
315
|
+
ask: async () => {
|
|
316
|
+
askCount++;
|
|
317
|
+
return 'approve-and-remember';
|
|
318
|
+
},
|
|
319
|
+
}),
|
|
320
|
+
],
|
|
321
|
+
printer: false,
|
|
322
|
+
});
|
|
323
|
+
await agent.invoke('Run tool twice');
|
|
324
|
+
expect(askCount).toBe(1);
|
|
325
|
+
});
|
|
326
|
+
it('negated tools cannot be trusted even with "t" response', async () => {
|
|
327
|
+
let askCount = 0;
|
|
328
|
+
let toolExecuted = false;
|
|
329
|
+
const model = new MockMessageModel()
|
|
330
|
+
.addTurn({ type: 'toolUseBlock', name: 'dangerTool', toolUseId: 'tool-1', input: {} })
|
|
331
|
+
.addTurn({ type: 'toolUseBlock', name: 'dangerTool', toolUseId: 'tool-2', input: {} })
|
|
332
|
+
.addTurn({ type: 'textBlock', text: 'Done' });
|
|
333
|
+
const tool = createMockTool('dangerTool', () => {
|
|
334
|
+
toolExecuted = true;
|
|
335
|
+
return 'ran';
|
|
336
|
+
});
|
|
337
|
+
const agent = new Agent({
|
|
338
|
+
model,
|
|
339
|
+
tools: [tool],
|
|
340
|
+
interventions: [
|
|
341
|
+
new HumanInTheLoop({
|
|
342
|
+
allowedTools: ['*', '!dangerTool'],
|
|
343
|
+
enableTrust: true,
|
|
344
|
+
ask: async () => {
|
|
345
|
+
askCount++;
|
|
346
|
+
return 't';
|
|
347
|
+
},
|
|
348
|
+
}),
|
|
349
|
+
],
|
|
350
|
+
printer: false,
|
|
351
|
+
});
|
|
352
|
+
await agent.invoke('Run danger twice');
|
|
353
|
+
expect(askCount).toBe(2);
|
|
354
|
+
expect(toolExecuted).toBe(false);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
//# sourceMappingURL=hitl.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hitl.test.js","sourceRoot":"","sources":["../../../../../src/vended-interventions/hitl/__tests__/hitl.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { InterventionHandler } from '../../interventions/handler.js';
|
|
2
|
+
import type { InterventionAction } from '../../interventions/actions.js';
|
|
3
|
+
import type { BeforeToolCallEvent } from '../../hooks/events.js';
|
|
4
|
+
import type { JSONValue } from '../../types/json.js';
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for the {@link HumanInTheLoop} intervention handler.
|
|
7
|
+
*/
|
|
8
|
+
export interface HumanInTheLoopConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Tools that can execute WITHOUT human approval. All other tools require approval.
|
|
11
|
+
*
|
|
12
|
+
* - Use `'*'` to allow all tools.
|
|
13
|
+
* - Prefix with `!` to exclude specific tools from `'*'` (they still require approval).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Only readFile and listDir run freely; everything else needs approval
|
|
18
|
+
* { allowedTools: ['readFile', 'listDir'] }
|
|
19
|
+
*
|
|
20
|
+
* // All tools run freely (HITL disabled)
|
|
21
|
+
* { allowedTools: ['*'] }
|
|
22
|
+
*
|
|
23
|
+
* // All tools run freely EXCEPT deleteFile and sendEmail
|
|
24
|
+
* { allowedTools: ['*', '!deleteFile', '!sendEmail'] }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
allowedTools?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* When true, trust responses approve the tool AND remember it
|
|
30
|
+
* in `agent.appState` for the rest of the session (won't ask again).
|
|
31
|
+
* Works in both interrupt/resume and inline `ask` modes.
|
|
32
|
+
*
|
|
33
|
+
* Negated tools (`!tool`) cannot be trusted.
|
|
34
|
+
*
|
|
35
|
+
* Defaults to `false`.
|
|
36
|
+
*/
|
|
37
|
+
enableTrust?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Custom trust response validator. Defaults to accepting `'t'`/`'trust'` (case-insensitive).
|
|
40
|
+
* When this returns true, the tool is approved AND trusted for the session.
|
|
41
|
+
*
|
|
42
|
+
* Only evaluated when `enableTrust` is true.
|
|
43
|
+
*/
|
|
44
|
+
evaluateTrust?: (response: JSONValue) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Custom approval response validator. Defaults to accepting `true`, `'y'`/`'yes'` (case-insensitive).
|
|
47
|
+
*/
|
|
48
|
+
evaluate?: (response: JSONValue) => boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Controls how the human's response is collected.
|
|
51
|
+
*
|
|
52
|
+
* - **Default** (omitted): uses interrupt/resume — agent pauses, caller resumes with response.
|
|
53
|
+
* - **`'stdio'`**: prompts via CLI readline (Node.js only). Agent blocks inline until human responds.
|
|
54
|
+
* - **Custom function**: your own async prompt logic (Slack, web UI, etc.). Agent blocks inline.
|
|
55
|
+
*/
|
|
56
|
+
ask?: ((prompt: string) => Promise<JSONValue>) | 'stdio';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Human-in-the-loop intervention handler that pauses agent execution
|
|
60
|
+
* before tool calls to request human approval.
|
|
61
|
+
*
|
|
62
|
+
* By default, ALL tools require approval and the agent pauses via interrupt/resume.
|
|
63
|
+
* Use `allowedTools` to whitelist tools that run freely, and `ask` to provide
|
|
64
|
+
* inline prompting (CLI, custom UI).
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* import { Agent } from '@strands-agents/sdk'
|
|
69
|
+
* import { HumanInTheLoop } from '@strands-agents/sdk/vended-interventions/hitl'
|
|
70
|
+
*
|
|
71
|
+
* // All tools require approval, agent pauses via interrupt (default)
|
|
72
|
+
* const agent = new Agent({
|
|
73
|
+
* interventions: [new HumanInTheLoop()],
|
|
74
|
+
* })
|
|
75
|
+
*
|
|
76
|
+
* // readFile runs freely, everything else pauses for approval
|
|
77
|
+
* const agent = new Agent({
|
|
78
|
+
* interventions: [new HumanInTheLoop({ allowedTools: ['readFile'] })],
|
|
79
|
+
* })
|
|
80
|
+
*
|
|
81
|
+
* // CLI mode — prompts in terminal inline
|
|
82
|
+
* const agent = new Agent({
|
|
83
|
+
* interventions: [new HumanInTheLoop({ ask: 'stdio' })],
|
|
84
|
+
* })
|
|
85
|
+
*
|
|
86
|
+
* // Custom UI — provide your own prompt function
|
|
87
|
+
* const agent = new Agent({
|
|
88
|
+
* interventions: [new HumanInTheLoop({
|
|
89
|
+
* ask: async (prompt) => await slackDM(userId, prompt),
|
|
90
|
+
* })],
|
|
91
|
+
* })
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare class HumanInTheLoop extends InterventionHandler {
|
|
95
|
+
readonly name = "strands:human-in-the-loop";
|
|
96
|
+
private readonly _allowedTools;
|
|
97
|
+
private readonly _enableTrust;
|
|
98
|
+
private readonly _evaluateTrust;
|
|
99
|
+
private readonly _evaluate;
|
|
100
|
+
private readonly _ask;
|
|
101
|
+
constructor(config?: HumanInTheLoopConfig);
|
|
102
|
+
beforeToolCall(event: BeforeToolCallEvent): Promise<InterventionAction>;
|
|
103
|
+
/**
|
|
104
|
+
* Precedence (first match wins):
|
|
105
|
+
* 1. Negated (`!tool`) → always requires approval (cannot be trusted)
|
|
106
|
+
* 2. Trusted at runtime via 't' response (stored in agent.appState) → runs freely
|
|
107
|
+
* 3. Wildcard (`*`) → runs freely
|
|
108
|
+
* 4. Explicitly listed → runs freely
|
|
109
|
+
* 5. Default → requires approval
|
|
110
|
+
*/
|
|
111
|
+
private _requiresApproval;
|
|
112
|
+
private _trustTool;
|
|
113
|
+
private _isTrustResponse;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=hitl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hitl.d.ts","sourceRoot":"","sources":["../../../../src/vended-interventions/hitl/hitl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AAEpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AA6BpD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IAEvB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAA;IAEhD;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAA;IAE3C;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,OAAO,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,cAAe,SAAQ,mBAAmB;IACrD,QAAQ,CAAC,IAAI,+BAA8B;IAE3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkC;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgD;IAC1E,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsD;gBAE/D,MAAM,CAAC,EAAE,oBAAoB;IAS1B,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAmCtF;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,gBAAgB;CAIzB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { InterventionHandler } from '../../interventions/handler.js';
|
|
2
|
+
import { confirm, proceed, defaultEvaluate } from '../../interventions/actions.js';
|
|
3
|
+
const TRUST_RESPONSES = new Set(['t', 'trust']);
|
|
4
|
+
const TRUSTED_TOOLS_KEY = 'hitl:trustedTools';
|
|
5
|
+
/**
|
|
6
|
+
* CLI prompt that reads from stdin.
|
|
7
|
+
* Serializes prompts so concurrent tool calls don't collide on stdin.
|
|
8
|
+
*/
|
|
9
|
+
function createStdioAsk(includeTrust) {
|
|
10
|
+
const options = includeTrust ? '(y/n/t)' : '(y/n)';
|
|
11
|
+
let queue = Promise.resolve();
|
|
12
|
+
return (prompt) => {
|
|
13
|
+
const task = queue.then(async () => {
|
|
14
|
+
const { createInterface } = await import('node:readline');
|
|
15
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
rl.question(`${prompt} ${options}: `, (answer) => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(answer.trim());
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
queue = task.catch(() => { });
|
|
24
|
+
return task;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Human-in-the-loop intervention handler that pauses agent execution
|
|
29
|
+
* before tool calls to request human approval.
|
|
30
|
+
*
|
|
31
|
+
* By default, ALL tools require approval and the agent pauses via interrupt/resume.
|
|
32
|
+
* Use `allowedTools` to whitelist tools that run freely, and `ask` to provide
|
|
33
|
+
* inline prompting (CLI, custom UI).
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* import { Agent } from '@strands-agents/sdk'
|
|
38
|
+
* import { HumanInTheLoop } from '@strands-agents/sdk/vended-interventions/hitl'
|
|
39
|
+
*
|
|
40
|
+
* // All tools require approval, agent pauses via interrupt (default)
|
|
41
|
+
* const agent = new Agent({
|
|
42
|
+
* interventions: [new HumanInTheLoop()],
|
|
43
|
+
* })
|
|
44
|
+
*
|
|
45
|
+
* // readFile runs freely, everything else pauses for approval
|
|
46
|
+
* const agent = new Agent({
|
|
47
|
+
* interventions: [new HumanInTheLoop({ allowedTools: ['readFile'] })],
|
|
48
|
+
* })
|
|
49
|
+
*
|
|
50
|
+
* // CLI mode — prompts in terminal inline
|
|
51
|
+
* const agent = new Agent({
|
|
52
|
+
* interventions: [new HumanInTheLoop({ ask: 'stdio' })],
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* // Custom UI — provide your own prompt function
|
|
56
|
+
* const agent = new Agent({
|
|
57
|
+
* interventions: [new HumanInTheLoop({
|
|
58
|
+
* ask: async (prompt) => await slackDM(userId, prompt),
|
|
59
|
+
* })],
|
|
60
|
+
* })
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export class HumanInTheLoop extends InterventionHandler {
|
|
64
|
+
name = 'strands:human-in-the-loop';
|
|
65
|
+
_allowedTools;
|
|
66
|
+
_enableTrust;
|
|
67
|
+
_evaluateTrust;
|
|
68
|
+
_evaluate;
|
|
69
|
+
_ask;
|
|
70
|
+
constructor(config) {
|
|
71
|
+
super();
|
|
72
|
+
this._allowedTools = new Set(config?.allowedTools ?? []);
|
|
73
|
+
this._enableTrust = config?.enableTrust ?? false;
|
|
74
|
+
this._evaluateTrust = config?.evaluateTrust ?? ((r) => this._isTrustResponse(r));
|
|
75
|
+
this._evaluate = config?.evaluate;
|
|
76
|
+
this._ask = config?.ask === 'stdio' ? createStdioAsk(this._enableTrust) : config?.ask;
|
|
77
|
+
}
|
|
78
|
+
async beforeToolCall(event) {
|
|
79
|
+
const toolName = event.toolUse.name;
|
|
80
|
+
if (!this._requiresApproval(event)) {
|
|
81
|
+
return proceed();
|
|
82
|
+
}
|
|
83
|
+
const prompt = `Tool "${toolName}" requires human approval. Input: ${JSON.stringify(event.toolUse.input)}`;
|
|
84
|
+
const isNegated = this._allowedTools.has(`!${toolName}`);
|
|
85
|
+
const evaluate = (response) => {
|
|
86
|
+
if (!isNegated && this._enableTrust && this._evaluateTrust(response)) {
|
|
87
|
+
this._trustTool(event, toolName);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return this._evaluate ? this._evaluate(response) : defaultEvaluate(response);
|
|
91
|
+
};
|
|
92
|
+
if (!this._ask) {
|
|
93
|
+
return confirm(prompt, { evaluate });
|
|
94
|
+
}
|
|
95
|
+
const response = await this._ask(prompt);
|
|
96
|
+
if (!isNegated && this._enableTrust && this._evaluateTrust(response)) {
|
|
97
|
+
this._trustTool(event, toolName);
|
|
98
|
+
return proceed();
|
|
99
|
+
}
|
|
100
|
+
return confirm(prompt, {
|
|
101
|
+
response,
|
|
102
|
+
evaluate: this._evaluate ?? defaultEvaluate,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Precedence (first match wins):
|
|
107
|
+
* 1. Negated (`!tool`) → always requires approval (cannot be trusted)
|
|
108
|
+
* 2. Trusted at runtime via 't' response (stored in agent.appState) → runs freely
|
|
109
|
+
* 3. Wildcard (`*`) → runs freely
|
|
110
|
+
* 4. Explicitly listed → runs freely
|
|
111
|
+
* 5. Default → requires approval
|
|
112
|
+
*/
|
|
113
|
+
_requiresApproval(event) {
|
|
114
|
+
const toolName = event.toolUse.name;
|
|
115
|
+
if (this._allowedTools.has(`!${toolName}`))
|
|
116
|
+
return true;
|
|
117
|
+
const trusted = event.agent.appState.get(TRUSTED_TOOLS_KEY) ?? [];
|
|
118
|
+
if (trusted.includes(toolName))
|
|
119
|
+
return false;
|
|
120
|
+
if (this._allowedTools.has('*'))
|
|
121
|
+
return false;
|
|
122
|
+
if (this._allowedTools.has(toolName))
|
|
123
|
+
return false;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
_trustTool(event, toolName) {
|
|
127
|
+
const trusted = event.agent.appState.get(TRUSTED_TOOLS_KEY) ?? [];
|
|
128
|
+
if (!trusted.includes(toolName)) {
|
|
129
|
+
event.agent.appState.set(TRUSTED_TOOLS_KEY, [...trusted, toolName]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
_isTrustResponse(response) {
|
|
133
|
+
if (typeof response === 'string')
|
|
134
|
+
return TRUST_RESPONSES.has(response.toLowerCase().trim());
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=hitl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hitl.js","sourceRoot":"","sources":["../../../../src/vended-interventions/hitl/hitl.ts"],"names":[],"mappings":"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"}
|