@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,249 +0,0 @@
1
- /**
2
- * Live tests for sub-agent event propagation.
3
- *
4
- * These tests verify that sub-agent events are correctly emitted
5
- * when using the Task tool pattern.
6
- *
7
- * @see UAP-1.0 Spec Section 8.7
8
- */
9
-
10
- import { describe, test, expect } from 'bun:test';
11
- import { agent, AgentState } from '../../src/index.ts';
12
- import type {
13
- SubagentEvent,
14
- SubagentStartEvent,
15
- SubagentEndEvent,
16
- OnSubagentEvent,
17
- } from '../../src/execution/types.ts';
18
- import { anthropic } from '@providerprotocol/ai/anthropic';
19
- import type { Tool } from '@providerprotocol/ai';
20
-
21
- /**
22
- * Helper to create a simple sub-agent tool with event propagation.
23
- */
24
- function createSubagentTool(options: {
25
- name: string;
26
- subagentType: string;
27
- onSubagentEvent?: OnSubagentEvent;
28
- }): Tool {
29
- const { name, subagentType, onSubagentEvent } = options;
30
-
31
- // Create a simple sub-agent
32
- const subAgent = agent({
33
- model: anthropic('claude-3-5-haiku-latest'),
34
- params: { max_tokens: 256 },
35
- system: 'You are a helpful assistant. Be concise.',
36
- });
37
-
38
- return {
39
- name,
40
- description: `A sub-agent that helps with tasks. Type: ${subagentType}`,
41
- parameters: {
42
- type: 'object' as const,
43
- properties: {
44
- task: {
45
- type: 'string' as const,
46
- description: 'The task to perform',
47
- },
48
- },
49
- required: ['task'],
50
- },
51
- run: async (params: { task: string }, context?: { toolCallId?: string }) => {
52
- const subagentId = `sub-${Date.now()}`;
53
- const parentToolCallId = context?.toolCallId ?? `tool-${Date.now()}`;
54
- const startTime = Date.now();
55
-
56
- // Emit start event
57
- onSubagentEvent?.({
58
- type: 'subagent_start',
59
- subagentId,
60
- subagentType,
61
- parentToolCallId,
62
- prompt: params.task,
63
- timestamp: startTime,
64
- });
65
-
66
- try {
67
- // Stream the sub-agent for real-time events
68
- const stream = subAgent.stream(params.task, AgentState.initial());
69
-
70
- // Forward inner events
71
- for await (const event of stream) {
72
- onSubagentEvent?.({
73
- type: 'subagent_event',
74
- subagentId,
75
- subagentType,
76
- parentToolCallId,
77
- innerEvent: event,
78
- });
79
- }
80
-
81
- const result = await stream.result;
82
- const endTime = Date.now();
83
-
84
- // Emit end event
85
- onSubagentEvent?.({
86
- type: 'subagent_end',
87
- subagentId,
88
- subagentType,
89
- parentToolCallId,
90
- success: true,
91
- result: result.turn.response.text,
92
- timestamp: endTime,
93
- toolExecutions: result.turn.toolExecutions?.map((exec) => ({
94
- toolName: exec.toolName,
95
- arguments: exec.arguments as Record<string, unknown>,
96
- result: typeof exec.result === 'string' ? exec.result : JSON.stringify(exec.result),
97
- })),
98
- });
99
-
100
- return result.turn.response.text;
101
- } catch (error) {
102
- const endTime = Date.now();
103
-
104
- // Emit error end event
105
- onSubagentEvent?.({
106
- type: 'subagent_end',
107
- subagentId,
108
- subagentType,
109
- parentToolCallId,
110
- success: false,
111
- error: error instanceof Error ? error.message : String(error),
112
- timestamp: endTime,
113
- });
114
-
115
- throw error;
116
- }
117
- },
118
- };
119
- }
120
-
121
- describe('Sub-agent event propagation (live)', () => {
122
- test('sub-agent tool emits start, inner events, and end events', async () => {
123
- const receivedEvents: SubagentEvent[] = [];
124
-
125
- const subagentTool = createSubagentTool({
126
- name: 'helper',
127
- subagentType: 'assistant',
128
- onSubagentEvent: (event) => receivedEvents.push(event),
129
- });
130
-
131
- // Call the tool directly
132
- const result = await subagentTool.run({ task: 'Say hello' });
133
-
134
- // Verify we received events
135
- expect(receivedEvents.length).toBeGreaterThan(0);
136
-
137
- // Check for start event
138
- const startEvent = receivedEvents.find((e) => e.type === 'subagent_start') as SubagentStartEvent | undefined;
139
- expect(startEvent).toBeDefined();
140
- expect(startEvent?.subagentType).toBe('assistant');
141
- expect(startEvent?.prompt).toBe('Say hello');
142
-
143
- // Check for inner events (should have at least text deltas)
144
- const innerEvents = receivedEvents.filter((e) => e.type === 'subagent_event');
145
- expect(innerEvents.length).toBeGreaterThan(0);
146
-
147
- // Check for end event
148
- const endEvent = receivedEvents.find((e) => e.type === 'subagent_end') as SubagentEndEvent | undefined;
149
- expect(endEvent).toBeDefined();
150
- expect(endEvent?.success).toBe(true);
151
- expect(endEvent?.result).toBeDefined();
152
-
153
- // Result should match end event result
154
- expect(result).toBe(endEvent?.result);
155
-
156
- // Verify event order: start should come before end
157
- const startIndex = receivedEvents.findIndex((e) => e.type === 'subagent_start');
158
- const endIndex = receivedEvents.findIndex((e) => e.type === 'subagent_end');
159
- expect(startIndex).toBeLessThan(endIndex);
160
-
161
- // All events should have the same subagentId
162
- const subagentId = startEvent?.subagentId;
163
- expect(receivedEvents.every((e) => e.subagentId === subagentId)).toBe(true);
164
- }, 30000);
165
-
166
- test('sub-agent inner events include text deltas', async () => {
167
- const receivedEvents: SubagentEvent[] = [];
168
-
169
- const subagentTool = createSubagentTool({
170
- name: 'helper',
171
- subagentType: 'assistant',
172
- onSubagentEvent: (event) => receivedEvents.push(event),
173
- });
174
-
175
- await subagentTool.run({ task: 'Count from 1 to 3' });
176
-
177
- // Find text delta events
178
- const textDeltas = receivedEvents.filter(
179
- (e) => e.type === 'subagent_event' && e.innerEvent.source === 'upp' && e.innerEvent.upp?.type === 'text_delta'
180
- );
181
-
182
- // Should have some text deltas
183
- expect(textDeltas.length).toBeGreaterThan(0);
184
- }, 30000);
185
-
186
- test('sub-agent events have consistent parentToolCallId', async () => {
187
- const receivedEvents: SubagentEvent[] = [];
188
-
189
- const subagentTool = createSubagentTool({
190
- name: 'helper',
191
- subagentType: 'assistant',
192
- onSubagentEvent: (event) => receivedEvents.push(event),
193
- });
194
-
195
- // Call with explicit context
196
- await (subagentTool.run as (params: { task: string }, context?: { toolCallId?: string }) => Promise<string>)(
197
- { task: 'Hello' },
198
- { toolCallId: 'custom-tool-id-123' }
199
- );
200
-
201
- // All events should have the same parentToolCallId
202
- expect(receivedEvents.every((e) => e.parentToolCallId === 'custom-tool-id-123')).toBe(true);
203
- }, 30000);
204
-
205
- test('timestamps are monotonically increasing', async () => {
206
- const receivedEvents: SubagentEvent[] = [];
207
-
208
- const subagentTool = createSubagentTool({
209
- name: 'helper',
210
- subagentType: 'assistant',
211
- onSubagentEvent: (event) => receivedEvents.push(event),
212
- });
213
-
214
- await subagentTool.run({ task: 'Hi' });
215
-
216
- const startEvent = receivedEvents.find((e) => e.type === 'subagent_start') as SubagentStartEvent | undefined;
217
- const endEvent = receivedEvents.find((e) => e.type === 'subagent_end') as SubagentEndEvent | undefined;
218
-
219
- expect(startEvent?.timestamp).toBeDefined();
220
- expect(endEvent?.timestamp).toBeDefined();
221
- if (startEvent && endEvent) {
222
- expect(endEvent.timestamp).toBeGreaterThanOrEqual(startEvent.timestamp);
223
- }
224
- }, 30000);
225
- });
226
-
227
- describe('Sub-agent event type guards', () => {
228
- test('can filter events by type', async () => {
229
- const receivedEvents: SubagentEvent[] = [];
230
-
231
- const subagentTool = createSubagentTool({
232
- name: 'helper',
233
- subagentType: 'assistant',
234
- onSubagentEvent: (event) => receivedEvents.push(event),
235
- });
236
-
237
- await subagentTool.run({ task: 'Say one word' });
238
-
239
- // Filter start events
240
- const startEvents = receivedEvents.filter((e): e is SubagentStartEvent => e.type === 'subagent_start');
241
- expect(startEvents).toHaveLength(1);
242
- expect(startEvents[0]?.prompt).toBeDefined();
243
-
244
- // Filter end events
245
- const endEvents = receivedEvents.filter((e): e is SubagentEndEvent => e.type === 'subagent_end');
246
- expect(endEvents).toHaveLength(1);
247
- expect(typeof endEvents[0]?.success).toBe('boolean');
248
- }, 30000);
249
- });
@@ -1,186 +0,0 @@
1
- import { describe, test, expect, setDefaultTimeout } from 'bun:test';
2
- import { anthropic } from '@providerprotocol/ai/anthropic';
3
- import { agent, ThreadTree } from '../../src/index.ts';
4
-
5
- // Skip tests if no API key
6
- const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
7
-
8
- // Increase timeout for live API tests (60 seconds)
9
- setDefaultTimeout(60_000);
10
-
11
- describe.skipIf(!ANTHROPIC_API_KEY)('ThreadTree (Live)', () => {
12
- describe('basic branching', () => {
13
- test('maintains separate contexts in branches', async () => {
14
- const a = agent({
15
- model: anthropic('claude-3-5-haiku-latest'),
16
- params: { max_tokens: 100 },
17
- });
18
-
19
- const tree = new ThreadTree();
20
-
21
- // First conversation in root
22
- const result1 = await a.ask('My name is Alice.', tree.history());
23
- tree.current.state = result1.state;
24
-
25
- // Create branch 1 from root
26
- const branch1Id = tree.branch(tree.root.id, 'branch-1');
27
- tree.checkout(branch1Id);
28
-
29
- // In branch 1, say name is Bob
30
- const branch1Result = await a.ask('Actually, my name is Bob.', tree.history());
31
- tree.current.state = branch1Result.state;
32
-
33
- // Create branch 2 from root (not from branch 1)
34
- const branch2Id = tree.branch(tree.root.id, 'branch-2');
35
- tree.checkout(branch2Id);
36
-
37
- // In branch 2, say name is Charlie
38
- const branch2Result = await a.ask('Actually, my name is Charlie.', tree.history());
39
- tree.current.state = branch2Result.state;
40
-
41
- // Verify branch 1 remembers Bob
42
- tree.checkout(branch1Id);
43
- const branch1Check = await a.ask('What is my name?', tree.history());
44
- expect(branch1Check.turn.response.text.toLowerCase()).toContain('bob');
45
-
46
- // Verify branch 2 remembers Charlie
47
- tree.checkout(branch2Id);
48
- const branch2Check = await a.ask('What is my name?', tree.history());
49
- expect(branch2Check.turn.response.text.toLowerCase()).toContain('charlie');
50
- });
51
-
52
- test('branch inherits parent state', async () => {
53
- const a = agent({
54
- model: anthropic('claude-3-5-haiku-latest'),
55
- params: { max_tokens: 100 },
56
- });
57
-
58
- const tree = new ThreadTree();
59
-
60
- // Establish context in root
61
- const result1 = await a.ask('My favorite color is green.', tree.history());
62
- tree.current.state = result1.state;
63
-
64
- // Create branch
65
- const branchId = tree.branch(tree.root.id, 'color-branch');
66
- tree.checkout(branchId);
67
-
68
- // Branch should remember the color from parent
69
- const branchResult = await a.ask('What is my favorite color?', tree.history());
70
- expect(branchResult.turn.response.text.toLowerCase()).toContain('green');
71
- });
72
- });
73
-
74
- describe('serialization with live data', () => {
75
- test('serializes and restores tree with conversation history', async () => {
76
- const a = agent({
77
- model: anthropic('claude-3-5-haiku-latest'),
78
- params: { max_tokens: 100 },
79
- });
80
-
81
- const tree = new ThreadTree();
82
-
83
- // Build conversation
84
- const result1 = await a.ask('Remember the number 42.', tree.history());
85
- tree.current.state = result1.state;
86
-
87
- // Create branch
88
- const branchId = tree.branch(tree.root.id, 'number-branch');
89
- tree.checkout(branchId);
90
-
91
- const result2 = await a.ask('The number is now 99.', tree.history());
92
- tree.current.state = result2.state;
93
-
94
- // Serialize
95
- const json = tree.toJSON();
96
-
97
- // Restore
98
- const restored = ThreadTree.fromJSON(json);
99
-
100
- // Verify restored tree has correct structure
101
- expect(restored.nodes.size).toBe(tree.nodes.size);
102
- expect(restored.root.id).toBe(tree.root.id);
103
-
104
- // Checkout branch and verify context
105
- restored.checkout(branchId);
106
- const checkResult = await a.ask('What number did I mention last?', restored.history());
107
- expect(checkResult.turn.response.text).toContain('99');
108
- });
109
- });
110
-
111
- describe('multiple branches', () => {
112
- test('supports multiple simultaneous branches', async () => {
113
- const a = agent({
114
- model: anthropic('claude-3-5-haiku-latest'),
115
- params: { max_tokens: 100 },
116
- });
117
-
118
- const tree = new ThreadTree();
119
-
120
- // Setup base context
121
- const result = await a.ask('I am thinking of a secret word.', tree.history());
122
- tree.current.state = result.state;
123
-
124
- // Create 3 branches with different secrets
125
- const branch1Id = tree.branch(tree.root.id, 'branch-apple');
126
- tree.checkout(branch1Id);
127
- const r1 = await a.ask('The secret word is apple.', tree.history());
128
- tree.current.state = r1.state;
129
-
130
- const branch2Id = tree.branch(tree.root.id, 'branch-banana');
131
- tree.checkout(branch2Id);
132
- const r2 = await a.ask('The secret word is banana.', tree.history());
133
- tree.current.state = r2.state;
134
-
135
- const branch3Id = tree.branch(tree.root.id, 'branch-cherry');
136
- tree.checkout(branch3Id);
137
- const r3 = await a.ask('The secret word is cherry.', tree.history());
138
- tree.current.state = r3.state;
139
-
140
- // Verify leaves
141
- const leaves = tree.getLeaves();
142
- expect(leaves.length).toBe(3);
143
-
144
- // Verify each branch has correct secret
145
- tree.checkout(branch1Id);
146
- const check1 = await a.ask('What is the secret word?', tree.history());
147
- expect(check1.turn.response.text.toLowerCase()).toContain('apple');
148
-
149
- tree.checkout(branch2Id);
150
- const check2 = await a.ask('What is the secret word?', tree.history());
151
- expect(check2.turn.response.text.toLowerCase()).toContain('banana');
152
-
153
- tree.checkout(branch3Id);
154
- const check3 = await a.ask('What is the secret word?', tree.history());
155
- expect(check3.turn.response.text.toLowerCase()).toContain('cherry');
156
- });
157
- });
158
-
159
- describe('branch naming', () => {
160
- test('retrieves branches by name', async () => {
161
- const a = agent({
162
- model: anthropic('claude-3-5-haiku-latest'),
163
- params: { max_tokens: 50 },
164
- });
165
-
166
- const tree = new ThreadTree();
167
-
168
- // Build initial state
169
- const result = await a.ask('Hello', tree.history());
170
- tree.current.state = result.state;
171
-
172
- // Create named branches
173
- tree.branch(tree.root.id, 'experiment-a');
174
- tree.branch(tree.root.id, 'experiment-b');
175
- tree.branch(tree.root.id, 'experiment-c');
176
-
177
- const branches = tree.getBranches();
178
-
179
- // Should have 4 branches (root + 3 named)
180
- expect(branches.size).toBe(4);
181
- expect([...branches.values()]).toContain('experiment-a');
182
- expect([...branches.values()]).toContain('experiment-b');
183
- expect([...branches.values()]).toContain('experiment-c');
184
- });
185
- });
186
- });