@providerprotocol/agents 0.0.2 → 0.0.3
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/dist/checkpoint/index.d.ts +43 -0
- package/dist/checkpoint/index.js +64 -0
- package/dist/checkpoint/index.js.map +1 -0
- package/{src/execution/loop.ts → dist/chunk-4ESYN66B.js} +54 -162
- package/dist/chunk-4ESYN66B.js.map +1 -0
- package/dist/chunk-EKRXMSDX.js +8 -0
- package/dist/chunk-EKRXMSDX.js.map +1 -0
- package/dist/chunk-PHI5ULBV.js +427 -0
- package/dist/chunk-PHI5ULBV.js.map +1 -0
- package/dist/execution/index.d.ts +105 -0
- package/dist/execution/index.js +679 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/index-qsPwbY86.d.ts +65 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +218 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.d.ts +23 -0
- package/dist/middleware/index.js +82 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/thread-tree/index.d.ts +115 -0
- package/dist/thread-tree/index.js +4 -0
- package/dist/thread-tree/index.js.map +1 -0
- package/dist/types-2Vsthzyu.d.ts +163 -0
- package/dist/types-BhX9uD_d.d.ts +91 -0
- package/dist/types-DR02gtFv.d.ts +270 -0
- package/dist/types-NGQMdnaD.d.ts +65 -0
- package/package.json +40 -8
- package/.claude/settings.local.json +0 -29
- package/AGENTS.md +0 -681
- package/CLAUDE.md +0 -681
- package/bun.lock +0 -472
- package/eslint.config.js +0 -75
- package/index.ts +0 -1
- package/llms.md +0 -796
- package/specs/UAP-1.0.md +0 -2355
- package/src/agent/index.ts +0 -384
- package/src/agent/types.ts +0 -91
- package/src/checkpoint/file.ts +0 -126
- package/src/checkpoint/index.ts +0 -40
- package/src/checkpoint/types.ts +0 -95
- package/src/execution/index.ts +0 -37
- package/src/execution/plan.ts +0 -497
- package/src/execution/react.ts +0 -340
- package/src/execution/tool-ordering.ts +0 -186
- package/src/execution/types.ts +0 -315
- package/src/index.ts +0 -80
- package/src/middleware/index.ts +0 -7
- package/src/middleware/logging.ts +0 -123
- package/src/middleware/types.ts +0 -69
- package/src/state/index.ts +0 -301
- package/src/state/types.ts +0 -173
- package/src/thread-tree/index.ts +0 -249
- package/src/thread-tree/types.ts +0 -29
- package/src/utils/uuid.ts +0 -7
- package/tests/live/agent-anthropic.test.ts +0 -288
- package/tests/live/agent-strategy-hooks.test.ts +0 -268
- package/tests/live/checkpoint.test.ts +0 -243
- package/tests/live/execution-strategies.test.ts +0 -255
- package/tests/live/plan-strategy.test.ts +0 -160
- package/tests/live/subagent-events.live.test.ts +0 -249
- package/tests/live/thread-tree.test.ts +0 -186
- package/tests/unit/agent.test.ts +0 -703
- package/tests/unit/checkpoint.test.ts +0 -232
- package/tests/unit/execution/equivalence.test.ts +0 -402
- package/tests/unit/execution/loop.test.ts +0 -437
- package/tests/unit/execution/plan.test.ts +0 -590
- package/tests/unit/execution/react.test.ts +0 -604
- package/tests/unit/execution/subagent-events.test.ts +0 -235
- package/tests/unit/execution/tool-ordering.test.ts +0 -310
- package/tests/unit/middleware/logging.test.ts +0 -276
- package/tests/unit/state.test.ts +0 -573
- package/tests/unit/thread-tree.test.ts +0 -249
- package/tsconfig.json +0 -29
package/src/checkpoint/types.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checkpoint Types
|
|
3
|
-
*
|
|
4
|
-
* Type definitions for step-level persistence and session resume.
|
|
5
|
-
*
|
|
6
|
-
* @see UAP-1.0 Spec Section 12.4
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { AgentStateJSON } from '../state/types.ts';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Checkpoint store interface.
|
|
13
|
-
*
|
|
14
|
-
* Implementations handle persistence of agent state at step boundaries
|
|
15
|
-
* for crash recovery and session resume.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const store = fileCheckpoints({ dir: './checkpoints' });
|
|
20
|
-
*
|
|
21
|
-
* // Save checkpoint
|
|
22
|
-
* await store.save('session-123', state.toJSON());
|
|
23
|
-
*
|
|
24
|
-
* // Load checkpoint
|
|
25
|
-
* const saved = await store.load('session-123');
|
|
26
|
-
* if (saved) {
|
|
27
|
-
* const restored = AgentState.fromJSON(saved);
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export interface CheckpointStore {
|
|
32
|
-
/**
|
|
33
|
-
* Save a checkpoint at the current state.
|
|
34
|
-
*
|
|
35
|
-
* @param sessionId - Session identifier
|
|
36
|
-
* @param state - Serialized agent state
|
|
37
|
-
*/
|
|
38
|
-
save(sessionId: string, state: AgentStateJSON): Promise<void>;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Load the most recent checkpoint for a session.
|
|
42
|
-
*
|
|
43
|
-
* @param sessionId - Session identifier
|
|
44
|
-
* @returns Serialized state or null if not found
|
|
45
|
-
*/
|
|
46
|
-
load(sessionId: string): Promise<AgentStateJSON | null>;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Delete all checkpoints for a session.
|
|
50
|
-
*
|
|
51
|
-
* @param sessionId - Session identifier
|
|
52
|
-
*/
|
|
53
|
-
delete(sessionId: string): Promise<void>;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* List all session IDs with checkpoints.
|
|
57
|
-
*
|
|
58
|
-
* @returns Array of session IDs
|
|
59
|
-
*/
|
|
60
|
-
list(): Promise<string[]>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Options for file-based checkpoint store.
|
|
65
|
-
*/
|
|
66
|
-
export interface FileCheckpointOptions {
|
|
67
|
-
/** Directory for checkpoint files. Default: ".checkpoints" */
|
|
68
|
-
dir?: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Checkpoint metadata stored alongside state.
|
|
73
|
-
*/
|
|
74
|
-
export interface CheckpointMetadata {
|
|
75
|
-
/** Session identifier */
|
|
76
|
-
sessionId: string;
|
|
77
|
-
/** Unique checkpoint ID */
|
|
78
|
-
checkpointId: string;
|
|
79
|
-
/** ISO 8601 timestamp */
|
|
80
|
-
timestamp: string;
|
|
81
|
-
/** Step number at checkpoint */
|
|
82
|
-
step: number;
|
|
83
|
-
/** Agent instance ID */
|
|
84
|
-
agentId: string;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Full checkpoint data including state and metadata.
|
|
89
|
-
*/
|
|
90
|
-
export interface CheckpointData {
|
|
91
|
-
/** Checkpoint metadata */
|
|
92
|
-
metadata: CheckpointMetadata;
|
|
93
|
-
/** Serialized agent state */
|
|
94
|
-
state: AgentStateJSON;
|
|
95
|
-
}
|
package/src/execution/index.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export { loop } from './loop.ts';
|
|
2
|
-
export { react } from './react.ts';
|
|
3
|
-
export { plan } from './plan.ts';
|
|
4
|
-
|
|
5
|
-
// Tool ordering utilities
|
|
6
|
-
export {
|
|
7
|
-
orderToolCalls,
|
|
8
|
-
hasToolDependencies,
|
|
9
|
-
hasCallDependencies,
|
|
10
|
-
} from './tool-ordering.ts';
|
|
11
|
-
export type { ExecutionGroup } from './tool-ordering.ts';
|
|
12
|
-
|
|
13
|
-
export type {
|
|
14
|
-
ExecutionStrategy,
|
|
15
|
-
ExecutionContext,
|
|
16
|
-
ExecutionResult,
|
|
17
|
-
LoopOptions,
|
|
18
|
-
ReactOptions,
|
|
19
|
-
PlanOptions,
|
|
20
|
-
AgentStrategy,
|
|
21
|
-
GenerateResult,
|
|
22
|
-
AgentStreamResult,
|
|
23
|
-
AgentStreamEvent,
|
|
24
|
-
UAPEventType,
|
|
25
|
-
// Tool dependency types (Section 8.5)
|
|
26
|
-
ToolDependencyOptions,
|
|
27
|
-
ToolWithDependencies,
|
|
28
|
-
OrderedToolCall,
|
|
29
|
-
// Sub-agent event types (Section 8.7)
|
|
30
|
-
SubagentEventType,
|
|
31
|
-
SubagentEventBase,
|
|
32
|
-
SubagentStartEvent,
|
|
33
|
-
SubagentInnerEvent,
|
|
34
|
-
SubagentEndEvent,
|
|
35
|
-
SubagentEvent,
|
|
36
|
-
OnSubagentEvent,
|
|
37
|
-
} from './types.ts';
|
package/src/execution/plan.ts
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
import type { Turn, StreamEvent } from '@providerprotocol/ai';
|
|
2
|
-
import { UserMessage } from '@providerprotocol/ai';
|
|
3
|
-
import type { PlanStep } from '../state/index.ts';
|
|
4
|
-
import { generateUUID } from '../utils/uuid.ts';
|
|
5
|
-
import type {
|
|
6
|
-
ExecutionStrategy,
|
|
7
|
-
ExecutionContext,
|
|
8
|
-
ExecutionResult,
|
|
9
|
-
PlanOptions,
|
|
10
|
-
AgentStreamResult,
|
|
11
|
-
AgentStreamEvent,
|
|
12
|
-
} from './types.ts';
|
|
13
|
-
|
|
14
|
-
const DEFAULT_PLAN_OPTIONS: Required<PlanOptions> = {
|
|
15
|
-
maxPlanSteps: Infinity,
|
|
16
|
-
allowReplan: true,
|
|
17
|
-
planSchema: {
|
|
18
|
-
type: 'object',
|
|
19
|
-
properties: {
|
|
20
|
-
steps: {
|
|
21
|
-
type: 'array',
|
|
22
|
-
items: {
|
|
23
|
-
type: 'object',
|
|
24
|
-
properties: {
|
|
25
|
-
id: { type: 'string', description: 'Unique step identifier' },
|
|
26
|
-
description: { type: 'string', description: 'What this step does' },
|
|
27
|
-
tool: { type: 'string', description: 'Tool to use (if applicable)' },
|
|
28
|
-
dependsOn: {
|
|
29
|
-
type: 'array',
|
|
30
|
-
items: { type: 'string' },
|
|
31
|
-
description: 'IDs of steps this depends on',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
required: ['id', 'description', 'dependsOn'],
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
required: ['steps'],
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const PLAN_PROMPT = `Create a detailed execution plan to accomplish the task.
|
|
43
|
-
Break it down into clear steps, specifying which tool to use for each step if applicable.
|
|
44
|
-
Include dependencies between steps (which steps must complete before others can start).
|
|
45
|
-
Return your plan as a JSON object with a "steps" array.`;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Create a plan-then-execute strategy.
|
|
49
|
-
*
|
|
50
|
-
* Behavior:
|
|
51
|
-
* 1. Plan: LLM generates structured plan with steps and dependencies
|
|
52
|
-
* 2. Execute: Execute each plan step respecting dependency order
|
|
53
|
-
* 3. Replan: If a step fails and allowReplan is true, generate new plan
|
|
54
|
-
*
|
|
55
|
-
* @param options - Plan configuration options
|
|
56
|
-
* @returns ExecutionStrategy
|
|
57
|
-
*/
|
|
58
|
-
export function plan(options: PlanOptions = {}): ExecutionStrategy {
|
|
59
|
-
const opts = { ...DEFAULT_PLAN_OPTIONS, ...options };
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
name: 'plan',
|
|
63
|
-
|
|
64
|
-
async execute(context: ExecutionContext): Promise<ExecutionResult> {
|
|
65
|
-
const { llm, input, state, strategy, signal } = context;
|
|
66
|
-
|
|
67
|
-
// Add input message to state and set agentId in metadata
|
|
68
|
-
// This ensures checkpoints include the full conversation
|
|
69
|
-
let currentState = state
|
|
70
|
-
.withMessage(input)
|
|
71
|
-
.withMetadata('agentId', context.agent.id);
|
|
72
|
-
let step = 0;
|
|
73
|
-
let finalTurn: Turn | undefined;
|
|
74
|
-
|
|
75
|
-
// Messages for LLM generation (includes input we just added)
|
|
76
|
-
const messages = [...currentState.messages];
|
|
77
|
-
|
|
78
|
-
// PLANNING PHASE
|
|
79
|
-
step++;
|
|
80
|
-
currentState = currentState.withStep(step);
|
|
81
|
-
|
|
82
|
-
if (signal?.aborted) {
|
|
83
|
-
throw new Error('Execution aborted');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
strategy.onStepStart?.(step, currentState);
|
|
87
|
-
|
|
88
|
-
// Generate the plan
|
|
89
|
-
const planMessages = [
|
|
90
|
-
...messages,
|
|
91
|
-
new UserMessage(PLAN_PROMPT),
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
const planTurn = await llm.generate(planMessages);
|
|
95
|
-
|
|
96
|
-
// Parse the plan from the response
|
|
97
|
-
let planData: { steps: Array<{ id: string; description: string; tool?: string; dependsOn: string[] }> };
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
if (planTurn.data) {
|
|
101
|
-
planData = planTurn.data as typeof planData;
|
|
102
|
-
} else {
|
|
103
|
-
// Try to parse from text
|
|
104
|
-
const jsonMatch = planTurn.response.text.match(/\{[\s\S]*\}/);
|
|
105
|
-
if (jsonMatch) {
|
|
106
|
-
planData = JSON.parse(jsonMatch[0]) as typeof planData;
|
|
107
|
-
} else {
|
|
108
|
-
throw new Error('Could not parse plan from response');
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
} catch (err) {
|
|
112
|
-
throw new Error(`Failed to parse execution plan: ${err instanceof Error ? err.message : String(err)}`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Convert to PlanStep format
|
|
116
|
-
let planSteps: PlanStep[] = planData.steps.map((s) => ({
|
|
117
|
-
id: s.id || generateUUID(),
|
|
118
|
-
description: s.description,
|
|
119
|
-
tool: s.tool,
|
|
120
|
-
dependsOn: s.dependsOn || [],
|
|
121
|
-
status: 'pending' as const,
|
|
122
|
-
}));
|
|
123
|
-
|
|
124
|
-
// Apply maxPlanSteps limit
|
|
125
|
-
if (opts.maxPlanSteps !== Infinity && planSteps.length > opts.maxPlanSteps) {
|
|
126
|
-
planSteps = planSteps.slice(0, opts.maxPlanSteps);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
currentState = currentState.withPlan(planSteps);
|
|
130
|
-
messages.push(...planTurn.messages);
|
|
131
|
-
|
|
132
|
-
strategy.onStepEnd?.(step, { turn: planTurn, state: currentState });
|
|
133
|
-
|
|
134
|
-
// EXECUTION PHASE
|
|
135
|
-
const completedSteps = new Set<string>();
|
|
136
|
-
|
|
137
|
-
// Execute steps in topological order
|
|
138
|
-
while (planSteps.some((s) => s.status === 'pending')) {
|
|
139
|
-
// Find next executable step (all dependencies completed)
|
|
140
|
-
const nextStep = planSteps.find(
|
|
141
|
-
(s) => s.status === 'pending'
|
|
142
|
-
&& s.dependsOn.every((depId) => completedSteps.has(depId)),
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
if (!nextStep) {
|
|
146
|
-
// No step can be executed - either done or cyclic dependency
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
step++;
|
|
151
|
-
currentState = currentState.withStep(step);
|
|
152
|
-
|
|
153
|
-
if (signal?.aborted) {
|
|
154
|
-
throw new Error('Execution aborted');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
strategy.onStepStart?.(step, currentState);
|
|
158
|
-
|
|
159
|
-
// Update step status to in_progress
|
|
160
|
-
nextStep.status = 'in_progress';
|
|
161
|
-
currentState = currentState.withPlan([...planSteps]);
|
|
162
|
-
|
|
163
|
-
// Execute the step
|
|
164
|
-
const stepPrompt = new UserMessage(
|
|
165
|
-
`Execute step "${nextStep.id}": ${nextStep.description}${nextStep.tool ? ` using the ${nextStep.tool} tool` : ''}`,
|
|
166
|
-
);
|
|
167
|
-
messages.push(stepPrompt);
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const stepTurn = await llm.generate(messages);
|
|
171
|
-
finalTurn = stepTurn;
|
|
172
|
-
|
|
173
|
-
messages.push(...stepTurn.messages);
|
|
174
|
-
currentState = currentState.withMessages(stepTurn.messages);
|
|
175
|
-
|
|
176
|
-
if (stepTurn.response.hasToolCalls) {
|
|
177
|
-
strategy.onAct?.(step, stepTurn.response.toolCalls ?? []);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (stepTurn.toolExecutions && stepTurn.toolExecutions.length > 0) {
|
|
181
|
-
strategy.onObserve?.(step, stepTurn.toolExecutions);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Mark step as completed
|
|
185
|
-
nextStep.status = 'completed';
|
|
186
|
-
completedSteps.add(nextStep.id);
|
|
187
|
-
currentState = currentState.withPlan([...planSteps]);
|
|
188
|
-
|
|
189
|
-
strategy.onStepEnd?.(step, { turn: stepTurn, state: currentState });
|
|
190
|
-
} catch (err) {
|
|
191
|
-
nextStep.status = 'failed';
|
|
192
|
-
currentState = currentState.withPlan([...planSteps]);
|
|
193
|
-
|
|
194
|
-
if (opts.allowReplan) {
|
|
195
|
-
// Could implement replanning here
|
|
196
|
-
// For now, just continue and let the error propagate
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
throw err;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Check stop condition
|
|
203
|
-
const shouldStop = await strategy.stopCondition?.(currentState);
|
|
204
|
-
if (shouldStop) {
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (!finalTurn) {
|
|
210
|
-
finalTurn = planTurn; // Use plan turn if no execution happened
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Include sessionId in state metadata if checkpointing is enabled
|
|
214
|
-
let finalState = currentState;
|
|
215
|
-
if (context.sessionId) {
|
|
216
|
-
finalState = currentState.withMetadata('sessionId', context.sessionId);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const result: ExecutionResult = {
|
|
220
|
-
turn: finalTurn,
|
|
221
|
-
state: finalState,
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
strategy.onComplete?.(result);
|
|
225
|
-
|
|
226
|
-
return result;
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
stream(context: ExecutionContext): AgentStreamResult {
|
|
230
|
-
const { llm, input, state, strategy, signal } = context;
|
|
231
|
-
const agentId = context.agent.id;
|
|
232
|
-
|
|
233
|
-
let aborted = false;
|
|
234
|
-
const abortController = new AbortController();
|
|
235
|
-
|
|
236
|
-
if (signal) {
|
|
237
|
-
signal.addEventListener('abort', () => abortController.abort());
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let resolveResult: (result: ExecutionResult) => void;
|
|
241
|
-
let rejectResult: (error: Error) => void;
|
|
242
|
-
|
|
243
|
-
const resultPromise = new Promise<ExecutionResult>((resolve, reject) => {
|
|
244
|
-
resolveResult = resolve;
|
|
245
|
-
rejectResult = reject;
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
async function* generateEvents(): AsyncGenerator<AgentStreamEvent> {
|
|
249
|
-
// Add input message to state and set agentId in metadata
|
|
250
|
-
// This ensures checkpoints include the full conversation
|
|
251
|
-
let currentState = state
|
|
252
|
-
.withMessage(input)
|
|
253
|
-
.withMetadata('agentId', context.agent.id);
|
|
254
|
-
let step = 0;
|
|
255
|
-
let finalTurn: Turn | undefined;
|
|
256
|
-
|
|
257
|
-
// Messages for LLM generation (includes input we just added)
|
|
258
|
-
const messages = [...currentState.messages];
|
|
259
|
-
|
|
260
|
-
try {
|
|
261
|
-
// PLANNING PHASE
|
|
262
|
-
step++;
|
|
263
|
-
currentState = currentState.withStep(step);
|
|
264
|
-
|
|
265
|
-
if (abortController.signal.aborted) {
|
|
266
|
-
throw new Error('Execution aborted');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
strategy.onStepStart?.(step, currentState);
|
|
270
|
-
|
|
271
|
-
yield {
|
|
272
|
-
source: 'uap',
|
|
273
|
-
uap: {
|
|
274
|
-
type: 'step_start',
|
|
275
|
-
step,
|
|
276
|
-
agentId,
|
|
277
|
-
data: { phase: 'planning' },
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const planMessages = [
|
|
282
|
-
...messages,
|
|
283
|
-
new UserMessage(PLAN_PROMPT),
|
|
284
|
-
];
|
|
285
|
-
|
|
286
|
-
const planStream = llm.stream(planMessages);
|
|
287
|
-
|
|
288
|
-
for await (const event of planStream as AsyncIterable<StreamEvent>) {
|
|
289
|
-
if (abortController.signal.aborted) {
|
|
290
|
-
throw new Error('Execution aborted');
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
yield { source: 'upp', upp: event };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const planTurn = await planStream.turn;
|
|
297
|
-
|
|
298
|
-
let planData: { steps: Array<{ id: string; description: string; tool?: string; dependsOn: string[] }> };
|
|
299
|
-
|
|
300
|
-
try {
|
|
301
|
-
if (planTurn.data) {
|
|
302
|
-
planData = planTurn.data as typeof planData;
|
|
303
|
-
} else {
|
|
304
|
-
const jsonMatch = planTurn.response.text.match(/\{[\s\S]*\}/);
|
|
305
|
-
if (jsonMatch) {
|
|
306
|
-
planData = JSON.parse(jsonMatch[0]) as typeof planData;
|
|
307
|
-
} else {
|
|
308
|
-
throw new Error('Could not parse plan from response');
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
} catch (err) {
|
|
312
|
-
throw new Error(`Failed to parse execution plan: ${err instanceof Error ? err.message : String(err)}`);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
let planSteps: PlanStep[] = planData.steps.map((s) => ({
|
|
316
|
-
id: s.id || generateUUID(),
|
|
317
|
-
description: s.description,
|
|
318
|
-
tool: s.tool,
|
|
319
|
-
dependsOn: s.dependsOn || [],
|
|
320
|
-
status: 'pending' as const,
|
|
321
|
-
}));
|
|
322
|
-
|
|
323
|
-
if (opts.maxPlanSteps !== Infinity && planSteps.length > opts.maxPlanSteps) {
|
|
324
|
-
planSteps = planSteps.slice(0, opts.maxPlanSteps);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
currentState = currentState.withPlan(planSteps);
|
|
328
|
-
messages.push(...planTurn.messages);
|
|
329
|
-
|
|
330
|
-
yield {
|
|
331
|
-
source: 'uap',
|
|
332
|
-
uap: {
|
|
333
|
-
type: 'plan_created',
|
|
334
|
-
step,
|
|
335
|
-
agentId,
|
|
336
|
-
data: { plan: planSteps },
|
|
337
|
-
},
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
strategy.onStepEnd?.(step, { turn: planTurn, state: currentState });
|
|
341
|
-
|
|
342
|
-
yield {
|
|
343
|
-
source: 'uap',
|
|
344
|
-
uap: {
|
|
345
|
-
type: 'step_end',
|
|
346
|
-
step,
|
|
347
|
-
agentId,
|
|
348
|
-
data: { phase: 'planning' },
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
// EXECUTION PHASE
|
|
353
|
-
const completedSteps = new Set<string>();
|
|
354
|
-
|
|
355
|
-
while (planSteps.some((s) => s.status === 'pending') && !aborted) {
|
|
356
|
-
const nextStep = planSteps.find(
|
|
357
|
-
(s) => s.status === 'pending'
|
|
358
|
-
&& s.dependsOn.every((depId) => completedSteps.has(depId)),
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
if (!nextStep) {
|
|
362
|
-
break;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
step++;
|
|
366
|
-
currentState = currentState.withStep(step);
|
|
367
|
-
|
|
368
|
-
if (abortController.signal.aborted) {
|
|
369
|
-
throw new Error('Execution aborted');
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
strategy.onStepStart?.(step, currentState);
|
|
373
|
-
|
|
374
|
-
yield {
|
|
375
|
-
source: 'uap',
|
|
376
|
-
uap: {
|
|
377
|
-
type: 'plan_step_start',
|
|
378
|
-
step,
|
|
379
|
-
agentId,
|
|
380
|
-
data: { planStep: nextStep },
|
|
381
|
-
},
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
nextStep.status = 'in_progress';
|
|
385
|
-
currentState = currentState.withPlan([...planSteps]);
|
|
386
|
-
|
|
387
|
-
const stepPrompt = new UserMessage(
|
|
388
|
-
`Execute step "${nextStep.id}": ${nextStep.description}${nextStep.tool ? ` using the ${nextStep.tool} tool` : ''}`,
|
|
389
|
-
);
|
|
390
|
-
messages.push(stepPrompt);
|
|
391
|
-
|
|
392
|
-
const stepStream = llm.stream(messages);
|
|
393
|
-
|
|
394
|
-
for await (const event of stepStream as AsyncIterable<StreamEvent>) {
|
|
395
|
-
if (abortController.signal.aborted) {
|
|
396
|
-
throw new Error('Execution aborted');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
yield { source: 'upp', upp: event };
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const stepTurn = await stepStream.turn;
|
|
403
|
-
finalTurn = stepTurn;
|
|
404
|
-
|
|
405
|
-
messages.push(...stepTurn.messages);
|
|
406
|
-
currentState = currentState.withMessages(stepTurn.messages);
|
|
407
|
-
|
|
408
|
-
if (stepTurn.response.hasToolCalls) {
|
|
409
|
-
strategy.onAct?.(step, stepTurn.response.toolCalls ?? []);
|
|
410
|
-
|
|
411
|
-
yield {
|
|
412
|
-
source: 'uap',
|
|
413
|
-
uap: {
|
|
414
|
-
type: 'action',
|
|
415
|
-
step,
|
|
416
|
-
agentId,
|
|
417
|
-
data: { toolCalls: stepTurn.response.toolCalls },
|
|
418
|
-
},
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (stepTurn.toolExecutions && stepTurn.toolExecutions.length > 0) {
|
|
423
|
-
strategy.onObserve?.(step, stepTurn.toolExecutions);
|
|
424
|
-
|
|
425
|
-
yield {
|
|
426
|
-
source: 'uap',
|
|
427
|
-
uap: {
|
|
428
|
-
type: 'observation',
|
|
429
|
-
step,
|
|
430
|
-
agentId,
|
|
431
|
-
data: { observations: stepTurn.toolExecutions },
|
|
432
|
-
},
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
nextStep.status = 'completed';
|
|
437
|
-
completedSteps.add(nextStep.id);
|
|
438
|
-
currentState = currentState.withPlan([...planSteps]);
|
|
439
|
-
|
|
440
|
-
strategy.onStepEnd?.(step, { turn: stepTurn, state: currentState });
|
|
441
|
-
|
|
442
|
-
yield {
|
|
443
|
-
source: 'uap',
|
|
444
|
-
uap: {
|
|
445
|
-
type: 'plan_step_end',
|
|
446
|
-
step,
|
|
447
|
-
agentId,
|
|
448
|
-
data: { planStep: nextStep },
|
|
449
|
-
},
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
const shouldStop = await strategy.stopCondition?.(currentState);
|
|
453
|
-
if (shouldStop) {
|
|
454
|
-
break;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (!finalTurn) {
|
|
459
|
-
finalTurn = planTurn;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Include sessionId in state metadata if checkpointing is enabled
|
|
463
|
-
let finalState = currentState;
|
|
464
|
-
if (context.sessionId) {
|
|
465
|
-
finalState = currentState.withMetadata('sessionId', context.sessionId);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const result: ExecutionResult = {
|
|
469
|
-
turn: finalTurn,
|
|
470
|
-
state: finalState,
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
strategy.onComplete?.(result);
|
|
474
|
-
resolveResult(result);
|
|
475
|
-
} catch (error) {
|
|
476
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
477
|
-
strategy.onError?.(err, currentState);
|
|
478
|
-
rejectResult(err);
|
|
479
|
-
throw err;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const iterator = generateEvents();
|
|
484
|
-
|
|
485
|
-
return {
|
|
486
|
-
[Symbol.asyncIterator]() {
|
|
487
|
-
return iterator;
|
|
488
|
-
},
|
|
489
|
-
result: resultPromise,
|
|
490
|
-
abort() {
|
|
491
|
-
aborted = true;
|
|
492
|
-
abortController.abort();
|
|
493
|
-
},
|
|
494
|
-
};
|
|
495
|
-
},
|
|
496
|
-
};
|
|
497
|
-
}
|