@providerprotocol/agents 0.0.2 → 0.0.4
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/LICENSE +21 -0
- package/dist/checkpoint/index.d.ts +43 -0
- package/dist/checkpoint/index.js +73 -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-T47B3VAF.js +427 -0
- package/dist/chunk-T47B3VAF.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-BiyEVOnf.d.ts +65 -0
- package/dist/types-D1egxttz.d.ts +270 -0
- package/dist/types-DChRdQoX.d.ts +98 -0
- package/package.json +41 -9
- 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
|
@@ -1,590 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
|
2
|
-
import { UserMessage, AssistantMessage } from '@providerprotocol/ai';
|
|
3
|
-
import type { Turn, LLMInstance, Tool, ToolCall, ToolExecution } from '@providerprotocol/ai';
|
|
4
|
-
import { plan } from '../../../src/execution/plan.ts';
|
|
5
|
-
import { AgentState } from '../../../src/state/index.ts';
|
|
6
|
-
import type { ExecutionContext } from '../../../src/execution/types.ts';
|
|
7
|
-
|
|
8
|
-
// Mock Turn factory
|
|
9
|
-
function createMockTurn(options: {
|
|
10
|
-
text?: string;
|
|
11
|
-
toolCalls?: ToolCall[];
|
|
12
|
-
toolExecutions?: ToolExecution[];
|
|
13
|
-
data?: unknown;
|
|
14
|
-
}): Turn {
|
|
15
|
-
const response = new AssistantMessage(options.text ?? 'Hello', options.toolCalls);
|
|
16
|
-
return {
|
|
17
|
-
response,
|
|
18
|
-
messages: [response],
|
|
19
|
-
toolExecutions: options.toolExecutions ?? [],
|
|
20
|
-
data: options.data,
|
|
21
|
-
usage: {
|
|
22
|
-
inputTokens: 10,
|
|
23
|
-
outputTokens: 20,
|
|
24
|
-
totalTokens: 30,
|
|
25
|
-
},
|
|
26
|
-
cycles: 1,
|
|
27
|
-
} as unknown as Turn;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Mock LLM factory
|
|
31
|
-
function createMockLLM(turnSequence: Turn[]): LLMInstance {
|
|
32
|
-
let callIndex = 0;
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
generate: mock(async () => {
|
|
36
|
-
const turn = turnSequence[callIndex] ?? turnSequence[turnSequence.length - 1];
|
|
37
|
-
callIndex++;
|
|
38
|
-
return turn;
|
|
39
|
-
}),
|
|
40
|
-
stream: mock(() => {
|
|
41
|
-
const turn = turnSequence[callIndex] ?? turnSequence[turnSequence.length - 1];
|
|
42
|
-
callIndex++;
|
|
43
|
-
|
|
44
|
-
const events: Array<{ type: string; delta?: { text?: string } }> = [];
|
|
45
|
-
if (turn) {
|
|
46
|
-
events.push({ type: 'text_delta', delta: { text: turn.response.text } });
|
|
47
|
-
events.push({ type: 'message_stop' });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
async *[Symbol.asyncIterator]() {
|
|
52
|
-
for (const event of events) {
|
|
53
|
-
yield event;
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
turn: Promise.resolve(turn),
|
|
57
|
-
};
|
|
58
|
-
}),
|
|
59
|
-
} as unknown as LLMInstance;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Create a plan response turn
|
|
63
|
-
function createPlanTurn(steps: Array<{ id: string; description: string; tool?: string; dependsOn?: string[] }>): Turn {
|
|
64
|
-
const planData = {
|
|
65
|
-
steps: steps.map((s) => ({
|
|
66
|
-
id: s.id,
|
|
67
|
-
description: s.description,
|
|
68
|
-
tool: s.tool,
|
|
69
|
-
dependsOn: s.dependsOn ?? [],
|
|
70
|
-
})),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
return createMockTurn({
|
|
74
|
-
text: JSON.stringify(planData),
|
|
75
|
-
data: planData,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
describe('plan() execution strategy', () => {
|
|
80
|
-
let state: AgentState;
|
|
81
|
-
let input: UserMessage;
|
|
82
|
-
let tools: Tool[];
|
|
83
|
-
|
|
84
|
-
beforeEach(() => {
|
|
85
|
-
state = AgentState.initial();
|
|
86
|
-
input = new UserMessage('Complete this task');
|
|
87
|
-
tools = [];
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('execute()', () => {
|
|
91
|
-
test('returns turn and updated state', async () => {
|
|
92
|
-
const planTurn = createPlanTurn([
|
|
93
|
-
{ id: 'step-1', description: 'First step' },
|
|
94
|
-
]);
|
|
95
|
-
const executionTurn = createMockTurn({ text: 'Step 1 completed.' });
|
|
96
|
-
|
|
97
|
-
const llm = createMockLLM([planTurn, executionTurn]);
|
|
98
|
-
|
|
99
|
-
const strategy = plan();
|
|
100
|
-
const context: ExecutionContext = {
|
|
101
|
-
agent: { id: 'test-agent' },
|
|
102
|
-
llm,
|
|
103
|
-
input,
|
|
104
|
-
state,
|
|
105
|
-
tools,
|
|
106
|
-
strategy: {},
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const result = await strategy.execute(context);
|
|
110
|
-
|
|
111
|
-
expect(result.turn).toBeDefined();
|
|
112
|
-
expect(result.state).toBeDefined();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test('creates plan in first phase', async () => {
|
|
116
|
-
const planTurn = createPlanTurn([
|
|
117
|
-
{ id: 'step-1', description: 'First step' },
|
|
118
|
-
{ id: 'step-2', description: 'Second step', dependsOn: ['step-1'] },
|
|
119
|
-
]);
|
|
120
|
-
const step1Turn = createMockTurn({ text: 'Step 1 done.' });
|
|
121
|
-
const step2Turn = createMockTurn({ text: 'Step 2 done.' });
|
|
122
|
-
|
|
123
|
-
const llm = createMockLLM([planTurn, step1Turn, step2Turn]);
|
|
124
|
-
|
|
125
|
-
const strategy = plan();
|
|
126
|
-
const context: ExecutionContext = {
|
|
127
|
-
agent: { id: 'test-agent' },
|
|
128
|
-
llm,
|
|
129
|
-
input,
|
|
130
|
-
state,
|
|
131
|
-
tools,
|
|
132
|
-
strategy: {},
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const result = await strategy.execute(context);
|
|
136
|
-
|
|
137
|
-
expect(result.state.plan).toBeDefined();
|
|
138
|
-
expect(result.state.plan).toHaveLength(2);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('executes plan steps in order', async () => {
|
|
142
|
-
const planTurn = createPlanTurn([
|
|
143
|
-
{ id: 'step-1', description: 'First step' },
|
|
144
|
-
{ id: 'step-2', description: 'Second step', dependsOn: ['step-1'] },
|
|
145
|
-
]);
|
|
146
|
-
const step1Turn = createMockTurn({ text: 'Step 1 done.' });
|
|
147
|
-
const step2Turn = createMockTurn({ text: 'Step 2 done.' });
|
|
148
|
-
|
|
149
|
-
const llm = createMockLLM([planTurn, step1Turn, step2Turn]);
|
|
150
|
-
|
|
151
|
-
const strategy = plan();
|
|
152
|
-
const context: ExecutionContext = {
|
|
153
|
-
agent: { id: 'test-agent' },
|
|
154
|
-
llm,
|
|
155
|
-
input,
|
|
156
|
-
state,
|
|
157
|
-
tools,
|
|
158
|
-
strategy: {},
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const result = await strategy.execute(context);
|
|
162
|
-
|
|
163
|
-
// 3 calls: planning + step1 + step2
|
|
164
|
-
expect(llm.generate).toHaveBeenCalledTimes(3);
|
|
165
|
-
|
|
166
|
-
// Both steps should be completed
|
|
167
|
-
const completedSteps = result.state.plan?.filter((s) => s.status === 'completed');
|
|
168
|
-
expect(completedSteps).toHaveLength(2);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('respects step dependencies', async () => {
|
|
172
|
-
// step-3 depends on step-1 and step-2
|
|
173
|
-
const planTurn = createPlanTurn([
|
|
174
|
-
{ id: 'step-1', description: 'First step' },
|
|
175
|
-
{ id: 'step-2', description: 'Second step' },
|
|
176
|
-
{ id: 'step-3', description: 'Third step', dependsOn: ['step-1', 'step-2'] },
|
|
177
|
-
]);
|
|
178
|
-
const step1Turn = createMockTurn({ text: 'Step 1.' });
|
|
179
|
-
const step2Turn = createMockTurn({ text: 'Step 2.' });
|
|
180
|
-
const step3Turn = createMockTurn({ text: 'Step 3.' });
|
|
181
|
-
|
|
182
|
-
const llm = createMockLLM([planTurn, step1Turn, step2Turn, step3Turn]);
|
|
183
|
-
|
|
184
|
-
const strategy = plan();
|
|
185
|
-
const context: ExecutionContext = {
|
|
186
|
-
agent: { id: 'test-agent' },
|
|
187
|
-
llm,
|
|
188
|
-
input,
|
|
189
|
-
state,
|
|
190
|
-
tools,
|
|
191
|
-
strategy: {},
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const result = await strategy.execute(context);
|
|
195
|
-
|
|
196
|
-
// All 3 steps + planning = 4 calls
|
|
197
|
-
expect(llm.generate).toHaveBeenCalledTimes(4);
|
|
198
|
-
|
|
199
|
-
// All should be completed
|
|
200
|
-
const completedSteps = result.state.plan?.filter((s) => s.status === 'completed');
|
|
201
|
-
expect(completedSteps).toHaveLength(3);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test('respects maxPlanSteps', async () => {
|
|
205
|
-
const planTurn = createPlanTurn([
|
|
206
|
-
{ id: 'step-1', description: 'First step' },
|
|
207
|
-
{ id: 'step-2', description: 'Second step' },
|
|
208
|
-
{ id: 'step-3', description: 'Third step' },
|
|
209
|
-
{ id: 'step-4', description: 'Fourth step' },
|
|
210
|
-
{ id: 'step-5', description: 'Fifth step' },
|
|
211
|
-
]);
|
|
212
|
-
const stepTurn = createMockTurn({ text: 'Step done.' });
|
|
213
|
-
|
|
214
|
-
const llm = createMockLLM([planTurn, stepTurn, stepTurn, stepTurn, stepTurn, stepTurn]);
|
|
215
|
-
|
|
216
|
-
const strategy = plan({ maxPlanSteps: 3 });
|
|
217
|
-
const context: ExecutionContext = {
|
|
218
|
-
agent: { id: 'test-agent' },
|
|
219
|
-
llm,
|
|
220
|
-
input,
|
|
221
|
-
state,
|
|
222
|
-
tools,
|
|
223
|
-
strategy: {},
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const result = await strategy.execute(context);
|
|
227
|
-
|
|
228
|
-
// Plan should be truncated to 3 steps
|
|
229
|
-
expect(result.state.plan).toHaveLength(3);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test('defaults to Infinity maxPlanSteps', () => {
|
|
233
|
-
const strategy = plan();
|
|
234
|
-
expect(strategy.name).toBe('plan');
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test('parses plan from JSON in response text', async () => {
|
|
238
|
-
// Plan without data field, only in text
|
|
239
|
-
const planText = JSON.stringify({
|
|
240
|
-
steps: [
|
|
241
|
-
{ id: 'step-1', description: 'First step', dependsOn: [] },
|
|
242
|
-
],
|
|
243
|
-
});
|
|
244
|
-
const planTurn = createMockTurn({ text: `Here is the plan: ${planText}` });
|
|
245
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
246
|
-
|
|
247
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
248
|
-
|
|
249
|
-
const strategy = plan();
|
|
250
|
-
const context: ExecutionContext = {
|
|
251
|
-
agent: { id: 'test-agent' },
|
|
252
|
-
llm,
|
|
253
|
-
input,
|
|
254
|
-
state,
|
|
255
|
-
tools,
|
|
256
|
-
strategy: {},
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const result = await strategy.execute(context);
|
|
260
|
-
|
|
261
|
-
expect(result.state.plan).toHaveLength(1);
|
|
262
|
-
expect(result.state.plan?.[0]?.id).toBe('step-1');
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
test('calls onStepStart hook', async () => {
|
|
266
|
-
const planTurn = createPlanTurn([
|
|
267
|
-
{ id: 'step-1', description: 'First step' },
|
|
268
|
-
]);
|
|
269
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
270
|
-
|
|
271
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
272
|
-
const onStepStart = mock(() => {});
|
|
273
|
-
|
|
274
|
-
const strategy = plan();
|
|
275
|
-
const context: ExecutionContext = {
|
|
276
|
-
agent: { id: 'test-agent' },
|
|
277
|
-
llm,
|
|
278
|
-
input,
|
|
279
|
-
state,
|
|
280
|
-
tools,
|
|
281
|
-
strategy: { onStepStart },
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
await strategy.execute(context);
|
|
285
|
-
|
|
286
|
-
// Called for planning step + execution step
|
|
287
|
-
expect(onStepStart).toHaveBeenCalledTimes(2);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
test('calls onStepEnd hook', async () => {
|
|
291
|
-
const planTurn = createPlanTurn([
|
|
292
|
-
{ id: 'step-1', description: 'First step' },
|
|
293
|
-
]);
|
|
294
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
295
|
-
|
|
296
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
297
|
-
const onStepEnd = mock(() => {});
|
|
298
|
-
|
|
299
|
-
const strategy = plan();
|
|
300
|
-
const context: ExecutionContext = {
|
|
301
|
-
agent: { id: 'test-agent' },
|
|
302
|
-
llm,
|
|
303
|
-
input,
|
|
304
|
-
state,
|
|
305
|
-
tools,
|
|
306
|
-
strategy: { onStepEnd },
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
await strategy.execute(context);
|
|
310
|
-
|
|
311
|
-
// Called for planning step + execution step
|
|
312
|
-
expect(onStepEnd).toHaveBeenCalledTimes(2);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
test('calls onAct hook on tool calls', async () => {
|
|
316
|
-
const toolCall: ToolCall = {
|
|
317
|
-
toolCallId: 'call-1',
|
|
318
|
-
toolName: 'test_tool',
|
|
319
|
-
arguments: {},
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const planTurn = createPlanTurn([
|
|
323
|
-
{ id: 'step-1', description: 'Use tool', tool: 'test_tool' },
|
|
324
|
-
]);
|
|
325
|
-
const stepTurn = createMockTurn({
|
|
326
|
-
text: 'Used tool.',
|
|
327
|
-
toolCalls: [toolCall],
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
331
|
-
const onAct = mock(() => {});
|
|
332
|
-
|
|
333
|
-
const strategy = plan();
|
|
334
|
-
const context: ExecutionContext = {
|
|
335
|
-
agent: { id: 'test-agent' },
|
|
336
|
-
llm,
|
|
337
|
-
input,
|
|
338
|
-
state,
|
|
339
|
-
tools,
|
|
340
|
-
strategy: { onAct },
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
await strategy.execute(context);
|
|
344
|
-
|
|
345
|
-
expect(onAct).toHaveBeenCalledWith(2, [toolCall]);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
test('calls onObserve hook on tool executions', async () => {
|
|
349
|
-
const execution: ToolExecution = {
|
|
350
|
-
toolCallId: 'call-1',
|
|
351
|
-
toolName: 'test_tool',
|
|
352
|
-
arguments: {},
|
|
353
|
-
result: 'result',
|
|
354
|
-
duration: 100,
|
|
355
|
-
isError: false,
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
const planTurn = createPlanTurn([
|
|
359
|
-
{ id: 'step-1', description: 'Observe results' },
|
|
360
|
-
]);
|
|
361
|
-
const stepTurn = createMockTurn({
|
|
362
|
-
text: 'Observed.',
|
|
363
|
-
toolExecutions: [execution],
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
367
|
-
const onObserve = mock(() => {});
|
|
368
|
-
|
|
369
|
-
const strategy = plan();
|
|
370
|
-
const context: ExecutionContext = {
|
|
371
|
-
agent: { id: 'test-agent' },
|
|
372
|
-
llm,
|
|
373
|
-
input,
|
|
374
|
-
state,
|
|
375
|
-
tools,
|
|
376
|
-
strategy: { onObserve },
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
await strategy.execute(context);
|
|
380
|
-
|
|
381
|
-
expect(onObserve).toHaveBeenCalledWith(2, [execution]);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test('calls onComplete hook', async () => {
|
|
385
|
-
const planTurn = createPlanTurn([
|
|
386
|
-
{ id: 'step-1', description: 'Only step' },
|
|
387
|
-
]);
|
|
388
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
389
|
-
|
|
390
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
391
|
-
const onComplete = mock(() => {});
|
|
392
|
-
|
|
393
|
-
const strategy = plan();
|
|
394
|
-
const context: ExecutionContext = {
|
|
395
|
-
agent: { id: 'test-agent' },
|
|
396
|
-
llm,
|
|
397
|
-
input,
|
|
398
|
-
state,
|
|
399
|
-
tools,
|
|
400
|
-
strategy: { onComplete },
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
await strategy.execute(context);
|
|
404
|
-
|
|
405
|
-
expect(onComplete).toHaveBeenCalledTimes(1);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
test('respects stopCondition', async () => {
|
|
409
|
-
const planTurn = createPlanTurn([
|
|
410
|
-
{ id: 'step-1', description: 'First' },
|
|
411
|
-
{ id: 'step-2', description: 'Second' },
|
|
412
|
-
{ id: 'step-3', description: 'Third' },
|
|
413
|
-
]);
|
|
414
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
415
|
-
|
|
416
|
-
const llm = createMockLLM([planTurn, stepTurn, stepTurn, stepTurn]);
|
|
417
|
-
|
|
418
|
-
const strategy = plan();
|
|
419
|
-
const context: ExecutionContext = {
|
|
420
|
-
agent: { id: 'test-agent' },
|
|
421
|
-
llm,
|
|
422
|
-
input,
|
|
423
|
-
state,
|
|
424
|
-
tools,
|
|
425
|
-
strategy: {
|
|
426
|
-
stopCondition: (s) => s.step >= 2, // Stop after first execution step
|
|
427
|
-
},
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
const result = await strategy.execute(context);
|
|
431
|
-
|
|
432
|
-
// Should have stopped early
|
|
433
|
-
const completedSteps = result.state.plan?.filter((s) => s.status === 'completed');
|
|
434
|
-
expect(completedSteps?.length).toBeLessThan(3);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
test('updates step status during execution', async () => {
|
|
438
|
-
const planTurn = createPlanTurn([
|
|
439
|
-
{ id: 'step-1', description: 'First step' },
|
|
440
|
-
]);
|
|
441
|
-
const stepTurn = createMockTurn({ text: 'Step done.' });
|
|
442
|
-
|
|
443
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
444
|
-
|
|
445
|
-
const strategy = plan();
|
|
446
|
-
const context: ExecutionContext = {
|
|
447
|
-
agent: { id: 'test-agent' },
|
|
448
|
-
llm,
|
|
449
|
-
input,
|
|
450
|
-
state,
|
|
451
|
-
tools,
|
|
452
|
-
strategy: {},
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const result = await strategy.execute(context);
|
|
456
|
-
|
|
457
|
-
// Step should be completed
|
|
458
|
-
expect(result.state.plan?.[0]?.status).toBe('completed');
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
describe('stream()', () => {
|
|
463
|
-
test('yields events and resolves result', async () => {
|
|
464
|
-
const planTurn = createPlanTurn([
|
|
465
|
-
{ id: 'step-1', description: 'Only step' },
|
|
466
|
-
]);
|
|
467
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
468
|
-
|
|
469
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
470
|
-
|
|
471
|
-
const strategy = plan();
|
|
472
|
-
const context: ExecutionContext = {
|
|
473
|
-
agent: { id: 'test-agent' },
|
|
474
|
-
llm,
|
|
475
|
-
input,
|
|
476
|
-
state,
|
|
477
|
-
tools,
|
|
478
|
-
strategy: {},
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
const streamResult = strategy.stream(context);
|
|
482
|
-
const events: unknown[] = [];
|
|
483
|
-
|
|
484
|
-
for await (const event of streamResult) {
|
|
485
|
-
events.push(event);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const result = await streamResult.result;
|
|
489
|
-
|
|
490
|
-
expect(events.length).toBeGreaterThan(0);
|
|
491
|
-
expect(result.turn).toBeDefined();
|
|
492
|
-
expect(result.state).toBeDefined();
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
test('emits plan_created event', async () => {
|
|
496
|
-
const planTurn = createPlanTurn([
|
|
497
|
-
{ id: 'step-1', description: 'First step' },
|
|
498
|
-
]);
|
|
499
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
500
|
-
|
|
501
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
502
|
-
|
|
503
|
-
const strategy = plan();
|
|
504
|
-
const context: ExecutionContext = {
|
|
505
|
-
agent: { id: 'test-agent' },
|
|
506
|
-
llm,
|
|
507
|
-
input,
|
|
508
|
-
state,
|
|
509
|
-
tools,
|
|
510
|
-
strategy: {},
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
const streamResult = strategy.stream(context);
|
|
514
|
-
const uapEvents: Array<{ source: string; uap?: { type: string } }> = [];
|
|
515
|
-
|
|
516
|
-
for await (const event of streamResult) {
|
|
517
|
-
if (event.source === 'uap') {
|
|
518
|
-
uapEvents.push(event as typeof uapEvents[0]);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
await streamResult.result;
|
|
523
|
-
|
|
524
|
-
const planCreated = uapEvents.find((e) => e.uap?.type === 'plan_created');
|
|
525
|
-
expect(planCreated).toBeDefined();
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test('emits plan_step_start and plan_step_end events', async () => {
|
|
529
|
-
const planTurn = createPlanTurn([
|
|
530
|
-
{ id: 'step-1', description: 'First step' },
|
|
531
|
-
]);
|
|
532
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
533
|
-
|
|
534
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
535
|
-
|
|
536
|
-
const strategy = plan();
|
|
537
|
-
const context: ExecutionContext = {
|
|
538
|
-
agent: { id: 'test-agent' },
|
|
539
|
-
llm,
|
|
540
|
-
input,
|
|
541
|
-
state,
|
|
542
|
-
tools,
|
|
543
|
-
strategy: {},
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
const streamResult = strategy.stream(context);
|
|
547
|
-
const uapEvents: Array<{ source: string; uap?: { type: string } }> = [];
|
|
548
|
-
|
|
549
|
-
for await (const event of streamResult) {
|
|
550
|
-
if (event.source === 'uap') {
|
|
551
|
-
uapEvents.push(event as typeof uapEvents[0]);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
await streamResult.result;
|
|
556
|
-
|
|
557
|
-
const stepStart = uapEvents.find((e) => e.uap?.type === 'plan_step_start');
|
|
558
|
-
const stepEnd = uapEvents.find((e) => e.uap?.type === 'plan_step_end');
|
|
559
|
-
|
|
560
|
-
expect(stepStart).toBeDefined();
|
|
561
|
-
expect(stepEnd).toBeDefined();
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
test('supports abort()', async () => {
|
|
565
|
-
const planTurn = createPlanTurn([
|
|
566
|
-
{ id: 'step-1', description: 'Only step' },
|
|
567
|
-
]);
|
|
568
|
-
const stepTurn = createMockTurn({ text: 'Done.' });
|
|
569
|
-
|
|
570
|
-
const llm = createMockLLM([planTurn, stepTurn]);
|
|
571
|
-
|
|
572
|
-
const strategy = plan();
|
|
573
|
-
const context: ExecutionContext = {
|
|
574
|
-
agent: { id: 'test-agent' },
|
|
575
|
-
llm,
|
|
576
|
-
input,
|
|
577
|
-
state,
|
|
578
|
-
tools,
|
|
579
|
-
strategy: {},
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
const streamResult = strategy.stream(context);
|
|
583
|
-
|
|
584
|
-
// Abort immediately
|
|
585
|
-
streamResult.abort();
|
|
586
|
-
|
|
587
|
-
expect(typeof streamResult.abort).toBe('function');
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
});
|