@strands-agents/sdk 1.1.0 → 1.3.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.
Files changed (199) hide show
  1. package/README.md +6 -0
  2. package/dist/src/__tests__/interrupt.test.js +13 -8
  3. package/dist/src/__tests__/interrupt.test.js.map +1 -1
  4. package/dist/src/__tests__/mcp.test.js +221 -7
  5. package/dist/src/__tests__/mcp.test.js.map +1 -1
  6. package/dist/src/agent/__tests__/agent.interrupt.test.js +54 -5
  7. package/dist/src/agent/__tests__/agent.interrupt.test.js.map +1 -1
  8. package/dist/src/agent/__tests__/agent.test.js +56 -0
  9. package/dist/src/agent/__tests__/agent.test.js.map +1 -1
  10. package/dist/src/agent/__tests__/printer.test.js +58 -18
  11. package/dist/src/agent/__tests__/printer.test.js.map +1 -1
  12. package/dist/src/agent/__tests__/snapshot.test.js +98 -0
  13. package/dist/src/agent/__tests__/snapshot.test.js.map +1 -1
  14. package/dist/src/agent/agent-as-tool.d.ts.map +1 -1
  15. package/dist/src/agent/agent-as-tool.js +2 -3
  16. package/dist/src/agent/agent-as-tool.js.map +1 -1
  17. package/dist/src/agent/agent.d.ts +65 -0
  18. package/dist/src/agent/agent.d.ts.map +1 -1
  19. package/dist/src/agent/agent.js +89 -10
  20. package/dist/src/agent/agent.js.map +1 -1
  21. package/dist/src/agent/printer.d.ts +14 -1
  22. package/dist/src/agent/printer.d.ts.map +1 -1
  23. package/dist/src/agent/printer.js +33 -5
  24. package/dist/src/agent/printer.js.map +1 -1
  25. package/dist/src/agent/snapshot.d.ts +9 -17
  26. package/dist/src/agent/snapshot.d.ts.map +1 -1
  27. package/dist/src/agent/snapshot.js +9 -17
  28. package/dist/src/agent/snapshot.js.map +1 -1
  29. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +371 -39
  30. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js.map +1 -1
  31. package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts +26 -7
  32. package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
  33. package/dist/src/conversation-manager/sliding-window-conversation-manager.js +192 -41
  34. package/dist/src/conversation-manager/sliding-window-conversation-manager.js.map +1 -1
  35. package/dist/src/hooks/events.d.ts +24 -3
  36. package/dist/src/hooks/events.d.ts.map +1 -1
  37. package/dist/src/hooks/events.js +26 -4
  38. package/dist/src/hooks/events.js.map +1 -1
  39. package/dist/src/hooks/index.d.ts +1 -1
  40. package/dist/src/hooks/index.d.ts.map +1 -1
  41. package/dist/src/hooks/index.js +1 -1
  42. package/dist/src/hooks/index.js.map +1 -1
  43. package/dist/src/hooks/types.d.ts +2 -0
  44. package/dist/src/hooks/types.d.ts.map +1 -1
  45. package/dist/src/hooks/types.js +2 -0
  46. package/dist/src/hooks/types.js.map +1 -1
  47. package/dist/src/index.d.ts +8 -4
  48. package/dist/src/index.d.ts.map +1 -1
  49. package/dist/src/index.js +6 -2
  50. package/dist/src/index.js.map +1 -1
  51. package/dist/src/interrupt.d.ts +29 -2
  52. package/dist/src/interrupt.d.ts.map +1 -1
  53. package/dist/src/interrupt.js +45 -3
  54. package/dist/src/interrupt.js.map +1 -1
  55. package/dist/src/interventions/__tests__/handler.test.d.ts +2 -0
  56. package/dist/src/interventions/__tests__/handler.test.d.ts.map +1 -0
  57. package/dist/src/interventions/__tests__/handler.test.js +35 -0
  58. package/dist/src/interventions/__tests__/handler.test.js.map +1 -0
  59. package/dist/src/interventions/__tests__/registry.test.d.ts +2 -0
  60. package/dist/src/interventions/__tests__/registry.test.d.ts.map +1 -0
  61. package/dist/src/interventions/__tests__/registry.test.js +692 -0
  62. package/dist/src/interventions/__tests__/registry.test.js.map +1 -0
  63. package/dist/src/interventions/actions.d.ts +186 -0
  64. package/dist/src/interventions/actions.d.ts.map +1 -0
  65. package/dist/src/interventions/actions.js +56 -0
  66. package/dist/src/interventions/actions.js.map +1 -0
  67. package/dist/src/interventions/handler.d.ts +44 -0
  68. package/dist/src/interventions/handler.d.ts.map +1 -0
  69. package/dist/src/interventions/handler.js +41 -0
  70. package/dist/src/interventions/handler.js.map +1 -0
  71. package/dist/src/interventions/index.d.ts +12 -0
  72. package/dist/src/interventions/index.d.ts.map +1 -0
  73. package/dist/src/interventions/index.js +4 -0
  74. package/dist/src/interventions/index.js.map +1 -0
  75. package/dist/src/interventions/registry.d.ts +34 -0
  76. package/dist/src/interventions/registry.d.ts.map +1 -0
  77. package/dist/src/interventions/registry.js +248 -0
  78. package/dist/src/interventions/registry.js.map +1 -0
  79. package/dist/src/mcp.d.ts +38 -2
  80. package/dist/src/mcp.d.ts.map +1 -1
  81. package/dist/src/mcp.js +84 -7
  82. package/dist/src/mcp.js.map +1 -1
  83. package/dist/src/models/__tests__/anthropic.test.js +74 -10
  84. package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
  85. package/dist/src/models/__tests__/bedrock.test.js +43 -20
  86. package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
  87. package/dist/src/models/__tests__/google.test.js +14 -6
  88. package/dist/src/models/__tests__/google.test.js.map +1 -1
  89. package/dist/src/models/anthropic.d.ts +15 -3
  90. package/dist/src/models/anthropic.d.ts.map +1 -1
  91. package/dist/src/models/anthropic.js +30 -9
  92. package/dist/src/models/anthropic.js.map +1 -1
  93. package/dist/src/models/bedrock.d.ts +6 -7
  94. package/dist/src/models/bedrock.d.ts.map +1 -1
  95. package/dist/src/models/bedrock.js +31 -18
  96. package/dist/src/models/bedrock.js.map +1 -1
  97. package/dist/src/models/google/model.js +1 -1
  98. package/dist/src/models/google/model.js.map +1 -1
  99. package/dist/src/models/google/types.d.ts +5 -3
  100. package/dist/src/models/google/types.d.ts.map +1 -1
  101. package/dist/src/models/openai/__tests__/chat.test.js +10 -2
  102. package/dist/src/models/openai/__tests__/chat.test.js.map +1 -1
  103. package/dist/src/models/openai/__tests__/mantle.test.d.ts +2 -0
  104. package/dist/src/models/openai/__tests__/mantle.test.d.ts.map +1 -0
  105. package/dist/src/models/openai/__tests__/mantle.test.js +189 -0
  106. package/dist/src/models/openai/__tests__/mantle.test.js.map +1 -0
  107. package/dist/src/models/openai/__tests__/responses.test.js +19 -0
  108. package/dist/src/models/openai/__tests__/responses.test.js.map +1 -1
  109. package/dist/src/models/openai/errors.d.ts.map +1 -1
  110. package/dist/src/models/openai/errors.js +7 -4
  111. package/dist/src/models/openai/errors.js.map +1 -1
  112. package/dist/src/models/openai/index.d.ts +1 -0
  113. package/dist/src/models/openai/index.d.ts.map +1 -1
  114. package/dist/src/models/openai/mantle.d.ts +77 -0
  115. package/dist/src/models/openai/mantle.d.ts.map +1 -0
  116. package/dist/src/models/openai/mantle.js +83 -0
  117. package/dist/src/models/openai/mantle.js.map +1 -0
  118. package/dist/src/models/openai/model.d.ts.map +1 -1
  119. package/dist/src/models/openai/model.js +29 -1
  120. package/dist/src/models/openai/model.js.map +1 -1
  121. package/dist/src/models/openai/types.d.ts +11 -0
  122. package/dist/src/models/openai/types.d.ts.map +1 -1
  123. package/dist/src/multiagent/__tests__/graph.tracer.test.js +14 -0
  124. package/dist/src/multiagent/__tests__/graph.tracer.test.js.map +1 -1
  125. package/dist/src/multiagent/__tests__/interrupts.test.d.ts +2 -0
  126. package/dist/src/multiagent/__tests__/interrupts.test.d.ts.map +1 -0
  127. package/dist/src/multiagent/__tests__/interrupts.test.js +390 -0
  128. package/dist/src/multiagent/__tests__/interrupts.test.js.map +1 -0
  129. package/dist/src/multiagent/__tests__/state.test.js +139 -1
  130. package/dist/src/multiagent/__tests__/state.test.js.map +1 -1
  131. package/dist/src/multiagent/events.d.ts +15 -1
  132. package/dist/src/multiagent/events.d.ts.map +1 -1
  133. package/dist/src/multiagent/events.js +18 -0
  134. package/dist/src/multiagent/events.js.map +1 -1
  135. package/dist/src/multiagent/graph.d.ts +37 -1
  136. package/dist/src/multiagent/graph.d.ts.map +1 -1
  137. package/dist/src/multiagent/graph.js +171 -43
  138. package/dist/src/multiagent/graph.js.map +1 -1
  139. package/dist/src/multiagent/multiagent.d.ts +78 -6
  140. package/dist/src/multiagent/multiagent.d.ts.map +1 -1
  141. package/dist/src/multiagent/multiagent.js +115 -1
  142. package/dist/src/multiagent/multiagent.js.map +1 -1
  143. package/dist/src/multiagent/nodes.d.ts.map +1 -1
  144. package/dist/src/multiagent/nodes.js +55 -21
  145. package/dist/src/multiagent/nodes.js.map +1 -1
  146. package/dist/src/multiagent/state.d.ts +39 -3
  147. package/dist/src/multiagent/state.d.ts.map +1 -1
  148. package/dist/src/multiagent/state.js +80 -1
  149. package/dist/src/multiagent/state.js.map +1 -1
  150. package/dist/src/multiagent/swarm.d.ts +15 -0
  151. package/dist/src/multiagent/swarm.d.ts.map +1 -1
  152. package/dist/src/multiagent/swarm.js +127 -37
  153. package/dist/src/multiagent/swarm.js.map +1 -1
  154. package/dist/src/registry/__tests__/tool-registry.test.js +26 -0
  155. package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -1
  156. package/dist/src/registry/tool-registry.d.ts +9 -7
  157. package/dist/src/registry/tool-registry.d.ts.map +1 -1
  158. package/dist/src/registry/tool-registry.js +29 -10
  159. package/dist/src/registry/tool-registry.js.map +1 -1
  160. package/dist/src/session/__tests__/session-manager.test.js +45 -3
  161. package/dist/src/session/__tests__/session-manager.test.js.map +1 -1
  162. package/dist/src/session/session-manager.d.ts +5 -2
  163. package/dist/src/session/session-manager.d.ts.map +1 -1
  164. package/dist/src/session/session-manager.js +9 -6
  165. package/dist/src/session/session-manager.js.map +1 -1
  166. package/dist/src/telemetry/__tests__/meter.test.js +5 -27
  167. package/dist/src/telemetry/__tests__/meter.test.js.map +1 -1
  168. package/dist/src/telemetry/meter.d.ts +12 -4
  169. package/dist/src/telemetry/meter.d.ts.map +1 -1
  170. package/dist/src/telemetry/meter.js +13 -8
  171. package/dist/src/telemetry/meter.js.map +1 -1
  172. package/dist/src/tools/mcp-tool.d.ts.map +1 -1
  173. package/dist/src/tools/mcp-tool.js +3 -2
  174. package/dist/src/tools/mcp-tool.js.map +1 -1
  175. package/dist/src/tsconfig.tsbuildinfo +1 -1
  176. package/dist/src/types/__tests__/agent.test.js +97 -0
  177. package/dist/src/types/__tests__/agent.test.js.map +1 -1
  178. package/dist/src/types/agent.d.ts +26 -5
  179. package/dist/src/types/agent.d.ts.map +1 -1
  180. package/dist/src/types/agent.js +20 -3
  181. package/dist/src/types/agent.js.map +1 -1
  182. package/dist/src/types/messages.d.ts +3 -1
  183. package/dist/src/types/messages.d.ts.map +1 -1
  184. package/dist/src/types/messages.js.map +1 -1
  185. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js +208 -0
  186. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js.map +1 -1
  187. package/dist/src/vended-plugins/context-offloader/__tests__/search.test.d.ts +2 -0
  188. package/dist/src/vended-plugins/context-offloader/__tests__/search.test.d.ts.map +1 -0
  189. package/dist/src/vended-plugins/context-offloader/__tests__/search.test.js +149 -0
  190. package/dist/src/vended-plugins/context-offloader/__tests__/search.test.js.map +1 -0
  191. package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -1
  192. package/dist/src/vended-plugins/context-offloader/plugin.js +57 -10
  193. package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -1
  194. package/dist/src/vended-plugins/context-offloader/search.d.ts +25 -0
  195. package/dist/src/vended-plugins/context-offloader/search.d.ts.map +1 -0
  196. package/dist/src/vended-plugins/context-offloader/search.js +120 -0
  197. package/dist/src/vended-plugins/context-offloader/search.js.map +1 -0
  198. package/dist/src/vended-tools/notebook/notebook.d.ts +1 -1
  199. package/package.json +11 -1
@@ -0,0 +1,390 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Agent } from '../../agent/agent.js';
3
+ import { MockMessageModel } from '../../__fixtures__/mock-message-model.js';
4
+ import { MockSnapshotStorage } from '../../__fixtures__/mock-storage-provider.js';
5
+ import { createCancellableAgent } from '../../__fixtures__/agent-helpers.js';
6
+ import { createMockTool } from '../../__fixtures__/tool-helpers.js';
7
+ import { InterruptResponseContent } from '../../types/interrupt.js';
8
+ import { Graph } from '../graph.js';
9
+ import { Swarm } from '../swarm.js';
10
+ import { Status } from '../state.js';
11
+ import { SessionManager } from '../../session/session-manager.js';
12
+ import { BeforeNodeCallEvent } from '../events.js';
13
+ import { TextBlock } from '../../types/messages.js';
14
+ /**
15
+ * Interrupt round-trip tests. Verifies that an orchestrator can hit an interrupt,
16
+ * persist enough state via a SessionManager to let a later invocation resume, and
17
+ * produce a clean terminal result once all interrupts are answered.
18
+ *
19
+ * Each run uses a fresh agent instance so session-driven state restoration is what
20
+ * wires resume together — just like a real cross-process resume.
21
+ */
22
+ function makeSessionManager(storage) {
23
+ return new SessionManager({
24
+ sessionId: 'test-session',
25
+ storage: { snapshot: storage },
26
+ });
27
+ }
28
+ /** Tool that interrupts once, then returns a static value on resume. */
29
+ function interruptingTool(name, interruptName, resumeValue = 'ok') {
30
+ return createMockTool(name, (context) => {
31
+ context.interrupt({ name: interruptName, reason: `need ${interruptName}` });
32
+ return resumeValue;
33
+ });
34
+ }
35
+ describe('Multi-agent interrupts: round-trip', () => {
36
+ it('Graph: agent interrupts, resumes via top-level SessionManager', async () => {
37
+ const storage = new MockSnapshotStorage();
38
+ const tool = interruptingTool('confirmTool', 'confirm', 'approved');
39
+ // Agent's model for run 1 returns a tool use (which interrupts).
40
+ const modelRun1 = new MockMessageModel().addTurn({
41
+ type: 'toolUseBlock',
42
+ name: 'confirmTool',
43
+ toolUseId: 'tool-1',
44
+ input: {},
45
+ });
46
+ const agent1 = new Agent({ model: modelRun1, tools: [tool], printer: false, id: 'a' });
47
+ const graph1 = new Graph({
48
+ nodes: [agent1],
49
+ edges: [],
50
+ sessionManager: makeSessionManager(storage),
51
+ });
52
+ const interruptResult = await graph1.invoke('go');
53
+ expect(interruptResult.status).toBe(Status.INTERRUPTED);
54
+ expect(interruptResult.interrupts).toHaveLength(1);
55
+ // Run 2's model provides the final text turn plus a trailing turn that should
56
+ // never be consumed. Two turns are needed so the mock model's callCount tracks
57
+ // (single-turn mode has a quirk where callCount stays at 0 regardless of calls).
58
+ // If the resumed agent replayed the pending tool use correctly, it calls the
59
+ // model exactly once (for the post-tool turn) — NOT twice (which would mean the
60
+ // tool use was re-fetched from the model instead of replayed).
61
+ const modelRun2 = new MockMessageModel()
62
+ .addTurn({ type: 'textBlock', text: 'done' })
63
+ .addTurn({ type: 'textBlock', text: 'unreachable' });
64
+ const agent2 = new Agent({ model: modelRun2, tools: [tool], printer: false, id: 'a' });
65
+ const graph2 = new Graph({
66
+ nodes: [agent2],
67
+ edges: [],
68
+ sessionManager: makeSessionManager(storage),
69
+ });
70
+ const response = new InterruptResponseContent({
71
+ interruptId: interruptResult.interrupts[0].id,
72
+ response: 'yes',
73
+ });
74
+ const finalResult = await graph2.invoke([response]);
75
+ expect(finalResult.status).toBe(Status.COMPLETED);
76
+ expect(finalResult.interrupts).toBeUndefined();
77
+ for (const result of finalResult.results) {
78
+ expect(result.interrupts).toBeUndefined();
79
+ }
80
+ // Model called exactly once on resume — for the post-tool turn. The pending
81
+ // tool use came from the restored snapshot, not a re-fetch.
82
+ expect(modelRun2.callCount).toBe(1);
83
+ });
84
+ it('Swarm: agent interrupts, resumes via top-level SessionManager', async () => {
85
+ const storage = new MockSnapshotStorage();
86
+ const tool = interruptingTool('confirmTool', 'confirm_a', 'resumed');
87
+ const modelRun1 = new MockMessageModel().addTurn({
88
+ type: 'toolUseBlock',
89
+ name: 'confirmTool',
90
+ toolUseId: 'tool-A',
91
+ input: {},
92
+ });
93
+ const agent1 = new Agent({ model: modelRun1, tools: [tool], printer: false, id: 'a' });
94
+ const swarm1 = new Swarm({
95
+ nodes: [agent1],
96
+ start: 'a',
97
+ sessionManager: makeSessionManager(storage),
98
+ });
99
+ const interruptResult = await swarm1.invoke('start');
100
+ expect(interruptResult.status).toBe(Status.INTERRUPTED);
101
+ expect(interruptResult.interrupts).toHaveLength(1);
102
+ // Swarm uses structured output for handoffs — the final (non-handoff) turn
103
+ // terminates execution.
104
+ const modelRun2 = new MockMessageModel().addTurn({
105
+ type: 'toolUseBlock',
106
+ name: 'strands_structured_output',
107
+ toolUseId: 'so-1',
108
+ input: { message: 'all done' },
109
+ });
110
+ const agent2 = new Agent({ model: modelRun2, tools: [tool], printer: false, id: 'a' });
111
+ const swarm2 = new Swarm({
112
+ nodes: [agent2],
113
+ start: 'a',
114
+ sessionManager: makeSessionManager(storage),
115
+ });
116
+ const response = new InterruptResponseContent({
117
+ interruptId: interruptResult.interrupts[0].id,
118
+ response: 'ok',
119
+ });
120
+ const finalResult = await swarm2.invoke([response]);
121
+ expect(finalResult.status).toBe(Status.COMPLETED);
122
+ });
123
+ it('Graph parallel: interrupt on one branch lets in-flight sibling finish', async () => {
124
+ const tool = interruptingTool('confirmTool', 'confirm', 'approved');
125
+ // Source node 'start' runs quickly and produces two parallel branches.
126
+ // Branch 'interrupter' interrupts immediately. Branch 'sibling' takes a moment
127
+ // to complete. The interrupt does not abort siblings — they run to completion
128
+ // and the aggregate result carries both outcomes.
129
+ const startModel = new MockMessageModel().addTurn({ type: 'textBlock', text: 'go' });
130
+ const start = new Agent({ model: startModel, printer: false, id: 'start' });
131
+ const interrupterModel = new MockMessageModel().addTurn({
132
+ type: 'toolUseBlock',
133
+ name: 'confirmTool',
134
+ toolUseId: 'tool-i',
135
+ input: {},
136
+ });
137
+ const interrupter = new Agent({ model: interrupterModel, tools: [tool], printer: false, id: 'interrupter' });
138
+ const sibling = createCancellableAgent('sibling', 50);
139
+ const graph = new Graph({
140
+ nodes: [start, interrupter, sibling],
141
+ edges: [
142
+ ['start', 'interrupter'],
143
+ ['start', 'sibling'],
144
+ ],
145
+ timeout: 5_000,
146
+ });
147
+ const result = await graph.invoke('begin');
148
+ // Aggregate status surfaces INTERRUPTED (the actionable state) — `_resolveStatus`
149
+ // ranks INTERRUPTED above COMPLETED.
150
+ expect(result.status).toBe(Status.INTERRUPTED);
151
+ const siblingResult = result.results.find((r) => r.nodeId === 'sibling');
152
+ expect(siblingResult?.status).toBe(Status.COMPLETED);
153
+ const interrupterResult = result.results.find((r) => r.nodeId === 'interrupter');
154
+ expect(interrupterResult?.status).toBe(Status.INTERRUPTED);
155
+ expect(interrupterResult?.interrupts).toHaveLength(1);
156
+ });
157
+ it('Nested orchestrator: interrupts bubble up on first run but do not round-trip without a nested SessionManager', async () => {
158
+ // Nested orchestrator has no SessionManager of its own, only the outer one does.
159
+ // First run works (interrupt bubbles up through MultiAgentNode into outer result).
160
+ // Second run FAILS at routing because the nested state was never persisted: the
161
+ // nested Swarm's NodeState.interrupts is empty on rehydrate, so the response id
162
+ // has no home. This test pins down the documented limitation.
163
+ const storage = new MockSnapshotStorage();
164
+ const tool = interruptingTool('confirmTool', 'confirm_nested', 'ok');
165
+ const buildInner = () => {
166
+ const model = new MockMessageModel().addTurn({
167
+ type: 'toolUseBlock',
168
+ name: 'confirmTool',
169
+ toolUseId: 'tool-n',
170
+ input: {},
171
+ });
172
+ const agent = new Agent({ model, tools: [tool], printer: false, id: 'inner-agent' });
173
+ return new Swarm({ nodes: [agent], start: 'inner-agent', id: 'inner' });
174
+ };
175
+ const outer1 = new Graph({
176
+ nodes: [{ orchestrator: buildInner() }],
177
+ edges: [],
178
+ sessionManager: makeSessionManager(storage),
179
+ });
180
+ const interruptResult = await outer1.invoke('go');
181
+ expect(interruptResult.status).toBe(Status.INTERRUPTED);
182
+ expect(interruptResult.interrupts).toHaveLength(1);
183
+ const outer2 = new Graph({
184
+ nodes: [{ orchestrator: buildInner() }],
185
+ edges: [],
186
+ sessionManager: makeSessionManager(storage),
187
+ });
188
+ const response = new InterruptResponseContent({
189
+ interruptId: interruptResult.interrupts[0].id,
190
+ response: 'yes',
191
+ });
192
+ // Routing at the outer level finds the MultiAgentNode. Inside, the nested
193
+ // Swarm creates a fresh MultiAgentState; no nested NodeState.interrupts match
194
+ // the response id, so groupInterruptResponsesByNode throws. `Node.stream` catches
195
+ // the error and produces a FAILED result for the nested node. The limitation is
196
+ // diagnosable via the error message on that node's result, just not transparent.
197
+ const finalResult = await outer2.invoke([response]);
198
+ const innerNode = finalResult.results.find((r) => r.nodeId === 'inner');
199
+ expect(innerNode?.status).toBe(Status.FAILED);
200
+ expect(innerNode?.error?.message).toMatch(/no node found with matching interrupt/);
201
+ });
202
+ it('Graph: BeforeNodeCallEvent.interrupt gates a node before it runs, resumes via SessionManager', async () => {
203
+ const storage = new MockSnapshotStorage();
204
+ // The gated node has a normal agent — the interrupt fires BEFORE the node runs
205
+ // via an orchestrator hook, not from inside the agent.
206
+ const buildAgent = () => {
207
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'executed' });
208
+ return new Agent({ model, printer: false, id: 'execute' });
209
+ };
210
+ const graph1 = new Graph({
211
+ nodes: [buildAgent()],
212
+ edges: [],
213
+ sessionManager: makeSessionManager(storage),
214
+ });
215
+ graph1.addHook(BeforeNodeCallEvent, (event) => {
216
+ if (event.nodeId === 'execute') {
217
+ event.interrupt({ name: 'node_approval', reason: 'approve?' });
218
+ }
219
+ });
220
+ const interruptResult = await graph1.invoke('begin');
221
+ expect(interruptResult.status).toBe(Status.INTERRUPTED);
222
+ expect(interruptResult.interrupts).toHaveLength(1);
223
+ expect(interruptResult.interrupts[0].source).toBe('multiagent-hook');
224
+ // Resume with approval. Hook runs again, sees the stored response, returns it
225
+ // without throwing. Node proceeds to execute.
226
+ const graph2 = new Graph({
227
+ nodes: [buildAgent()],
228
+ edges: [],
229
+ sessionManager: makeSessionManager(storage),
230
+ });
231
+ graph2.addHook(BeforeNodeCallEvent, (event) => {
232
+ if (event.nodeId === 'execute') {
233
+ const response = event.interrupt({ name: 'node_approval', reason: 'approve?' });
234
+ if (!response.approved) {
235
+ event.cancel = 'not approved';
236
+ }
237
+ }
238
+ });
239
+ const response = new InterruptResponseContent({
240
+ interruptId: interruptResult.interrupts[0].id,
241
+ response: { approved: true },
242
+ });
243
+ const finalResult = await graph2.invoke([response]);
244
+ expect(finalResult.status).toBe(Status.COMPLETED);
245
+ const executedNode = finalResult.results.find((r) => r.nodeId === 'execute');
246
+ expect(executedNode?.status).toBe(Status.COMPLETED);
247
+ expect(executedNode?.content.some((b) => b instanceof TextBlock && b.text === 'executed')).toBe(true);
248
+ });
249
+ it('Swarm: BeforeNodeCallEvent.interrupt gates a node before it runs, resumes via SessionManager', async () => {
250
+ const storage = new MockSnapshotStorage();
251
+ const buildAgent = () => {
252
+ const model = new MockMessageModel().addTurn({
253
+ type: 'toolUseBlock',
254
+ name: 'strands_structured_output',
255
+ toolUseId: 'so-1',
256
+ input: { message: 'ran' },
257
+ });
258
+ return new Agent({ model, printer: false, id: 'a' });
259
+ };
260
+ const swarm1 = new Swarm({
261
+ nodes: [buildAgent()],
262
+ start: 'a',
263
+ sessionManager: makeSessionManager(storage),
264
+ });
265
+ swarm1.addHook(BeforeNodeCallEvent, (event) => {
266
+ event.interrupt({ name: 'gate', reason: 'approve?' });
267
+ });
268
+ const interruptResult = await swarm1.invoke('begin');
269
+ expect(interruptResult.status).toBe(Status.INTERRUPTED);
270
+ expect(interruptResult.interrupts[0].source).toBe('multiagent-hook');
271
+ const swarm2 = new Swarm({
272
+ nodes: [buildAgent()],
273
+ start: 'a',
274
+ sessionManager: makeSessionManager(storage),
275
+ });
276
+ swarm2.addHook(BeforeNodeCallEvent, (event) => {
277
+ event.interrupt({ name: 'gate', reason: 'approve?' });
278
+ });
279
+ const response = new InterruptResponseContent({
280
+ interruptId: interruptResult.interrupts[0].id,
281
+ response: 'approved',
282
+ });
283
+ const finalResult = await swarm2.invoke([response]);
284
+ expect(finalResult.status).toBe(Status.COMPLETED);
285
+ });
286
+ it('Graph: hook gate + tool interrupt across successive runs, each layer resumed in turn', async () => {
287
+ // Run 1: orchestrator hook gates the node (hook interrupt, source=multiagent-hook).
288
+ // Run 2: hook approves on resume, node runs, tool interrupts (source=tool).
289
+ // Run 3: tool resumes, agent completes.
290
+ // Exercises both interrupt layers in the same graph, with proper layer routing
291
+ // via applyOrchestratorHookResponses.
292
+ const storage = new MockSnapshotStorage();
293
+ const tool = interruptingTool('toolInterrupt', 'tool_confirm', 'done');
294
+ // Each run uses a fresh agent whose model provides only the turns it needs.
295
+ const buildGraph = (modelTurns) => {
296
+ const model = new MockMessageModel();
297
+ if (modelTurns === 'toolUse') {
298
+ model.addTurn({ type: 'toolUseBlock', name: 'toolInterrupt', toolUseId: 'tool-1', input: {} });
299
+ }
300
+ else {
301
+ model.addTurn({ type: 'textBlock', text: 'done' }).addTurn({ type: 'textBlock', text: 'unreachable' });
302
+ }
303
+ const agent = new Agent({ model, tools: [tool], printer: false, id: 'a' });
304
+ const graph = new Graph({
305
+ nodes: [agent],
306
+ edges: [],
307
+ sessionManager: makeSessionManager(storage),
308
+ });
309
+ graph.addHook(BeforeNodeCallEvent, (event) => {
310
+ event.interrupt({ name: 'hook_gate', reason: 'approve node?' });
311
+ });
312
+ return graph;
313
+ };
314
+ const run1 = await buildGraph('toolUse').invoke('begin');
315
+ expect(run1.status).toBe(Status.INTERRUPTED);
316
+ expect(run1.interrupts[0].source).toBe('multiagent-hook');
317
+ const hookResponse = new InterruptResponseContent({
318
+ interruptId: run1.interrupts[0].id,
319
+ response: { approved: true },
320
+ });
321
+ const run2 = await buildGraph('toolUse').invoke([hookResponse]);
322
+ expect(run2.status).toBe(Status.INTERRUPTED);
323
+ expect(run2.interrupts[0].source).toBe('tool');
324
+ });
325
+ it('Graph: hook-gated node still emits NodeResultEvent and AfterNodeCallEvent', async () => {
326
+ // Lifecycle observers (SessionManager per-node save, metrics, tracing) rely on
327
+ // each node terminating with the same event pair regardless of HOW it terminated.
328
+ const agent = new Agent({ model: new MockMessageModel(), printer: false, id: 'gated' });
329
+ const graph = new Graph({ nodes: [agent], edges: [] });
330
+ graph.addHook(BeforeNodeCallEvent, (event) => {
331
+ event.interrupt({ name: 'gate', reason: 'approve?' });
332
+ });
333
+ const eventTypes = [];
334
+ for await (const event of graph.stream('hi')) {
335
+ eventTypes.push(event.type);
336
+ }
337
+ expect(eventTypes).toContain('beforeNodeCallEvent');
338
+ expect(eventTypes).toContain('nodeResultEvent');
339
+ expect(eventTypes).toContain('afterNodeCallEvent');
340
+ // Strict ordering: after comes after result, which comes after before.
341
+ expect(eventTypes.indexOf('beforeNodeCallEvent')).toBeLessThan(eventTypes.indexOf('nodeResultEvent'));
342
+ expect(eventTypes.indexOf('nodeResultEvent')).toBeLessThan(eventTypes.indexOf('afterNodeCallEvent'));
343
+ });
344
+ it('Swarm: hook-gated node still emits NodeResultEvent and AfterNodeCallEvent', async () => {
345
+ const agent = new Agent({ model: new MockMessageModel(), printer: false, id: 'a' });
346
+ const swarm = new Swarm({ nodes: [agent], start: 'a' });
347
+ swarm.addHook(BeforeNodeCallEvent, (event) => {
348
+ event.interrupt({ name: 'gate', reason: 'approve?' });
349
+ });
350
+ const eventTypes = [];
351
+ for await (const event of swarm.stream('hi')) {
352
+ eventTypes.push(event.type);
353
+ }
354
+ expect(eventTypes).toContain('beforeNodeCallEvent');
355
+ expect(eventTypes).toContain('nodeResultEvent');
356
+ expect(eventTypes).toContain('afterNodeCallEvent');
357
+ expect(eventTypes.indexOf('beforeNodeCallEvent')).toBeLessThan(eventTypes.indexOf('nodeResultEvent'));
358
+ expect(eventTypes.indexOf('nodeResultEvent')).toBeLessThan(eventTypes.indexOf('afterNodeCallEvent'));
359
+ });
360
+ it('Graph: resume against a graph whose topology changed throws a descriptive error', async () => {
361
+ // Simulate a save/restore where the reconstructed graph is missing a node that
362
+ // had an outstanding interrupt in the saved state. The routing lookup should fail
363
+ // loudly rather than silently (which would previously have crashed on a non-null
364
+ // assertion with an unhelpful TypeError).
365
+ const storage = new MockSnapshotStorage();
366
+ const tool = interruptingTool('confirmTool', 'confirm_top', 'ok');
367
+ const model1 = new MockMessageModel().addTurn({
368
+ type: 'toolUseBlock',
369
+ name: 'confirmTool',
370
+ toolUseId: 'tool-topo',
371
+ input: {},
372
+ });
373
+ const agent1 = new Agent({ model: model1, tools: [tool], printer: false, id: 'will-vanish' });
374
+ const graph1 = new Graph({ nodes: [agent1], edges: [], sessionManager: makeSessionManager(storage) });
375
+ const interruptResult = await graph1.invoke('go');
376
+ expect(interruptResult.status).toBe(Status.INTERRUPTED);
377
+ const differentAgent = new Agent({
378
+ model: new MockMessageModel(),
379
+ printer: false,
380
+ id: 'different-node',
381
+ });
382
+ const graph2 = new Graph({ nodes: [differentAgent], edges: [], sessionManager: makeSessionManager(storage) });
383
+ const response = new InterruptResponseContent({
384
+ interruptId: interruptResult.interrupts[0].id,
385
+ response: 'yes',
386
+ });
387
+ await expect(graph2.invoke([response])).rejects.toThrow(/topology changed between save and resume/);
388
+ });
389
+ });
390
+ //# sourceMappingURL=interrupts.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interrupts.test.js","sourceRoot":"","sources":["../../../../src/multiagent/__tests__/interrupts.test.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { NodeResult, NodeState, MultiAgentResult, MultiAgentState, Status } from '../state.js';
3
3
  import { TextBlock, ToolUseBlock } from '../../types/messages.js';
4
- import { stateToJSONSymbol, loadStateFromJSONSymbol } from '../../types/serializable.js';
4
+ import { stateToJSONSymbol, loadStateFromJSONSymbol, serializeStateSerializable, loadStateSerializable, } from '../../types/serializable.js';
5
+ import { Interrupt } from '../../interrupt.js';
6
+ import { InterruptResponseContent } from '../../types/interrupt.js';
7
+ import { extractResumeResponses, groupInterruptResponsesByNode } from '../multiagent.js';
5
8
  describe('NodeResult', () => {
6
9
  describe('toJSON / fromJSON', () => {
7
10
  it('round-trips a completed result with text content', () => {
@@ -195,6 +198,35 @@ describe('NodeState', () => {
195
198
  });
196
199
  expect(target.results).toHaveLength(1);
197
200
  });
201
+ it('round-trips interrupts and interruptedSnapshot on an INTERRUPTED node', () => {
202
+ const original = new NodeState();
203
+ original.status = Status.INTERRUPTED;
204
+ original.interrupts = [new Interrupt({ id: 'tool:1:confirm', name: 'confirm', reason: 'need it' })];
205
+ original.interruptedSnapshot = {
206
+ scope: 'agent',
207
+ schemaVersion: '1.0',
208
+ createdAt: '2026-01-01T00:00:00Z',
209
+ data: { messages: [], interrupts: { activated: true, interrupts: {} } },
210
+ appData: {},
211
+ };
212
+ const restored = new NodeState();
213
+ loadStateSerializable(restored, serializeStateSerializable(original));
214
+ expect(restored).toEqual(original);
215
+ });
216
+ it('clears interruptedSnapshot when it is absent from the serialized state', () => {
217
+ const original = new NodeState();
218
+ original.status = Status.COMPLETED;
219
+ const restored = new NodeState();
220
+ restored.interruptedSnapshot = {
221
+ scope: 'agent',
222
+ schemaVersion: '1.0',
223
+ createdAt: '2026-01-01T00:00:00Z',
224
+ data: {},
225
+ appData: {},
226
+ };
227
+ loadStateSerializable(restored, serializeStateSerializable(original));
228
+ expect(restored).toEqual(original);
229
+ });
198
230
  });
199
231
  });
200
232
  describe('MultiAgentResult', () => {
@@ -383,6 +415,112 @@ describe('MultiAgentState', () => {
383
415
  steps: 5,
384
416
  });
385
417
  });
418
+ it('round-trips _pendingInput as a string', () => {
419
+ const original = new MultiAgentState();
420
+ original._pendingInput = 'hello';
421
+ const restored = new MultiAgentState();
422
+ loadStateSerializable(restored, JSON.parse(JSON.stringify(serializeStateSerializable(original))));
423
+ expect(restored._pendingInput).toBe('hello');
424
+ });
425
+ it('rehydrates _pendingInput ContentBlock[] to ContentBlock instances', () => {
426
+ // Round-trips through JSON.stringify/parse to simulate FileStorage persistence,
427
+ // then asserts the restored entries are real ContentBlock instances rather than
428
+ // raw data objects — agent message construction depends on instance shape for
429
+ // some downstream code paths.
430
+ const original = new MultiAgentState();
431
+ original._pendingInput = [new TextBlock('question')];
432
+ const serialized = JSON.parse(JSON.stringify(serializeStateSerializable(original)));
433
+ const restored = new MultiAgentState();
434
+ loadStateSerializable(restored, serialized);
435
+ expect(restored._pendingInput).toEqual([new TextBlock('question')]);
436
+ expect(restored._pendingInput[0]).toBeInstanceOf(TextBlock);
437
+ });
438
+ });
439
+ });
440
+ describe('MultiAgentResult._resolveStatus precedence', () => {
441
+ function makeResult(status, nodeId = 'n') {
442
+ return new NodeResult({ nodeId, status, duration: 1 });
443
+ }
444
+ it('returns COMPLETED when all node results are completed', () => {
445
+ const r = new MultiAgentResult({
446
+ results: [makeResult(Status.COMPLETED), makeResult(Status.COMPLETED)],
447
+ duration: 10,
448
+ });
449
+ expect(r.status).toBe(Status.COMPLETED);
450
+ });
451
+ it('FAILED outranks INTERRUPTED', () => {
452
+ const r = new MultiAgentResult({
453
+ results: [makeResult(Status.INTERRUPTED), makeResult(Status.FAILED)],
454
+ duration: 10,
455
+ });
456
+ expect(r.status).toBe(Status.FAILED);
457
+ });
458
+ it('INTERRUPTED outranks CANCELLED', () => {
459
+ const r = new MultiAgentResult({
460
+ results: [makeResult(Status.CANCELLED), makeResult(Status.INTERRUPTED)],
461
+ duration: 10,
462
+ });
463
+ expect(r.status).toBe(Status.INTERRUPTED);
464
+ });
465
+ it('CANCELLED outranks COMPLETED', () => {
466
+ const r = new MultiAgentResult({
467
+ results: [makeResult(Status.COMPLETED), makeResult(Status.CANCELLED)],
468
+ duration: 10,
469
+ });
470
+ expect(r.status).toBe(Status.CANCELLED);
471
+ });
472
+ it('FAILED outranks CANCELLED', () => {
473
+ const r = new MultiAgentResult({ results: [makeResult(Status.CANCELLED), makeResult(Status.FAILED)], duration: 10 });
474
+ expect(r.status).toBe(Status.FAILED);
475
+ });
476
+ });
477
+ describe('groupInterruptResponsesByNode', () => {
478
+ function makeState(nodeInterrupts) {
479
+ const state = new MultiAgentState({ nodeIds: Object.keys(nodeInterrupts) });
480
+ for (const [id, interrupts] of Object.entries(nodeInterrupts)) {
481
+ state.node(id).interrupts = interrupts;
482
+ }
483
+ return state;
484
+ }
485
+ it('groups responses by the node whose interrupts match each id', () => {
486
+ const state = makeState({
487
+ a: [new Interrupt({ id: 'tool:1:confirm', name: 'confirm' })],
488
+ b: [new Interrupt({ id: 'tool:2:approve', name: 'approve' })],
489
+ });
490
+ const responses = [
491
+ new InterruptResponseContent({ interruptId: 'tool:1:confirm', response: 'yes' }),
492
+ new InterruptResponseContent({ interruptId: 'tool:2:approve', response: 'ok' }),
493
+ ];
494
+ const grouped = groupInterruptResponsesByNode(responses, state);
495
+ expect(grouped.get('a')).toHaveLength(1);
496
+ expect(grouped.get('b')).toHaveLength(1);
497
+ expect(grouped.get('a')?.[0]?.interruptResponse.interruptId).toBe('tool:1:confirm');
498
+ });
499
+ it('throws when a response id does not match any node interrupt', () => {
500
+ const state = makeState({ a: [new Interrupt({ id: 'tool:1:confirm', name: 'confirm' })] });
501
+ const responses = [new InterruptResponseContent({ interruptId: 'tool:missing:xyz', response: 'yes' })];
502
+ expect(() => groupInterruptResponsesByNode(responses, state)).toThrow(/tool:missing:xyz/);
503
+ });
504
+ it('returns an empty map for empty responses', () => {
505
+ const state = makeState({ a: [new Interrupt({ id: 'tool:1:confirm', name: 'confirm' })] });
506
+ const grouped = groupInterruptResponsesByNode([], state);
507
+ expect(grouped.size).toBe(0);
508
+ });
509
+ });
510
+ describe('extractResumeResponses', () => {
511
+ it('throws when interrupt responses are mixed with other content', () => {
512
+ // Cast through `unknown` since the public type rejects mixed arrays at compile-time;
513
+ // this test pins the runtime guard for callers that bypass typing.
514
+ const mixed = [
515
+ new InterruptResponseContent({ interruptId: 'tool:1:confirm', response: 'ok' }),
516
+ new TextBlock('stray content'),
517
+ ];
518
+ expect(() => extractResumeResponses(mixed)).toThrow(TypeError);
519
+ });
520
+ it('returns undefined for empty input or non-response arrays', () => {
521
+ expect(extractResumeResponses([])).toBeUndefined();
522
+ expect(extractResumeResponses('hello')).toBeUndefined();
523
+ expect(extractResumeResponses([new TextBlock('hi')])).toBeUndefined();
386
524
  });
387
525
  });
388
526
  //# sourceMappingURL=state.test.js.map