@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.
Files changed (73) hide show
  1. package/dist/checkpoint/index.d.ts +43 -0
  2. package/dist/checkpoint/index.js +64 -0
  3. package/dist/checkpoint/index.js.map +1 -0
  4. package/{src/execution/loop.ts → dist/chunk-4ESYN66B.js} +54 -162
  5. package/dist/chunk-4ESYN66B.js.map +1 -0
  6. package/dist/chunk-EKRXMSDX.js +8 -0
  7. package/dist/chunk-EKRXMSDX.js.map +1 -0
  8. package/dist/chunk-PHI5ULBV.js +427 -0
  9. package/dist/chunk-PHI5ULBV.js.map +1 -0
  10. package/dist/execution/index.d.ts +105 -0
  11. package/dist/execution/index.js +679 -0
  12. package/dist/execution/index.js.map +1 -0
  13. package/dist/index-qsPwbY86.d.ts +65 -0
  14. package/dist/index.d.ts +101 -0
  15. package/dist/index.js +218 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/middleware/index.d.ts +23 -0
  18. package/dist/middleware/index.js +82 -0
  19. package/dist/middleware/index.js.map +1 -0
  20. package/dist/thread-tree/index.d.ts +115 -0
  21. package/dist/thread-tree/index.js +4 -0
  22. package/dist/thread-tree/index.js.map +1 -0
  23. package/dist/types-2Vsthzyu.d.ts +163 -0
  24. package/dist/types-BhX9uD_d.d.ts +91 -0
  25. package/dist/types-DR02gtFv.d.ts +270 -0
  26. package/dist/types-NGQMdnaD.d.ts +65 -0
  27. package/package.json +40 -8
  28. package/.claude/settings.local.json +0 -29
  29. package/AGENTS.md +0 -681
  30. package/CLAUDE.md +0 -681
  31. package/bun.lock +0 -472
  32. package/eslint.config.js +0 -75
  33. package/index.ts +0 -1
  34. package/llms.md +0 -796
  35. package/specs/UAP-1.0.md +0 -2355
  36. package/src/agent/index.ts +0 -384
  37. package/src/agent/types.ts +0 -91
  38. package/src/checkpoint/file.ts +0 -126
  39. package/src/checkpoint/index.ts +0 -40
  40. package/src/checkpoint/types.ts +0 -95
  41. package/src/execution/index.ts +0 -37
  42. package/src/execution/plan.ts +0 -497
  43. package/src/execution/react.ts +0 -340
  44. package/src/execution/tool-ordering.ts +0 -186
  45. package/src/execution/types.ts +0 -315
  46. package/src/index.ts +0 -80
  47. package/src/middleware/index.ts +0 -7
  48. package/src/middleware/logging.ts +0 -123
  49. package/src/middleware/types.ts +0 -69
  50. package/src/state/index.ts +0 -301
  51. package/src/state/types.ts +0 -173
  52. package/src/thread-tree/index.ts +0 -249
  53. package/src/thread-tree/types.ts +0 -29
  54. package/src/utils/uuid.ts +0 -7
  55. package/tests/live/agent-anthropic.test.ts +0 -288
  56. package/tests/live/agent-strategy-hooks.test.ts +0 -268
  57. package/tests/live/checkpoint.test.ts +0 -243
  58. package/tests/live/execution-strategies.test.ts +0 -255
  59. package/tests/live/plan-strategy.test.ts +0 -160
  60. package/tests/live/subagent-events.live.test.ts +0 -249
  61. package/tests/live/thread-tree.test.ts +0 -186
  62. package/tests/unit/agent.test.ts +0 -703
  63. package/tests/unit/checkpoint.test.ts +0 -232
  64. package/tests/unit/execution/equivalence.test.ts +0 -402
  65. package/tests/unit/execution/loop.test.ts +0 -437
  66. package/tests/unit/execution/plan.test.ts +0 -590
  67. package/tests/unit/execution/react.test.ts +0 -604
  68. package/tests/unit/execution/subagent-events.test.ts +0 -235
  69. package/tests/unit/execution/tool-ordering.test.ts +0 -310
  70. package/tests/unit/middleware/logging.test.ts +0 -276
  71. package/tests/unit/state.test.ts +0 -573
  72. package/tests/unit/thread-tree.test.ts +0 -249
  73. package/tsconfig.json +0 -29
@@ -1,604 +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, TextBlock } from '@providerprotocol/ai';
4
- import { react } from '../../../src/execution/react.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
- }): Turn {
14
- const response = new AssistantMessage(options.text ?? 'Hello', options.toolCalls);
15
- return {
16
- response,
17
- messages: [response],
18
- toolExecutions: options.toolExecutions ?? [],
19
- usage: {
20
- inputTokens: 10,
21
- outputTokens: 20,
22
- totalTokens: 30,
23
- },
24
- cycles: 1,
25
- } as unknown as Turn;
26
- }
27
-
28
- // Mock LLM factory that returns different turns for reasoning and action phases
29
- function createMockLLM(turnSequence: Turn[]): LLMInstance {
30
- let callIndex = 0;
31
-
32
- return {
33
- generate: mock(async () => {
34
- const turn = turnSequence[callIndex] ?? turnSequence[turnSequence.length - 1];
35
- callIndex++;
36
- return turn;
37
- }),
38
- stream: mock(() => {
39
- const turn = turnSequence[callIndex] ?? turnSequence[turnSequence.length - 1];
40
- callIndex++;
41
-
42
- const events: Array<{ type: string; delta?: { text?: string } }> = [];
43
- if (turn) {
44
- events.push({ type: 'text_delta', delta: { text: turn.response.text } });
45
- events.push({ type: 'message_stop' });
46
- }
47
-
48
- return {
49
- async *[Symbol.asyncIterator]() {
50
- for (const event of events) {
51
- yield event;
52
- }
53
- },
54
- turn: Promise.resolve(turn),
55
- };
56
- }),
57
- } as unknown as LLMInstance;
58
- }
59
-
60
- describe('react() execution strategy', () => {
61
- let state: AgentState;
62
- let input: UserMessage;
63
- let tools: Tool[];
64
-
65
- beforeEach(() => {
66
- state = AgentState.initial();
67
- input = new UserMessage('Hello');
68
- tools = [];
69
- });
70
-
71
- describe('execute()', () => {
72
- test('returns turn and updated state', async () => {
73
- // ReAct makes 2 LLM calls per step: reasoning + action
74
- const reasoningTurn = createMockTurn({ text: 'I should respond with a greeting.' });
75
- const actionTurn = createMockTurn({ text: 'Hi there!' });
76
- const llm = createMockLLM([reasoningTurn, actionTurn]);
77
-
78
- const strategy = react();
79
- const context: ExecutionContext = {
80
- agent: { id: 'test-agent' },
81
- llm,
82
- input,
83
- state,
84
- tools,
85
- strategy: {},
86
- };
87
-
88
- const result = await strategy.execute(context);
89
-
90
- expect(result.turn).toBe(actionTurn);
91
- expect(result.state.step).toBe(1);
92
- });
93
-
94
- test('calls reasoning and action phases', async () => {
95
- const reasoningTurn = createMockTurn({ text: 'Thinking about the problem...' });
96
- const actionTurn = createMockTurn({ text: 'Here is my answer.' });
97
- const llm = createMockLLM([reasoningTurn, actionTurn]);
98
-
99
- const strategy = react();
100
- const context: ExecutionContext = {
101
- agent: { id: 'test-agent' },
102
- llm,
103
- input,
104
- state,
105
- tools,
106
- strategy: {},
107
- };
108
-
109
- await strategy.execute(context);
110
-
111
- // 2 calls: reasoning phase + action phase
112
- expect(llm.generate).toHaveBeenCalledTimes(2);
113
- });
114
-
115
- test('stores reasoning in state', async () => {
116
- const reasoningTurn = createMockTurn({ text: 'My reasoning about this task.' });
117
- const actionTurn = createMockTurn({ text: 'Action result.' });
118
- const llm = createMockLLM([reasoningTurn, actionTurn]);
119
-
120
- const strategy = react();
121
- const context: ExecutionContext = {
122
- agent: { id: 'test-agent' },
123
- llm,
124
- input,
125
- state,
126
- tools,
127
- strategy: {},
128
- };
129
-
130
- const result = await strategy.execute(context);
131
-
132
- expect(result.state.reasoning).toHaveLength(1);
133
- expect(result.state.reasoning[0]).toBe('My reasoning about this task.');
134
- });
135
-
136
- test('loops on tool calls', async () => {
137
- const toolCall: ToolCall = {
138
- toolCallId: 'call-1',
139
- toolName: 'test_tool',
140
- arguments: { arg: 'value' },
141
- };
142
-
143
- // Step 1: reasoning + action with tool call
144
- const reasoning1 = createMockTurn({ text: 'I need to use a tool.' });
145
- const action1 = createMockTurn({
146
- text: 'Using tool...',
147
- toolCalls: [toolCall],
148
- toolExecutions: [{
149
- toolCallId: 'call-1',
150
- toolName: 'test_tool',
151
- arguments: { arg: 'value' },
152
- result: 'tool result',
153
- duration: 100,
154
- isError: false,
155
- }],
156
- });
157
-
158
- // Step 2: reasoning + final action
159
- const reasoning2 = createMockTurn({ text: 'Now I can provide the answer.' });
160
- const action2 = createMockTurn({ text: 'Final answer!' });
161
-
162
- const llm = createMockLLM([reasoning1, action1, reasoning2, action2]);
163
-
164
- const strategy = react();
165
- const context: ExecutionContext = {
166
- agent: { id: 'test-agent' },
167
- llm,
168
- input,
169
- state,
170
- tools,
171
- strategy: {},
172
- };
173
-
174
- const result = await strategy.execute(context);
175
-
176
- // 4 calls: (reasoning1 + action1) + (reasoning2 + action2)
177
- expect(llm.generate).toHaveBeenCalledTimes(4);
178
- expect(result.state.step).toBe(2);
179
- expect(result.state.reasoning).toHaveLength(2);
180
- });
181
-
182
- test('respects maxSteps', async () => {
183
- const toolCall: ToolCall = {
184
- toolCallId: 'call-1',
185
- toolName: 'test_tool',
186
- arguments: {},
187
- };
188
-
189
- // All turns have tool calls to force looping
190
- const reasoningTurn = createMockTurn({ text: 'Thinking...' });
191
- const actionTurn = createMockTurn({
192
- text: 'Acting...',
193
- toolCalls: [toolCall],
194
- toolExecutions: [{
195
- toolCallId: 'call-1',
196
- toolName: 'test_tool',
197
- arguments: {},
198
- result: 'ok',
199
- duration: 10,
200
- isError: false,
201
- }],
202
- });
203
-
204
- const llm = createMockLLM([
205
- reasoningTurn, actionTurn,
206
- reasoningTurn, actionTurn,
207
- reasoningTurn, actionTurn,
208
- reasoningTurn, actionTurn,
209
- reasoningTurn, actionTurn,
210
- ]);
211
-
212
- const strategy = react({ maxSteps: 2 });
213
- const context: ExecutionContext = {
214
- agent: { id: 'test-agent' },
215
- llm,
216
- input,
217
- state,
218
- tools,
219
- strategy: {},
220
- };
221
-
222
- const result = await strategy.execute(context);
223
-
224
- // 2 steps * 2 calls per step = 4 calls
225
- expect(llm.generate).toHaveBeenCalledTimes(4);
226
- expect(result.state.step).toBe(2);
227
- });
228
-
229
- test('defaults to Infinity maxSteps', () => {
230
- const strategy = react();
231
- expect(strategy.name).toBe('react');
232
- });
233
-
234
- test('calls onReason hook', async () => {
235
- const reasoningTurn = createMockTurn({ text: 'My reasoning process.' });
236
- const actionTurn = createMockTurn({ text: 'Done.' });
237
- const llm = createMockLLM([reasoningTurn, actionTurn]);
238
- const onReason = mock(() => {});
239
-
240
- const strategy = react();
241
- const context: ExecutionContext = {
242
- agent: { id: 'test-agent' },
243
- llm,
244
- input,
245
- state,
246
- tools,
247
- strategy: { onReason },
248
- };
249
-
250
- await strategy.execute(context);
251
-
252
- expect(onReason).toHaveBeenCalledTimes(1);
253
- expect(onReason).toHaveBeenCalledWith(1, 'My reasoning process.');
254
- });
255
-
256
- test('calls onStepStart hook', async () => {
257
- const reasoningTurn = createMockTurn({ text: 'Thinking.' });
258
- const actionTurn = createMockTurn({ text: 'Done.' });
259
- const llm = createMockLLM([reasoningTurn, actionTurn]);
260
- const onStepStart = mock(() => {});
261
-
262
- const strategy = react();
263
- const context: ExecutionContext = {
264
- agent: { id: 'test-agent' },
265
- llm,
266
- input,
267
- state,
268
- tools,
269
- strategy: { onStepStart },
270
- };
271
-
272
- await strategy.execute(context);
273
-
274
- expect(onStepStart).toHaveBeenCalledTimes(1);
275
- });
276
-
277
- test('calls onStepEnd hook', async () => {
278
- const reasoningTurn = createMockTurn({ text: 'Thinking.' });
279
- const actionTurn = createMockTurn({ text: 'Done.' });
280
- const llm = createMockLLM([reasoningTurn, actionTurn]);
281
- const onStepEnd = mock(() => {});
282
-
283
- const strategy = react();
284
- const context: ExecutionContext = {
285
- agent: { id: 'test-agent' },
286
- llm,
287
- input,
288
- state,
289
- tools,
290
- strategy: { onStepEnd },
291
- };
292
-
293
- await strategy.execute(context);
294
-
295
- expect(onStepEnd).toHaveBeenCalledTimes(1);
296
- });
297
-
298
- test('calls onAct hook on tool calls', async () => {
299
- const toolCall: ToolCall = {
300
- toolCallId: 'call-1',
301
- toolName: 'test_tool',
302
- arguments: {},
303
- };
304
-
305
- const reasoningTurn = createMockTurn({ text: 'Using tool.' });
306
- const actionTurn = createMockTurn({
307
- text: 'Called tool.',
308
- toolCalls: [toolCall],
309
- });
310
- const reasoning2 = createMockTurn({ text: 'Done now.' });
311
- const action2 = createMockTurn({ text: 'Final.' });
312
-
313
- const llm = createMockLLM([reasoningTurn, actionTurn, reasoning2, action2]);
314
- const onAct = mock(() => {});
315
-
316
- const strategy = react();
317
- const context: ExecutionContext = {
318
- agent: { id: 'test-agent' },
319
- llm,
320
- input,
321
- state,
322
- tools,
323
- strategy: { onAct },
324
- };
325
-
326
- await strategy.execute(context);
327
-
328
- expect(onAct).toHaveBeenCalledWith(1, [toolCall]);
329
- });
330
-
331
- test('calls onObserve hook on tool executions', async () => {
332
- const execution: ToolExecution = {
333
- toolCallId: 'call-1',
334
- toolName: 'test_tool',
335
- arguments: {},
336
- result: 'result',
337
- duration: 100,
338
- isError: false,
339
- };
340
-
341
- const reasoningTurn = createMockTurn({ text: 'Observing.' });
342
- const actionTurn = createMockTurn({
343
- text: 'Done.',
344
- toolExecutions: [execution],
345
- });
346
- const llm = createMockLLM([reasoningTurn, actionTurn]);
347
- const onObserve = mock(() => {});
348
-
349
- const strategy = react();
350
- const context: ExecutionContext = {
351
- agent: { id: 'test-agent' },
352
- llm,
353
- input,
354
- state,
355
- tools,
356
- strategy: { onObserve },
357
- };
358
-
359
- await strategy.execute(context);
360
-
361
- expect(onObserve).toHaveBeenCalledWith(1, [execution]);
362
- });
363
-
364
- test('calls onComplete hook', async () => {
365
- const reasoningTurn = createMockTurn({ text: 'Thinking.' });
366
- const actionTurn = createMockTurn({ text: 'Done.' });
367
- const llm = createMockLLM([reasoningTurn, actionTurn]);
368
- const onComplete = mock(() => {});
369
-
370
- const strategy = react();
371
- const context: ExecutionContext = {
372
- agent: { id: 'test-agent' },
373
- llm,
374
- input,
375
- state,
376
- tools,
377
- strategy: { onComplete },
378
- };
379
-
380
- await strategy.execute(context);
381
-
382
- expect(onComplete).toHaveBeenCalledTimes(1);
383
- });
384
-
385
- test('respects stopCondition', async () => {
386
- const toolCall: ToolCall = {
387
- toolCallId: 'call-1',
388
- toolName: 'test_tool',
389
- arguments: {},
390
- };
391
-
392
- const reasoningTurn = createMockTurn({ text: 'Thinking.' });
393
- const actionTurn = createMockTurn({
394
- text: 'Acting.',
395
- toolCalls: [toolCall],
396
- toolExecutions: [{
397
- toolCallId: 'call-1',
398
- toolName: 'test_tool',
399
- arguments: {},
400
- result: 'ok',
401
- duration: 10,
402
- isError: false,
403
- }],
404
- });
405
-
406
- const llm = createMockLLM([
407
- reasoningTurn, actionTurn,
408
- reasoningTurn, actionTurn,
409
- reasoningTurn, actionTurn,
410
- ]);
411
-
412
- const strategy = react();
413
- const context: ExecutionContext = {
414
- agent: { id: 'test-agent' },
415
- llm,
416
- input,
417
- state,
418
- tools,
419
- strategy: {
420
- stopCondition: (s) => s.step >= 2,
421
- },
422
- };
423
-
424
- const result = await strategy.execute(context);
425
-
426
- expect(result.state.step).toBe(2);
427
- });
428
-
429
- test('uses custom reasoning prompt', async () => {
430
- const reasoningTurn = createMockTurn({ text: 'Custom reasoning.' });
431
- const actionTurn = createMockTurn({ text: 'Done.' });
432
-
433
- let callCount = 0;
434
- let reasoningCallMessages: unknown[] = [];
435
- const llm = {
436
- generate: mock(async (...args: unknown[]) => {
437
- callCount++;
438
- // First call is reasoning phase - capture those messages
439
- if (callCount === 1) {
440
- // The args[0] is the messages array passed to llm.generate
441
- reasoningCallMessages = Array.isArray(args[0]) ? [...args[0]] : [];
442
- return reasoningTurn;
443
- }
444
- return actionTurn;
445
- }),
446
- stream: mock(() => ({
447
- async *[Symbol.asyncIterator]() {},
448
- turn: Promise.resolve(actionTurn),
449
- })),
450
- } as unknown as LLMInstance;
451
-
452
- const customPrompt = 'Think carefully about this specific problem.';
453
- const strategy = react({ reasoningPrompt: customPrompt });
454
- const context: ExecutionContext = {
455
- agent: { id: 'test-agent' },
456
- llm,
457
- input,
458
- state,
459
- tools,
460
- strategy: {},
461
- };
462
-
463
- await strategy.execute(context);
464
-
465
- // Should have 2 calls: reasoning + action
466
- expect(callCount).toBe(2);
467
-
468
- // The reasoning phase messages should include the custom prompt
469
- // The last message should be the custom reasoning prompt as a UserMessage
470
- expect(reasoningCallMessages.length).toBeGreaterThan(0);
471
-
472
- const lastMsg = reasoningCallMessages[reasoningCallMessages.length - 1];
473
- expect(lastMsg).toBeInstanceOf(UserMessage);
474
-
475
- // UserMessage.content is an array of content blocks in UPP format
476
- const msgContent = (lastMsg as UserMessage).content;
477
- // Content is array of blocks like [{ type: "text", text: "..." }]
478
- const textBlock = msgContent.find(
479
- (block): block is TextBlock => block.type === 'text',
480
- );
481
- expect(textBlock).toBeDefined();
482
- expect(textBlock?.text).toBe(customPrompt);
483
- });
484
- });
485
-
486
- describe('stream()', () => {
487
- test('yields events and resolves result', async () => {
488
- const reasoningTurn = createMockTurn({ text: 'Streaming reasoning.' });
489
- const actionTurn = createMockTurn({ text: 'Streaming action.' });
490
- const llm = createMockLLM([reasoningTurn, actionTurn]);
491
-
492
- const strategy = react();
493
- const context: ExecutionContext = {
494
- agent: { id: 'test-agent' },
495
- llm,
496
- input,
497
- state,
498
- tools,
499
- strategy: {},
500
- };
501
-
502
- const streamResult = strategy.stream(context);
503
- const events: unknown[] = [];
504
-
505
- for await (const event of streamResult) {
506
- events.push(event);
507
- }
508
-
509
- const result = await streamResult.result;
510
-
511
- expect(events.length).toBeGreaterThan(0);
512
- expect(result.turn).toBeDefined();
513
- expect(result.state).toBeDefined();
514
- });
515
-
516
- test('emits reasoning event', async () => {
517
- const reasoningTurn = createMockTurn({ text: 'My reasoning.' });
518
- const actionTurn = createMockTurn({ text: 'Done.' });
519
- const llm = createMockLLM([reasoningTurn, actionTurn]);
520
-
521
- const strategy = react();
522
- const context: ExecutionContext = {
523
- agent: { id: 'test-agent' },
524
- llm,
525
- input,
526
- state,
527
- tools,
528
- strategy: {},
529
- };
530
-
531
- const streamResult = strategy.stream(context);
532
- const uapEvents: Array<{ source: string; uap?: { type: string; data?: { reasoning?: string } } }> = [];
533
-
534
- for await (const event of streamResult) {
535
- if (event.source === 'uap') {
536
- uapEvents.push(event as typeof uapEvents[0]);
537
- }
538
- }
539
-
540
- await streamResult.result;
541
-
542
- const reasoningEvent = uapEvents.find((e) => e.uap?.type === 'reasoning');
543
- expect(reasoningEvent).toBeDefined();
544
- expect(reasoningEvent?.uap?.data?.reasoning).toBe('My reasoning.');
545
- });
546
-
547
- test('emits step_start and step_end events', async () => {
548
- const reasoningTurn = createMockTurn({ text: 'Thinking.' });
549
- const actionTurn = createMockTurn({ text: 'Done.' });
550
- const llm = createMockLLM([reasoningTurn, actionTurn]);
551
-
552
- const strategy = react();
553
- const context: ExecutionContext = {
554
- agent: { id: 'test-agent' },
555
- llm,
556
- input,
557
- state,
558
- tools,
559
- strategy: {},
560
- };
561
-
562
- const streamResult = strategy.stream(context);
563
- const uapEvents: Array<{ source: string; uap?: { type: string } }> = [];
564
-
565
- for await (const event of streamResult) {
566
- if (event.source === 'uap') {
567
- uapEvents.push(event as typeof uapEvents[0]);
568
- }
569
- }
570
-
571
- await streamResult.result;
572
-
573
- const stepStart = uapEvents.find((e) => e.uap?.type === 'step_start');
574
- const stepEnd = uapEvents.find((e) => e.uap?.type === 'step_end');
575
-
576
- expect(stepStart).toBeDefined();
577
- expect(stepEnd).toBeDefined();
578
- });
579
-
580
- test('supports abort()', async () => {
581
- const reasoningTurn = createMockTurn({ text: 'Thinking.' });
582
- const actionTurn = createMockTurn({ text: 'Done.' });
583
- const llm = createMockLLM([reasoningTurn, actionTurn]);
584
-
585
- const strategy = react();
586
- const context: ExecutionContext = {
587
- agent: { id: 'test-agent' },
588
- llm,
589
- input,
590
- state,
591
- tools,
592
- strategy: {},
593
- };
594
-
595
- const streamResult = strategy.stream(context);
596
-
597
- // Abort immediately
598
- streamResult.abort();
599
-
600
- // The stream should handle abort gracefully
601
- expect(typeof streamResult.abort).toBe('function');
602
- });
603
- });
604
- });