@strands-agents/sdk 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/src/__fixtures__/agent-helpers.d.ts +16 -1
- package/dist/src/__fixtures__/agent-helpers.d.ts.map +1 -1
- package/dist/src/__fixtures__/agent-helpers.js +42 -0
- package/dist/src/__fixtures__/agent-helpers.js.map +1 -1
- package/dist/src/__fixtures__/tool-helpers.d.ts +2 -1
- package/dist/src/__fixtures__/tool-helpers.d.ts.map +1 -1
- package/dist/src/__fixtures__/tool-helpers.js +20 -3
- package/dist/src/__fixtures__/tool-helpers.js.map +1 -1
- package/dist/src/__tests__/interrupt.test.d.ts +2 -0
- package/dist/src/__tests__/interrupt.test.d.ts.map +1 -0
- package/dist/src/__tests__/interrupt.test.js +264 -0
- package/dist/src/__tests__/interrupt.test.js.map +1 -0
- package/dist/src/__tests__/mcp.test.js +447 -7
- package/dist/src/__tests__/mcp.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.hook.test.js +551 -1
- package/dist/src/agent/__tests__/agent.hook.test.js.map +1 -1
- package/dist/src/agent/__tests__/agent.interrupt.test.d.ts +2 -0
- package/dist/src/agent/__tests__/agent.interrupt.test.d.ts.map +1 -0
- package/dist/src/agent/__tests__/agent.interrupt.test.js +779 -0
- package/dist/src/agent/__tests__/agent.interrupt.test.js.map +1 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.d.ts +2 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.d.ts.map +1 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.js +161 -0
- package/dist/src/agent/__tests__/agent.model-retry.test.js.map +1 -0
- package/dist/src/agent/__tests__/agent.test.js +174 -0
- package/dist/src/agent/__tests__/agent.test.js.map +1 -1
- package/dist/src/agent/__tests__/snapshot.test.js +148 -4
- package/dist/src/agent/__tests__/snapshot.test.js.map +1 -1
- package/dist/src/agent/agent-as-tool.d.ts.map +1 -1
- package/dist/src/agent/agent-as-tool.js +2 -3
- package/dist/src/agent/agent-as-tool.js.map +1 -1
- package/dist/src/agent/agent.d.ts +94 -4
- package/dist/src/agent/agent.d.ts.map +1 -1
- package/dist/src/agent/agent.js +625 -223
- package/dist/src/agent/agent.js.map +1 -1
- package/dist/src/agent/snapshot.d.ts +11 -19
- package/dist/src/agent/snapshot.d.ts.map +1 -1
- package/dist/src/agent/snapshot.js +23 -19
- package/dist/src/agent/snapshot.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.js +230 -9
- package/dist/src/conversation-manager/__tests__/conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js +19 -6
- package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +422 -41
- package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js +75 -1
- package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js.map +1 -1
- package/dist/src/conversation-manager/conversation-manager.d.ts +67 -22
- package/dist/src/conversation-manager/conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/conversation-manager.js +65 -13
- package/dist/src/conversation-manager/conversation-manager.js.map +1 -1
- package/dist/src/conversation-manager/index.d.ts +1 -1
- package/dist/src/conversation-manager/index.d.ts.map +1 -1
- package/dist/src/conversation-manager/index.js +1 -1
- package/dist/src/conversation-manager/index.js.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts +43 -10
- package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js +202 -45
- package/dist/src/conversation-manager/sliding-window-conversation-manager.js.map +1 -1
- package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts +23 -1
- package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts.map +1 -1
- package/dist/src/conversation-manager/summarizing-conversation-manager.js +39 -17
- package/dist/src/conversation-manager/summarizing-conversation-manager.js.map +1 -1
- package/dist/src/hooks/__tests__/events.test.js +99 -12
- package/dist/src/hooks/__tests__/events.test.js.map +1 -1
- package/dist/src/hooks/__tests__/registry.test.js +166 -2
- package/dist/src/hooks/__tests__/registry.test.js.map +1 -1
- package/dist/src/hooks/events.d.ts +125 -32
- package/dist/src/hooks/events.d.ts.map +1 -1
- package/dist/src/hooks/events.js +111 -8
- package/dist/src/hooks/events.js.map +1 -1
- package/dist/src/hooks/index.d.ts +4 -3
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/index.js +2 -1
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/hooks/registry.d.ts +12 -12
- package/dist/src/hooks/registry.d.ts.map +1 -1
- package/dist/src/hooks/registry.js +55 -15
- package/dist/src/hooks/registry.js.map +1 -1
- package/dist/src/hooks/types.d.ts +23 -0
- package/dist/src/hooks/types.d.ts.map +1 -1
- package/dist/src/hooks/types.js +17 -1
- package/dist/src/hooks/types.js.map +1 -1
- package/dist/src/index.d.ts +12 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/interrupt.d.ts +247 -0
- package/dist/src/interrupt.d.ts.map +1 -0
- package/dist/src/interrupt.js +316 -0
- package/dist/src/interrupt.js.map +1 -0
- package/dist/src/mcp.d.ts +61 -4
- package/dist/src/mcp.d.ts.map +1 -1
- package/dist/src/mcp.js +161 -25
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/models/__tests__/anthropic.test.js +78 -8
- package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
- package/dist/src/models/__tests__/bedrock.test.js +156 -18
- package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
- package/dist/src/models/__tests__/defaults.test.d.ts +2 -0
- package/dist/src/models/__tests__/defaults.test.d.ts.map +1 -0
- package/dist/src/models/__tests__/defaults.test.js +36 -0
- package/dist/src/models/__tests__/defaults.test.js.map +1 -0
- package/dist/src/models/__tests__/google.test.js +72 -6
- package/dist/src/models/__tests__/google.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 +14 -4
- package/dist/src/models/anthropic.js.map +1 -1
- package/dist/src/models/bedrock.d.ts +17 -3
- package/dist/src/models/bedrock.d.ts.map +1 -1
- package/dist/src/models/bedrock.js +80 -13
- package/dist/src/models/bedrock.js.map +1 -1
- package/dist/src/models/defaults.d.ts +10 -0
- package/dist/src/models/defaults.d.ts.map +1 -1
- package/dist/src/models/defaults.js +129 -0
- package/dist/src/models/defaults.js.map +1 -1
- package/dist/src/models/google/model.d.ts.map +1 -1
- package/dist/src/models/google/model.js +4 -2
- package/dist/src/models/google/model.js.map +1 -1
- package/dist/src/models/google/types.d.ts +10 -0
- package/dist/src/models/google/types.d.ts.map +1 -1
- package/dist/src/models/model.d.ts +15 -0
- package/dist/src/models/model.d.ts.map +1 -1
- package/dist/src/models/model.js +18 -0
- package/dist/src/models/model.js.map +1 -1
- package/dist/src/models/openai/__tests__/chat.test.js +55 -2
- package/dist/src/models/openai/__tests__/chat.test.js.map +1 -1
- package/dist/src/models/openai/__tests__/responses.test.js +19 -0
- 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 +7 -4
- package/dist/src/models/openai/errors.js.map +1 -1
- package/dist/src/models/openai/model.d.ts.map +1 -1
- package/dist/src/models/openai/model.js +2 -2
- package/dist/src/models/openai/model.js.map +1 -1
- package/dist/src/multiagent/__tests__/graph.test.js +69 -0
- package/dist/src/multiagent/__tests__/graph.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/graph.tracer.test.js +14 -0
- package/dist/src/multiagent/__tests__/graph.tracer.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/interrupts.test.d.ts +2 -0
- package/dist/src/multiagent/__tests__/interrupts.test.d.ts.map +1 -0
- package/dist/src/multiagent/__tests__/interrupts.test.js +390 -0
- package/dist/src/multiagent/__tests__/interrupts.test.js.map +1 -0
- package/dist/src/multiagent/__tests__/nodes.test.js +13 -0
- package/dist/src/multiagent/__tests__/nodes.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/state.test.js +139 -1
- package/dist/src/multiagent/__tests__/state.test.js.map +1 -1
- package/dist/src/multiagent/__tests__/swarm.test.js +77 -0
- package/dist/src/multiagent/__tests__/swarm.test.js.map +1 -1
- package/dist/src/multiagent/events.d.ts +15 -1
- package/dist/src/multiagent/events.d.ts.map +1 -1
- package/dist/src/multiagent/events.js +18 -0
- package/dist/src/multiagent/events.js.map +1 -1
- package/dist/src/multiagent/graph.d.ts +59 -3
- package/dist/src/multiagent/graph.d.ts.map +1 -1
- package/dist/src/multiagent/graph.js +201 -34
- package/dist/src/multiagent/graph.js.map +1 -1
- package/dist/src/multiagent/multiagent.d.ts +77 -3
- package/dist/src/multiagent/multiagent.d.ts.map +1 -1
- package/dist/src/multiagent/multiagent.js +115 -1
- package/dist/src/multiagent/multiagent.js.map +1 -1
- package/dist/src/multiagent/nodes.d.ts +18 -0
- package/dist/src/multiagent/nodes.d.ts.map +1 -1
- package/dist/src/multiagent/nodes.js +69 -22
- package/dist/src/multiagent/nodes.js.map +1 -1
- package/dist/src/multiagent/state.d.ts +39 -3
- package/dist/src/multiagent/state.d.ts.map +1 -1
- package/dist/src/multiagent/state.js +80 -1
- package/dist/src/multiagent/state.js.map +1 -1
- package/dist/src/multiagent/swarm.d.ts +30 -1
- package/dist/src/multiagent/swarm.d.ts.map +1 -1
- package/dist/src/multiagent/swarm.js +166 -33
- package/dist/src/multiagent/swarm.js.map +1 -1
- package/dist/src/registry/__tests__/tool-registry.test.js +37 -0
- package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -1
- package/dist/src/registry/tool-registry.d.ts +13 -7
- package/dist/src/registry/tool-registry.d.ts.map +1 -1
- package/dist/src/registry/tool-registry.js +35 -10
- package/dist/src/registry/tool-registry.js.map +1 -1
- package/dist/src/retry/__tests__/backoff-strategy.test.d.ts +2 -0
- package/dist/src/retry/__tests__/backoff-strategy.test.d.ts.map +1 -0
- package/dist/src/retry/__tests__/backoff-strategy.test.js +116 -0
- package/dist/src/retry/__tests__/backoff-strategy.test.js.map +1 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.d.ts +2 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.d.ts.map +1 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.js +225 -0
- package/dist/src/retry/__tests__/default-model-retry-strategy.test.js.map +1 -0
- package/dist/src/retry/backoff-strategy.d.ts +108 -0
- package/dist/src/retry/backoff-strategy.d.ts.map +1 -0
- package/dist/src/retry/backoff-strategy.js +86 -0
- package/dist/src/retry/backoff-strategy.js.map +1 -0
- package/dist/src/retry/default-model-retry-strategy.d.ts +76 -0
- package/dist/src/retry/default-model-retry-strategy.d.ts.map +1 -0
- package/dist/src/retry/default-model-retry-strategy.js +104 -0
- package/dist/src/retry/default-model-retry-strategy.js.map +1 -0
- package/dist/src/retry/index.d.ts +8 -0
- package/dist/src/retry/index.d.ts.map +1 -0
- package/dist/src/retry/index.js +7 -0
- package/dist/src/retry/index.js.map +1 -0
- package/dist/src/retry/model-retry-strategy.d.ts +80 -0
- package/dist/src/retry/model-retry-strategy.d.ts.map +1 -0
- package/dist/src/retry/model-retry-strategy.js +85 -0
- package/dist/src/retry/model-retry-strategy.js.map +1 -0
- package/dist/src/retry/retry-strategy.d.ts +34 -0
- package/dist/src/retry/retry-strategy.d.ts.map +1 -0
- package/dist/src/retry/retry-strategy.js +25 -0
- package/dist/src/retry/retry-strategy.js.map +1 -0
- package/dist/src/session/__tests__/session-manager.test.js +84 -3
- package/dist/src/session/__tests__/session-manager.test.js.map +1 -1
- package/dist/src/session/session-manager.d.ts +11 -2
- package/dist/src/session/session-manager.d.ts.map +1 -1
- package/dist/src/session/session-manager.js +17 -6
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/telemetry/__tests__/meter.test.js +5 -27
- package/dist/src/telemetry/__tests__/meter.test.js.map +1 -1
- package/dist/src/telemetry/meter.d.ts +12 -4
- package/dist/src/telemetry/meter.d.ts.map +1 -1
- package/dist/src/telemetry/meter.js +13 -8
- package/dist/src/telemetry/meter.js.map +1 -1
- package/dist/src/tools/__tests__/tool.test.js +24 -1
- package/dist/src/tools/__tests__/tool.test.js.map +1 -1
- package/dist/src/tools/function-tool.d.ts.map +1 -1
- package/dist/src/tools/function-tool.js +6 -1
- package/dist/src/tools/function-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts.map +1 -1
- package/dist/src/tools/mcp-tool.js +3 -2
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/tool.d.ts +10 -1
- package/dist/src/tools/tool.d.ts.map +1 -1
- package/dist/src/tools/tool.js +12 -0
- package/dist/src/tools/tool.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/types/__tests__/agent.test.js +97 -0
- package/dist/src/types/__tests__/agent.test.js.map +1 -1
- package/dist/src/types/agent.d.ts +48 -8
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/agent.js +28 -3
- package/dist/src/types/agent.js.map +1 -1
- package/dist/src/types/interrupt.d.ts +103 -0
- package/dist/src/types/interrupt.d.ts.map +1 -0
- package/dist/src/types/interrupt.js +63 -0
- package/dist/src/types/interrupt.js.map +1 -0
- package/dist/src/types/messages.d.ts +2 -1
- package/dist/src/types/messages.d.ts.map +1 -1
- package/dist/src/types/messages.js.map +1 -1
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js +292 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.js +148 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.d.ts +2 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.js +78 -0
- package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.node.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/index.d.ts +23 -0
- package/dist/src/vended-plugins/context-offloader/index.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/index.js +21 -0
- package/dist/src/vended-plugins/context-offloader/index.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/plugin.d.ts +48 -0
- package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/plugin.js +244 -0
- package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -0
- package/dist/src/vended-plugins/context-offloader/storage.d.ts +114 -0
- package/dist/src/vended-plugins/context-offloader/storage.d.ts.map +1 -0
- package/dist/src/vended-plugins/context-offloader/storage.js +204 -0
- package/dist/src/vended-plugins/context-offloader/storage.js.map +1 -0
- package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js +12 -0
- package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js.map +1 -1
- package/dist/src/vended-tools/bash/__tests__/bash.test.node.js +3 -0
- package/dist/src/vended-tools/bash/__tests__/bash.test.node.js.map +1 -1
- package/dist/src/vended-tools/bash/bash.d.ts.map +1 -1
- package/dist/src/vended-tools/bash/bash.js +0 -3
- package/dist/src/vended-tools/bash/bash.js.map +1 -1
- package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js +3 -0
- package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js.map +1 -1
- package/dist/src/vended-tools/notebook/__tests__/notebook.test.js +3 -0
- package/dist/src/vended-tools/notebook/__tests__/notebook.test.js.map +1 -1
- package/dist/src/vended-tools/notebook/notebook.d.ts +1 -1
- package/package.json +9 -5
|
@@ -2,6 +2,7 @@ import { StateStore } from '../state-store.js';
|
|
|
2
2
|
import { contentBlockFromData } from '../types/messages.js';
|
|
3
3
|
import { accumulateUsage, createEmptyUsage } from '../models/streaming.js';
|
|
4
4
|
import { normalizeError, serializeError } from '../errors.js';
|
|
5
|
+
import { Interrupt } from '../interrupt.js';
|
|
5
6
|
import { loadStateFromJSONSymbol, stateToJSONSymbol, serializeStateSerializable, loadStateSerializable, } from '../types/serializable.js';
|
|
6
7
|
/**
|
|
7
8
|
* Execution lifecycle status shared across all multi-agent patterns.
|
|
@@ -17,6 +18,8 @@ export const Status = {
|
|
|
17
18
|
FAILED: 'FAILED',
|
|
18
19
|
/** Execution was cancelled before or during processing. */
|
|
19
20
|
CANCELLED: 'CANCELLED',
|
|
21
|
+
/** Execution paused awaiting an interrupt response; can be resumed. */
|
|
22
|
+
INTERRUPTED: 'INTERRUPTED',
|
|
20
23
|
};
|
|
21
24
|
/**
|
|
22
25
|
* Result of executing a single node.
|
|
@@ -33,6 +36,8 @@ export class NodeResult {
|
|
|
33
36
|
structuredOutput;
|
|
34
37
|
/** Token usage from the node execution. */
|
|
35
38
|
usage;
|
|
39
|
+
/** Interrupts raised by the underlying agent/orchestrator. Present iff `status === 'INTERRUPTED'`. */
|
|
40
|
+
interrupts;
|
|
36
41
|
constructor(data) {
|
|
37
42
|
this.nodeId = data.nodeId;
|
|
38
43
|
this.status = data.status;
|
|
@@ -44,6 +49,8 @@ export class NodeResult {
|
|
|
44
49
|
this.structuredOutput = data.structuredOutput;
|
|
45
50
|
if ('usage' in data)
|
|
46
51
|
this.usage = data.usage;
|
|
52
|
+
if (data.interrupts && data.interrupts.length > 0)
|
|
53
|
+
this.interrupts = data.interrupts;
|
|
47
54
|
}
|
|
48
55
|
/** Serializes this result to a JSON-compatible value. */
|
|
49
56
|
toJSON() {
|
|
@@ -56,6 +63,7 @@ export class NodeResult {
|
|
|
56
63
|
...(this.error && { error: serializeError(this.error) }),
|
|
57
64
|
...(this.structuredOutput !== undefined && { structuredOutput: this.structuredOutput }),
|
|
58
65
|
...(this.usage && { usage: { ...this.usage } }),
|
|
66
|
+
...(this.interrupts && { interrupts: this.interrupts.map((i) => i.toJSON()) }),
|
|
59
67
|
};
|
|
60
68
|
}
|
|
61
69
|
/** Creates a NodeResult from a previously serialized JSON value. */
|
|
@@ -69,6 +77,9 @@ export class NodeResult {
|
|
|
69
77
|
...(json.error && { error: normalizeError(json.error) }),
|
|
70
78
|
...(json.structuredOutput !== undefined && { structuredOutput: json.structuredOutput }),
|
|
71
79
|
...(json.usage && { usage: json.usage }),
|
|
80
|
+
...(json.interrupts && {
|
|
81
|
+
interrupts: json.interrupts.map((i) => Interrupt.fromJSON(i)),
|
|
82
|
+
}),
|
|
72
83
|
});
|
|
73
84
|
}
|
|
74
85
|
}
|
|
@@ -83,11 +94,21 @@ export class NodeState {
|
|
|
83
94
|
/** Node execution start time in milliseconds since epoch. */
|
|
84
95
|
startTime;
|
|
85
96
|
results;
|
|
97
|
+
/** Unanswered interrupts raised during this node's most recent run. Populated when `status === 'INTERRUPTED'`. */
|
|
98
|
+
interrupts;
|
|
99
|
+
/**
|
|
100
|
+
* Snapshot of the node's underlying runnable (Agent or nested orchestrator) captured
|
|
101
|
+
* when the node returned INTERRUPTED. Loaded back into the runnable on resume so it
|
|
102
|
+
* can pick up mid-execution without losing its interrupt bookkeeping. Cleared when
|
|
103
|
+
* the node completes.
|
|
104
|
+
*/
|
|
105
|
+
interruptedSnapshot;
|
|
86
106
|
constructor() {
|
|
87
107
|
this.status = Status.PENDING;
|
|
88
108
|
this.terminus = false;
|
|
89
109
|
this.startTime = Date.now();
|
|
90
110
|
this.results = [];
|
|
111
|
+
this.interrupts = [];
|
|
91
112
|
}
|
|
92
113
|
/** Content from the most recent result, or empty array if none. */
|
|
93
114
|
get content() {
|
|
@@ -101,6 +122,8 @@ export class NodeState {
|
|
|
101
122
|
terminus: this.terminus,
|
|
102
123
|
startTime: this.startTime,
|
|
103
124
|
results: this.results.map((res) => res.toJSON()),
|
|
125
|
+
interrupts: this.interrupts.map((i) => i.toJSON()),
|
|
126
|
+
...(this.interruptedSnapshot && { interruptedSnapshot: { ...this.interruptedSnapshot } }),
|
|
104
127
|
};
|
|
105
128
|
}
|
|
106
129
|
/** Loads state from a previously serialized JSON value. */
|
|
@@ -113,6 +136,13 @@ export class NodeState {
|
|
|
113
136
|
for (const entry of data.results) {
|
|
114
137
|
this.results.push(NodeResult.fromJSON(entry));
|
|
115
138
|
}
|
|
139
|
+
this.interrupts = (data.interrupts ?? []).map((i) => Interrupt.fromJSON(i));
|
|
140
|
+
if (data.interruptedSnapshot) {
|
|
141
|
+
this.interruptedSnapshot = data.interruptedSnapshot;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
delete this.interruptedSnapshot;
|
|
145
|
+
}
|
|
116
146
|
}
|
|
117
147
|
}
|
|
118
148
|
/**
|
|
@@ -128,6 +158,8 @@ export class MultiAgentResult {
|
|
|
128
158
|
error;
|
|
129
159
|
/** Aggregated token usage across all node results. */
|
|
130
160
|
usage;
|
|
161
|
+
/** Interrupts aggregated across all node results. Present when any node ended INTERRUPTED. */
|
|
162
|
+
interrupts;
|
|
131
163
|
constructor(data) {
|
|
132
164
|
this.status = data.status ?? this._resolveStatus(data.results);
|
|
133
165
|
this.results = data.results;
|
|
@@ -136,6 +168,9 @@ export class MultiAgentResult {
|
|
|
136
168
|
if ('error' in data)
|
|
137
169
|
this.error = data.error;
|
|
138
170
|
this.usage = this._aggregateNodeUsage(data.results);
|
|
171
|
+
const interrupts = data.interrupts ?? data.results.flatMap((r) => r.interrupts ?? []);
|
|
172
|
+
if (interrupts.length > 0)
|
|
173
|
+
this.interrupts = interrupts;
|
|
139
174
|
}
|
|
140
175
|
/** Serializes this result to a JSON-compatible value. */
|
|
141
176
|
toJSON() {
|
|
@@ -147,6 +182,7 @@ export class MultiAgentResult {
|
|
|
147
182
|
duration: this.duration,
|
|
148
183
|
usage: { ...this.usage },
|
|
149
184
|
...(this.error && { error: serializeError(this.error) }),
|
|
185
|
+
...(this.interrupts && { interrupts: this.interrupts.map((i) => i.toJSON()) }),
|
|
150
186
|
};
|
|
151
187
|
}
|
|
152
188
|
/** Creates a MultiAgentResult from a previously serialized JSON value. */
|
|
@@ -158,12 +194,24 @@ export class MultiAgentResult {
|
|
|
158
194
|
content: json.content.map((c) => contentBlockFromData(c)),
|
|
159
195
|
duration: json.duration,
|
|
160
196
|
...(json.error && { error: normalizeError(json.error) }),
|
|
197
|
+
...(json.interrupts && {
|
|
198
|
+
interrupts: json.interrupts.map((i) => Interrupt.fromJSON(i)),
|
|
199
|
+
}),
|
|
161
200
|
});
|
|
162
201
|
}
|
|
163
|
-
/**
|
|
202
|
+
/**
|
|
203
|
+
* Derives the aggregate status from individual node results.
|
|
204
|
+
*
|
|
205
|
+
* Precedence: FAILED \> INTERRUPTED \> CANCELLED \> COMPLETED. INTERRUPTED outranks
|
|
206
|
+
* CANCELLED because parallel-graph short-circuit aborts siblings as CANCELLED when
|
|
207
|
+
* one node interrupts — the actionable "resume me" signal should surface over the
|
|
208
|
+
* collateral cancellations.
|
|
209
|
+
*/
|
|
164
210
|
_resolveStatus(results) {
|
|
165
211
|
if (results.some((result) => result.status === Status.FAILED))
|
|
166
212
|
return Status.FAILED;
|
|
213
|
+
if (results.some((result) => result.status === Status.INTERRUPTED))
|
|
214
|
+
return Status.INTERRUPTED;
|
|
167
215
|
if (results.some((result) => result.status === Status.CANCELLED))
|
|
168
216
|
return Status.CANCELLED;
|
|
169
217
|
return Status.COMPLETED;
|
|
@@ -179,6 +227,21 @@ export class MultiAgentResult {
|
|
|
179
227
|
return usage;
|
|
180
228
|
}
|
|
181
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Rehydrates a serialized `_pendingInput` back to its runtime shape. `string` round-trips
|
|
232
|
+
* as-is; array inputs (which serialize as `ContentBlockData[]` via each block's `toJSON`)
|
|
233
|
+
* are mapped through `contentBlockFromData` so downstream callers see `ContentBlock[]`
|
|
234
|
+
* instead of raw data objects.
|
|
235
|
+
*/
|
|
236
|
+
function rehydratePendingInput(value) {
|
|
237
|
+
if (typeof value === 'string')
|
|
238
|
+
return value;
|
|
239
|
+
if (Array.isArray(value)) {
|
|
240
|
+
return value.map((entry) => contentBlockFromData(entry));
|
|
241
|
+
}
|
|
242
|
+
// Unexpected shape — pass through so callers see the exact value and can diagnose.
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
182
245
|
/**
|
|
183
246
|
* Per-execution state for multi-agent orchestration, created fresh each invocation.
|
|
184
247
|
*/
|
|
@@ -191,6 +254,15 @@ export class MultiAgentState {
|
|
|
191
254
|
results;
|
|
192
255
|
/** App-level key-value state accessible from hooks, edge handlers, and custom nodes. */
|
|
193
256
|
app;
|
|
257
|
+
/**
|
|
258
|
+
* The invocation's input, carried through an interrupt pause so that resuming a
|
|
259
|
+
* run (on the same instance, or via a SessionManager) can re-enter nodes that
|
|
260
|
+
* never ran (hook-gated source/start nodes) with the original content. Cleared
|
|
261
|
+
* when the invocation terminates in any non-INTERRUPTED state.
|
|
262
|
+
*
|
|
263
|
+
* @internal — not part of the public state shape; orchestrator-owned.
|
|
264
|
+
*/
|
|
265
|
+
_pendingInput;
|
|
194
266
|
_nodes;
|
|
195
267
|
constructor(data) {
|
|
196
268
|
this.startTime = Date.now();
|
|
@@ -229,6 +301,7 @@ export class MultiAgentState {
|
|
|
229
301
|
results: this.results.map((result) => result.toJSON()),
|
|
230
302
|
app: serializeStateSerializable(this.app),
|
|
231
303
|
nodes,
|
|
304
|
+
...(this._pendingInput !== undefined && { _pendingInput: this._pendingInput }),
|
|
232
305
|
};
|
|
233
306
|
}
|
|
234
307
|
/** Loads state from a previously serialized JSON value. */
|
|
@@ -250,6 +323,12 @@ export class MultiAgentState {
|
|
|
250
323
|
this._nodes.set(id, nodeState);
|
|
251
324
|
}
|
|
252
325
|
}
|
|
326
|
+
if (data._pendingInput !== undefined) {
|
|
327
|
+
this._pendingInput = rehydratePendingInput(data._pendingInput);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
delete this._pendingInput;
|
|
331
|
+
}
|
|
253
332
|
}
|
|
254
333
|
}
|
|
255
334
|
//# sourceMappingURL=state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/multiagent/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAqB,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAE9E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAG1E,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,GAEtB,MAAM,0BAA0B,CAAA;AAEjC;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,qCAAqC;IACrC,OAAO,EAAE,SAAS;IAClB,0CAA0C;IAC1C,SAAS,EAAE,WAAW;IACtB,uCAAuC;IACvC,SAAS,EAAE,WAAW;IACtB,sCAAsC;IACtC,MAAM,EAAE,QAAQ;IAChB,2DAA2D;IAC3D,SAAS,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/multiagent/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,8 +14,22 @@ import type { MultiAgentStreamEvent } from './events.js';
|
|
|
14
14
|
* Runtime configuration for swarm execution.
|
|
15
15
|
*/
|
|
16
16
|
export interface SwarmConfig {
|
|
17
|
-
/** Max total agent executions (including start). Defaults to Infinity. */
|
|
17
|
+
/** Max total agent executions (including start). Defaults to `Infinity` (no limit). */
|
|
18
18
|
maxSteps?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Wall-clock ceiling for the entire swarm invocation, in milliseconds. Defaults to `Infinity`
|
|
21
|
+
* (no limit). Composed with each node's cancel signal, so a node that exceeds this bound
|
|
22
|
+
* mid-execution will be aborted (cooperatively).
|
|
23
|
+
*/
|
|
24
|
+
timeout?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Fallback per-node wall-clock ceiling in milliseconds. Applied to any node that doesn't
|
|
27
|
+
* set its own `timeout`. Defaults to `Infinity` (no limit).
|
|
28
|
+
*
|
|
29
|
+
* Enforced via `AbortSignal` — cancellation is cooperative, so a tool that neither polls
|
|
30
|
+
* its cancel signal nor forwards it to a cancellable API can run past this deadline.
|
|
31
|
+
*/
|
|
32
|
+
nodeTimeout?: number;
|
|
19
33
|
}
|
|
20
34
|
/**
|
|
21
35
|
* Input type for swarm nodes. Pass an {@link InvokableAgent} directly for the simple case,
|
|
@@ -74,6 +88,13 @@ export declare class Swarm implements MultiAgent {
|
|
|
74
88
|
readonly start: AgentNode;
|
|
75
89
|
readonly sessionManager?: SessionManager | undefined;
|
|
76
90
|
private _initialized;
|
|
91
|
+
/**
|
|
92
|
+
* State retained across invocations when a run ends INTERRUPTED. Lets
|
|
93
|
+
* `swarm.invoke(responses)` resume on the same instance without requiring a
|
|
94
|
+
* SessionManager, mirroring single-agent ergonomics. Cleared when a run
|
|
95
|
+
* terminates in any non-INTERRUPTED state.
|
|
96
|
+
*/
|
|
97
|
+
private _pendingInterruptState?;
|
|
77
98
|
constructor(options: SwarmOptions);
|
|
78
99
|
/**
|
|
79
100
|
* Initialize the swarm. Invokes the {@link MultiAgentInitializedEvent} callback.
|
|
@@ -106,11 +127,19 @@ export declare class Swarm implements MultiAgent {
|
|
|
106
127
|
*/
|
|
107
128
|
stream(input: MultiAgentInput, options?: MultiAgentInvokeOptions): AsyncGenerator<MultiAgentStreamEvent, MultiAgentResult, undefined>;
|
|
108
129
|
private _stream;
|
|
130
|
+
/** Invokes hook callbacks on an event, then yields it. */
|
|
131
|
+
private _emit;
|
|
109
132
|
private _streamNode;
|
|
110
133
|
private _validateConfig;
|
|
111
134
|
private _resolveNodes;
|
|
112
135
|
private _resolveStart;
|
|
113
136
|
private _resolveContent;
|
|
137
|
+
/**
|
|
138
|
+
* Builds the input for the next node after a handoff, or returns the input as-is
|
|
139
|
+
* when there is no handoff (initial or resume invocation). The caller passes the
|
|
140
|
+
* original `MultiAgentInput` through; resume responses flow through here untouched
|
|
141
|
+
* so the underlying agent sees them directly.
|
|
142
|
+
*/
|
|
114
143
|
private _resolveNodeInput;
|
|
115
144
|
/**
|
|
116
145
|
* Checks whether the swarm has exceeded its step limit with work still pending.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../../../src/multiagent/swarm.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../../../src/multiagent/swarm.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { logger } from '../logging/logger.js';
|
|
2
|
+
import { warnOnce } from '../logging/warn-once.js';
|
|
3
|
+
import { applyOrchestratorHookResponses, dropStaleInterruptedResult, extractResumeResponses, groupInterruptResponsesByNode, recordHookInterrupt, } from './multiagent.js';
|
|
4
|
+
import { InterruptError } from '../interrupt.js';
|
|
2
5
|
import { z } from 'zod';
|
|
3
6
|
import { HookableEvent } from '../hooks/events.js';
|
|
4
7
|
import { HookRegistryImplementation } from '../hooks/registry.js';
|
|
@@ -6,7 +9,7 @@ import { MultiAgentPluginRegistry } from './plugins.js';
|
|
|
6
9
|
import { TextBlock } from '../types/messages.js';
|
|
7
10
|
import { AgentNode } from './nodes.js';
|
|
8
11
|
import { MultiAgentState, MultiAgentResult, NodeResult, Status } from './state.js';
|
|
9
|
-
import { AfterMultiAgentInvocationEvent, AfterNodeCallEvent, BeforeMultiAgentInvocationEvent, BeforeNodeCallEvent, MultiAgentHandoffEvent, MultiAgentInitializedEvent, MultiAgentResultEvent, NodeCancelEvent, } from './events.js';
|
|
12
|
+
import { AfterMultiAgentInvocationEvent, AfterNodeCallEvent, BeforeMultiAgentInvocationEvent, BeforeNodeCallEvent, MultiAgentHandoffEvent, MultiAgentInitializedEvent, MultiAgentResultEvent, NodeCancelEvent, NodeResultEvent, } from './events.js';
|
|
10
13
|
import { Tracer } from '../telemetry/tracer.js';
|
|
11
14
|
import { normalizeError } from '../errors.js';
|
|
12
15
|
/**
|
|
@@ -47,13 +50,25 @@ export class Swarm {
|
|
|
47
50
|
start;
|
|
48
51
|
sessionManager;
|
|
49
52
|
_initialized;
|
|
53
|
+
/**
|
|
54
|
+
* State retained across invocations when a run ends INTERRUPTED. Lets
|
|
55
|
+
* `swarm.invoke(responses)` resume on the same instance without requiring a
|
|
56
|
+
* SessionManager, mirroring single-agent ergonomics. Cleared when a run
|
|
57
|
+
* terminates in any non-INTERRUPTED state.
|
|
58
|
+
*/
|
|
59
|
+
_pendingInterruptState;
|
|
50
60
|
constructor(options) {
|
|
51
61
|
const { id, nodes, start, sessionManager, plugins, traceAttributes, ...config } = options;
|
|
52
62
|
this.id = id ?? 'swarm';
|
|
53
63
|
this.config = {
|
|
54
64
|
maxSteps: config.maxSteps ?? Infinity,
|
|
65
|
+
timeout: config.timeout ?? Infinity,
|
|
66
|
+
nodeTimeout: config.nodeTimeout ?? Infinity,
|
|
55
67
|
};
|
|
56
68
|
this._validateConfig();
|
|
69
|
+
if (this.config.maxSteps === Infinity && this.config.timeout === Infinity) {
|
|
70
|
+
warnOnce(logger, 'swarm has no maxSteps or timeout set; execution is unbounded');
|
|
71
|
+
}
|
|
57
72
|
this.nodes = this._resolveNodes(nodes);
|
|
58
73
|
this.start = this._resolveStart(start);
|
|
59
74
|
this.sessionManager = sessionManager;
|
|
@@ -117,47 +132,106 @@ export class Swarm {
|
|
|
117
132
|
// Shared by reference across every node so mutations in one node's agent
|
|
118
133
|
// are visible to the next.
|
|
119
134
|
const invocationState = options?.invocationState ?? {};
|
|
120
|
-
|
|
135
|
+
// Hook invocation lives in `_stream` so hook-raised `InterruptError`s land in the
|
|
136
|
+
// same frame as the execution loop.
|
|
137
|
+
const gen = this._stream(input, invocationState, options?.cancelSignal);
|
|
121
138
|
let next = await gen.next();
|
|
122
139
|
while (!next.done) {
|
|
123
|
-
if (next.value instanceof HookableEvent) {
|
|
124
|
-
await this._hookRegistry.invokeCallbacks(next.value);
|
|
125
|
-
}
|
|
126
140
|
yield next.value;
|
|
127
141
|
next = await gen.next();
|
|
128
142
|
}
|
|
129
143
|
return next.value;
|
|
130
144
|
}
|
|
131
|
-
async *_stream(input, invocationState) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
async *_stream(input, invocationState, externalCancelSignal) {
|
|
146
|
+
// Reuse state from a prior INTERRUPTED run so `swarm.invoke(responses)` can
|
|
147
|
+
// resume on the same instance without a SessionManager.
|
|
148
|
+
const state = this._pendingInterruptState ??
|
|
149
|
+
new MultiAgentState({
|
|
150
|
+
nodeIds: [...this.nodes.keys()],
|
|
151
|
+
});
|
|
152
|
+
delete this._pendingInterruptState;
|
|
135
153
|
const multiAgentSpan = this._tracer.startMultiAgentSpan({
|
|
136
154
|
orchestratorId: this.id,
|
|
137
155
|
orchestratorType: 'swarm',
|
|
138
156
|
input,
|
|
139
157
|
});
|
|
140
158
|
// SessionManager (or plugins) may restore state.results here via the hook
|
|
141
|
-
yield new BeforeMultiAgentInvocationEvent({ orchestrator: this, state, invocationState });
|
|
142
|
-
// Resume
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
159
|
+
yield* this._emit(new BeforeMultiAgentInvocationEvent({ orchestrator: this, state, invocationState }));
|
|
160
|
+
// Resume input bypasses handoff-derived resume (goes straight to the interrupted
|
|
161
|
+
// node). On fresh runs, stash the input for replay if a hook-gate pauses before
|
|
162
|
+
// the node runs.
|
|
163
|
+
const resumeResponses = extractResumeResponses(input);
|
|
164
|
+
const interruptResponsesByNode = resumeResponses ? groupInterruptResponsesByNode(resumeResponses, state) : undefined;
|
|
165
|
+
if (!resumeResponses) {
|
|
166
|
+
state._pendingInput = input;
|
|
167
|
+
}
|
|
168
|
+
let node;
|
|
169
|
+
let handoff;
|
|
170
|
+
let nextInput = input;
|
|
171
|
+
if (interruptResponsesByNode) {
|
|
172
|
+
// Swarm runs sequentially, so at most one node can be INTERRUPTED per run.
|
|
173
|
+
// Assert the invariant so a future change that accidentally produces multiple
|
|
174
|
+
// interrupted nodes surfaces loudly rather than silently taking the first.
|
|
175
|
+
if (interruptResponsesByNode.size > 1) {
|
|
176
|
+
throw new Error(`swarm_id=<${this.id}>, interrupted_nodes=<${[...interruptResponsesByNode.keys()].join(',')}> | swarm cannot have multiple interrupted nodes simultaneously`);
|
|
177
|
+
}
|
|
178
|
+
const entry = interruptResponsesByNode.entries().next().value;
|
|
179
|
+
if (!entry)
|
|
180
|
+
throw new Error(`swarm_id=<${this.id}> | no interrupt responses to route`);
|
|
181
|
+
const [nodeId, responses] = entry;
|
|
182
|
+
const resolvedNode = this.nodes.get(nodeId);
|
|
183
|
+
if (!resolvedNode) {
|
|
184
|
+
throw new Error(`node_id=<${nodeId}>, swarm_id=<${this.id}> | resume response targets a node missing from the swarm; topology changed between save and resume?`);
|
|
185
|
+
}
|
|
186
|
+
node = resolvedNode;
|
|
187
|
+
const resolvedNodeState = state.node(nodeId);
|
|
188
|
+
if (!resolvedNodeState) {
|
|
189
|
+
throw new Error(`node_id=<${nodeId}>, swarm_id=<${this.id}> | routed interrupt response targets a node missing from state; topology changed between save and resume?`);
|
|
190
|
+
}
|
|
191
|
+
// Orchestrator hooks consume matching responses; leftovers go to the child
|
|
192
|
+
// agent. If the hook consumed everything, replay the original invocation input.
|
|
193
|
+
const forwarded = applyOrchestratorHookResponses(resolvedNodeState, responses);
|
|
194
|
+
nextInput = forwarded.length > 0 ? forwarded : (state._pendingInput ?? '');
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
const resumeNode = this._findResumeNode(state);
|
|
198
|
+
node = resumeNode?.node ?? this.start;
|
|
199
|
+
handoff = resumeNode?.lastHandoff;
|
|
200
|
+
}
|
|
146
201
|
let caughtError;
|
|
147
202
|
let result;
|
|
203
|
+
// Swarm-level timeout composes with each node's signal so a hung node still gets
|
|
204
|
+
// aborted. Timer starts fresh per invocation; human response time between resumes
|
|
205
|
+
// is not deducted.
|
|
206
|
+
const execController = Number.isFinite(this.config.timeout) ? new AbortController() : undefined;
|
|
207
|
+
const execTimeoutHandle = execController ? setTimeout(() => execController.abort(), this.config.timeout) : undefined;
|
|
208
|
+
const nodeCancelSignal = execController && externalCancelSignal
|
|
209
|
+
? AbortSignal.any([execController.signal, externalCancelSignal])
|
|
210
|
+
: (execController?.signal ?? externalCancelSignal);
|
|
148
211
|
try {
|
|
149
212
|
while (state.steps < this.config.maxSteps) {
|
|
213
|
+
if (execController?.signal.aborted) {
|
|
214
|
+
throw new Error(`timeout=<${this.config.timeout}>, swarm_id=<${this.id}> | swarm exceeded wall-clock budget`);
|
|
215
|
+
}
|
|
216
|
+
if (externalCancelSignal?.aborted) {
|
|
217
|
+
throw new Error(`swarm_id=<${this.id}> | swarm cancelled by external signal`);
|
|
218
|
+
}
|
|
150
219
|
state.steps++;
|
|
151
|
-
//
|
|
152
|
-
|
|
220
|
+
// After the first step (which may use routed resume responses), revert to the
|
|
221
|
+
// original input so post-handoff nodes see fresh content.
|
|
222
|
+
const nodeResult = yield* this._streamNode(node, nextInput, state, handoff, multiAgentSpan, invocationState, nodeCancelSignal);
|
|
223
|
+
nextInput = input;
|
|
153
224
|
handoff = nodeResult.structuredOutput;
|
|
225
|
+
if (execController?.signal.aborted) {
|
|
226
|
+
throw new Error(`timeout=<${this.config.timeout}>, swarm_id=<${this.id}>, node_id=<${node.id}> | swarm exceeded wall-clock budget during node execution`);
|
|
227
|
+
}
|
|
154
228
|
// Check for terminal conditions
|
|
155
|
-
if (nodeResult.status === Status.FAILED || !handoff?.agentId) {
|
|
229
|
+
if (nodeResult.status === Status.FAILED || nodeResult.status === Status.INTERRUPTED || !handoff?.agentId) {
|
|
156
230
|
break;
|
|
157
231
|
}
|
|
158
232
|
// Hand off to next agent
|
|
159
233
|
const target = this.nodes.get(handoff.agentId);
|
|
160
|
-
yield new MultiAgentHandoffEvent({ source: node.id, targets: [target.id], state, invocationState });
|
|
234
|
+
yield* this._emit(new MultiAgentHandoffEvent({ source: node.id, targets: [target.id], state, invocationState }));
|
|
161
235
|
logger.debug(`source=<${node.id}>, target=<${target.id}> | swarm handoff`);
|
|
162
236
|
node = target;
|
|
163
237
|
}
|
|
@@ -167,70 +241,123 @@ export class Swarm {
|
|
|
167
241
|
content: this._resolveContent(state),
|
|
168
242
|
duration: Date.now() - state.startTime,
|
|
169
243
|
});
|
|
244
|
+
// Stash on interrupt so same-instance resume has state; otherwise start fresh.
|
|
245
|
+
if (result.status === Status.INTERRUPTED) {
|
|
246
|
+
this._pendingInterruptState = state;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
delete this._pendingInterruptState;
|
|
250
|
+
delete state._pendingInput;
|
|
251
|
+
}
|
|
170
252
|
}
|
|
171
253
|
catch (error) {
|
|
172
254
|
caughtError = normalizeError(error);
|
|
173
255
|
throw caughtError;
|
|
174
256
|
}
|
|
175
257
|
finally {
|
|
258
|
+
if (execTimeoutHandle !== undefined)
|
|
259
|
+
clearTimeout(execTimeoutHandle);
|
|
176
260
|
this._tracer.endMultiAgentSpan(multiAgentSpan, {
|
|
177
261
|
duration: Date.now() - state.startTime,
|
|
178
262
|
...(result && { usage: result.usage }),
|
|
179
263
|
...(caughtError && { error: caughtError }),
|
|
180
264
|
});
|
|
181
|
-
yield new AfterMultiAgentInvocationEvent({ orchestrator: this, state, invocationState });
|
|
265
|
+
yield* this._emit(new AfterMultiAgentInvocationEvent({ orchestrator: this, state, invocationState }));
|
|
182
266
|
}
|
|
183
|
-
yield new MultiAgentResultEvent({ result, invocationState });
|
|
267
|
+
yield* this._emit(new MultiAgentResultEvent({ result, invocationState }));
|
|
184
268
|
return result;
|
|
185
269
|
}
|
|
186
|
-
|
|
270
|
+
/** Invokes hook callbacks on an event, then yields it. */
|
|
271
|
+
async *_emit(event) {
|
|
272
|
+
await this._hookRegistry.invokeCallbacks(event);
|
|
273
|
+
yield event;
|
|
274
|
+
}
|
|
275
|
+
async *_streamNode(node, input, state, handoff, multiAgentSpan, invocationState, executionSignal) {
|
|
187
276
|
const nodeState = state.node(node.id);
|
|
188
277
|
const handoffSchema = this._buildHandoffSchema(node.id);
|
|
189
278
|
const nodeSpan = this._tracer.withSpanContext(multiAgentSpan, () => this._tracer.startNodeSpan({ nodeId: node.id, nodeType: node.type }));
|
|
190
279
|
const beforeEvent = new BeforeNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState });
|
|
280
|
+
try {
|
|
281
|
+
await this._hookRegistry.invokeCallbacks(beforeEvent);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
if (error instanceof InterruptError) {
|
|
285
|
+
const result = recordHookInterrupt(node.id, nodeState);
|
|
286
|
+
state.results.push(result);
|
|
287
|
+
yield beforeEvent;
|
|
288
|
+
yield* this._emit(new NodeResultEvent({ nodeId: node.id, nodeType: node.type, state, result, invocationState }));
|
|
289
|
+
yield* this._emit(new AfterNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState }));
|
|
290
|
+
this._tracer.endNodeSpan(nodeSpan, { status: Status.INTERRUPTED, duration: result.duration });
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
191
295
|
yield beforeEvent;
|
|
192
296
|
if (beforeEvent.cancel) {
|
|
193
297
|
const message = typeof beforeEvent.cancel === 'string' ? beforeEvent.cancel : 'node cancelled by hook';
|
|
298
|
+
// Cancel path doesn't go through Node.stream, so do its INTERRUPTED cleanup here.
|
|
299
|
+
dropStaleInterruptedResult(node.id, nodeState, state);
|
|
194
300
|
const result = new NodeResult({ nodeId: node.id, status: Status.CANCELLED, duration: 0 });
|
|
195
301
|
nodeState.status = Status.CANCELLED;
|
|
196
302
|
nodeState.results.push(result);
|
|
197
303
|
state.results.push(result);
|
|
198
|
-
yield new NodeCancelEvent({ nodeId: node.id, state, message, invocationState });
|
|
199
|
-
yield new AfterNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState });
|
|
304
|
+
yield* this._emit(new NodeCancelEvent({ nodeId: node.id, state, message, invocationState }));
|
|
305
|
+
yield* this._emit(new AfterNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState }));
|
|
200
306
|
this._tracer.endNodeSpan(nodeSpan, { status: Status.CANCELLED, duration: 0 });
|
|
201
307
|
return result;
|
|
202
308
|
}
|
|
203
309
|
const nodeInput = this._resolveNodeInput(input, handoff);
|
|
310
|
+
const nodeTimeout = node.timeout ?? this.config.nodeTimeout;
|
|
311
|
+
const timeoutController = Number.isFinite(nodeTimeout) ? new AbortController() : undefined;
|
|
312
|
+
const timeoutHandle = timeoutController ? setTimeout(() => timeoutController.abort(), nodeTimeout) : undefined;
|
|
313
|
+
const signals = [executionSignal, timeoutController?.signal].filter((s) => s !== undefined);
|
|
314
|
+
const cancelSignal = signals.length > 0 ? AbortSignal.any(signals) : undefined;
|
|
204
315
|
try {
|
|
205
|
-
const gen = this._tracer.withSpanContext(nodeSpan, () => node.stream(nodeInput, state, {
|
|
316
|
+
const gen = this._tracer.withSpanContext(nodeSpan, () => node.stream(nodeInput, state, {
|
|
317
|
+
structuredOutputSchema: handoffSchema,
|
|
318
|
+
invocationState,
|
|
319
|
+
...(cancelSignal && { cancelSignal }),
|
|
320
|
+
}));
|
|
206
321
|
let next = await this._tracer.withSpanContext(nodeSpan, () => gen.next());
|
|
207
322
|
while (!next.done) {
|
|
208
|
-
|
|
323
|
+
if (next.value instanceof HookableEvent) {
|
|
324
|
+
yield* this._emit(next.value);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
yield next.value;
|
|
328
|
+
}
|
|
209
329
|
next = await this._tracer.withSpanContext(nodeSpan, () => gen.next());
|
|
210
330
|
}
|
|
331
|
+
if (timeoutController?.signal.aborted) {
|
|
332
|
+
throw new Error(`node_timeout=<${nodeTimeout}>, node_id=<${node.id}>, swarm_id=<${this.id}> | node exceeded wall-clock budget`);
|
|
333
|
+
}
|
|
211
334
|
const result = next.value;
|
|
212
335
|
this._tracer.endNodeSpan(nodeSpan, { status: result.status, duration: result.duration, usage: result.usage });
|
|
213
336
|
state.results.push(result);
|
|
214
|
-
yield new AfterNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState });
|
|
337
|
+
yield* this._emit(new AfterNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState }));
|
|
215
338
|
return result;
|
|
216
339
|
}
|
|
217
340
|
catch (error) {
|
|
218
341
|
const nodeError = normalizeError(error);
|
|
219
342
|
this._tracer.endNodeSpan(nodeSpan, { error: nodeError });
|
|
220
|
-
yield new AfterNodeCallEvent({
|
|
221
|
-
orchestrator: this,
|
|
222
|
-
state,
|
|
223
|
-
nodeId: node.id,
|
|
224
|
-
invocationState,
|
|
225
|
-
error: nodeError,
|
|
226
|
-
});
|
|
343
|
+
yield* this._emit(new AfterNodeCallEvent({ orchestrator: this, state, nodeId: node.id, invocationState, error: nodeError }));
|
|
227
344
|
throw nodeError;
|
|
228
345
|
}
|
|
346
|
+
finally {
|
|
347
|
+
if (timeoutHandle !== undefined)
|
|
348
|
+
clearTimeout(timeoutHandle);
|
|
349
|
+
}
|
|
229
350
|
}
|
|
230
351
|
_validateConfig() {
|
|
231
352
|
if (this.config.maxSteps < 1) {
|
|
232
353
|
throw new Error(`max_steps=<${this.config.maxSteps}> | must be at least 1`);
|
|
233
354
|
}
|
|
355
|
+
if (this.config.timeout < 1) {
|
|
356
|
+
throw new Error(`timeout=<${this.config.timeout}> | must be at least 1`);
|
|
357
|
+
}
|
|
358
|
+
if (this.config.nodeTimeout < 1) {
|
|
359
|
+
throw new Error(`node_timeout=<${this.config.nodeTimeout}> | must be at least 1`);
|
|
360
|
+
}
|
|
234
361
|
}
|
|
235
362
|
_resolveNodes(definitions) {
|
|
236
363
|
if (definitions.length === 0) {
|
|
@@ -265,6 +392,12 @@ export class Swarm {
|
|
|
265
392
|
}
|
|
266
393
|
return [...last.content];
|
|
267
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Builds the input for the next node after a handoff, or returns the input as-is
|
|
397
|
+
* when there is no handoff (initial or resume invocation). The caller passes the
|
|
398
|
+
* original `MultiAgentInput` through; resume responses flow through here untouched
|
|
399
|
+
* so the underlying agent sees them directly.
|
|
400
|
+
*/
|
|
268
401
|
_resolveNodeInput(input, handoff) {
|
|
269
402
|
if (!handoff)
|
|
270
403
|
return input;
|