@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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/dist/checkpoint/index.d.ts +43 -0
  3. package/dist/checkpoint/index.js +73 -0
  4. package/dist/checkpoint/index.js.map +1 -0
  5. package/{src/execution/loop.ts → dist/chunk-4ESYN66B.js} +54 -162
  6. package/dist/chunk-4ESYN66B.js.map +1 -0
  7. package/dist/chunk-EKRXMSDX.js +8 -0
  8. package/dist/chunk-EKRXMSDX.js.map +1 -0
  9. package/dist/chunk-T47B3VAF.js +427 -0
  10. package/dist/chunk-T47B3VAF.js.map +1 -0
  11. package/dist/execution/index.d.ts +105 -0
  12. package/dist/execution/index.js +679 -0
  13. package/dist/execution/index.js.map +1 -0
  14. package/dist/index-qsPwbY86.d.ts +65 -0
  15. package/dist/index.d.ts +101 -0
  16. package/dist/index.js +218 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/middleware/index.d.ts +23 -0
  19. package/dist/middleware/index.js +82 -0
  20. package/dist/middleware/index.js.map +1 -0
  21. package/dist/thread-tree/index.d.ts +115 -0
  22. package/dist/thread-tree/index.js +4 -0
  23. package/dist/thread-tree/index.js.map +1 -0
  24. package/dist/types-2Vsthzyu.d.ts +163 -0
  25. package/dist/types-BiyEVOnf.d.ts +65 -0
  26. package/dist/types-D1egxttz.d.ts +270 -0
  27. package/dist/types-DChRdQoX.d.ts +98 -0
  28. package/package.json +41 -9
  29. package/.claude/settings.local.json +0 -29
  30. package/AGENTS.md +0 -681
  31. package/CLAUDE.md +0 -681
  32. package/bun.lock +0 -472
  33. package/eslint.config.js +0 -75
  34. package/index.ts +0 -1
  35. package/llms.md +0 -796
  36. package/specs/UAP-1.0.md +0 -2355
  37. package/src/agent/index.ts +0 -384
  38. package/src/agent/types.ts +0 -91
  39. package/src/checkpoint/file.ts +0 -126
  40. package/src/checkpoint/index.ts +0 -40
  41. package/src/checkpoint/types.ts +0 -95
  42. package/src/execution/index.ts +0 -37
  43. package/src/execution/plan.ts +0 -497
  44. package/src/execution/react.ts +0 -340
  45. package/src/execution/tool-ordering.ts +0 -186
  46. package/src/execution/types.ts +0 -315
  47. package/src/index.ts +0 -80
  48. package/src/middleware/index.ts +0 -7
  49. package/src/middleware/logging.ts +0 -123
  50. package/src/middleware/types.ts +0 -69
  51. package/src/state/index.ts +0 -301
  52. package/src/state/types.ts +0 -173
  53. package/src/thread-tree/index.ts +0 -249
  54. package/src/thread-tree/types.ts +0 -29
  55. package/src/utils/uuid.ts +0 -7
  56. package/tests/live/agent-anthropic.test.ts +0 -288
  57. package/tests/live/agent-strategy-hooks.test.ts +0 -268
  58. package/tests/live/checkpoint.test.ts +0 -243
  59. package/tests/live/execution-strategies.test.ts +0 -255
  60. package/tests/live/plan-strategy.test.ts +0 -160
  61. package/tests/live/subagent-events.live.test.ts +0 -249
  62. package/tests/live/thread-tree.test.ts +0 -186
  63. package/tests/unit/agent.test.ts +0 -703
  64. package/tests/unit/checkpoint.test.ts +0 -232
  65. package/tests/unit/execution/equivalence.test.ts +0 -402
  66. package/tests/unit/execution/loop.test.ts +0 -437
  67. package/tests/unit/execution/plan.test.ts +0 -590
  68. package/tests/unit/execution/react.test.ts +0 -604
  69. package/tests/unit/execution/subagent-events.test.ts +0 -235
  70. package/tests/unit/execution/tool-ordering.test.ts +0 -310
  71. package/tests/unit/middleware/logging.test.ts +0 -276
  72. package/tests/unit/state.test.ts +0 -573
  73. package/tests/unit/thread-tree.test.ts +0 -249
  74. 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
- });