@providerprotocol/agents 0.0.1 → 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/README.md +333 -6
- 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 -27
- 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/tests/unit/state.test.ts
DELETED
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach } from 'bun:test';
|
|
2
|
-
import { UserMessage, AssistantMessage } from '@providerprotocol/ai';
|
|
3
|
-
import { AgentState } from '../../src/state/index.ts';
|
|
4
|
-
import type { PlanStep, SubagentExecutionTrace } from '../../src/state/index.ts';
|
|
5
|
-
|
|
6
|
-
describe('AgentState', () => {
|
|
7
|
-
describe('initial()', () => {
|
|
8
|
-
test('creates state with empty messages', () => {
|
|
9
|
-
const state = AgentState.initial();
|
|
10
|
-
expect(state.messages).toEqual([]);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('creates state with step 0', () => {
|
|
14
|
-
const state = AgentState.initial();
|
|
15
|
-
expect(state.step).toBe(0);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test('creates state with empty metadata', () => {
|
|
19
|
-
const state = AgentState.initial();
|
|
20
|
-
expect(state.metadata).toEqual({});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('creates state with empty reasoning', () => {
|
|
24
|
-
const state = AgentState.initial();
|
|
25
|
-
expect(state.reasoning).toEqual([]);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('creates state with undefined plan', () => {
|
|
29
|
-
const state = AgentState.initial();
|
|
30
|
-
expect(state.plan).toBeUndefined();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('creates state with empty subagentTraces', () => {
|
|
34
|
-
const state = AgentState.initial();
|
|
35
|
-
expect(state.subagentTraces).toEqual([]);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('creates state with unique UUIDv4 id', () => {
|
|
39
|
-
const state1 = AgentState.initial();
|
|
40
|
-
const state2 = AgentState.initial();
|
|
41
|
-
|
|
42
|
-
expect(state1.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
43
|
-
expect(state1.id).not.toBe(state2.id);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('withMessage()', () => {
|
|
48
|
-
let state: AgentState;
|
|
49
|
-
|
|
50
|
-
beforeEach(() => {
|
|
51
|
-
state = AgentState.initial();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('returns new state with message added', () => {
|
|
55
|
-
const message = new UserMessage('Hello');
|
|
56
|
-
const newState = state.withMessage(message);
|
|
57
|
-
|
|
58
|
-
expect(newState.messages).toHaveLength(1);
|
|
59
|
-
expect(newState.messages[0]).toBe(message);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('does not modify original state', () => {
|
|
63
|
-
const message = new UserMessage('Hello');
|
|
64
|
-
state.withMessage(message);
|
|
65
|
-
|
|
66
|
-
expect(state.messages).toHaveLength(0);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('returns state with new id', () => {
|
|
70
|
-
const message = new UserMessage('Hello');
|
|
71
|
-
const newState = state.withMessage(message);
|
|
72
|
-
|
|
73
|
-
expect(newState.id).not.toBe(state.id);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('preserves other state properties', () => {
|
|
77
|
-
const stateWithMeta = state.withMetadata('key', 'value').withStep(5);
|
|
78
|
-
const message = new UserMessage('Hello');
|
|
79
|
-
const newState = stateWithMeta.withMessage(message);
|
|
80
|
-
|
|
81
|
-
expect(newState.metadata).toEqual({ key: 'value' });
|
|
82
|
-
expect(newState.step).toBe(5);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe('withMessages()', () => {
|
|
87
|
-
test('appends multiple messages', () => {
|
|
88
|
-
const state = AgentState.initial();
|
|
89
|
-
const messages = [
|
|
90
|
-
new UserMessage('Hello'),
|
|
91
|
-
new AssistantMessage('Hi there!'),
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
const newState = state.withMessages(messages);
|
|
95
|
-
|
|
96
|
-
expect(newState.messages).toHaveLength(2);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('appends to existing messages', () => {
|
|
100
|
-
const state = AgentState.initial()
|
|
101
|
-
.withMessage(new UserMessage('First'));
|
|
102
|
-
|
|
103
|
-
const newState = state.withMessages([
|
|
104
|
-
new AssistantMessage('Response'),
|
|
105
|
-
new UserMessage('Second'),
|
|
106
|
-
]);
|
|
107
|
-
|
|
108
|
-
expect(newState.messages).toHaveLength(3);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('withContext()', () => {
|
|
113
|
-
test('replaces all messages', () => {
|
|
114
|
-
const state = AgentState.initial()
|
|
115
|
-
.withMessage(new UserMessage('First'))
|
|
116
|
-
.withMessage(new AssistantMessage('Response'))
|
|
117
|
-
.withMessage(new UserMessage('Second'));
|
|
118
|
-
|
|
119
|
-
const newMessages = [
|
|
120
|
-
new UserMessage('Replacement'),
|
|
121
|
-
];
|
|
122
|
-
|
|
123
|
-
const newState = state.withContext(newMessages);
|
|
124
|
-
|
|
125
|
-
expect(newState.messages).toHaveLength(1);
|
|
126
|
-
expect(newState.messages[0]).toBe(newMessages[0]);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test('does not modify original state', () => {
|
|
130
|
-
const originalMessage = new UserMessage('Original');
|
|
131
|
-
const state = AgentState.initial()
|
|
132
|
-
.withMessage(originalMessage);
|
|
133
|
-
|
|
134
|
-
state.withContext([new UserMessage('Replacement')]);
|
|
135
|
-
|
|
136
|
-
expect(state.messages).toHaveLength(1);
|
|
137
|
-
expect(state.messages[0]).toBe(originalMessage);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('returns state with new id', () => {
|
|
141
|
-
const state = AgentState.initial()
|
|
142
|
-
.withMessage(new UserMessage('Hello'));
|
|
143
|
-
|
|
144
|
-
const newState = state.withContext([new UserMessage('New')]);
|
|
145
|
-
|
|
146
|
-
expect(newState.id).not.toBe(state.id);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('preserves other state properties', () => {
|
|
150
|
-
const state = AgentState.initial()
|
|
151
|
-
.withMessage(new UserMessage('Original'))
|
|
152
|
-
.withStep(5)
|
|
153
|
-
.withMetadata('key', 'value')
|
|
154
|
-
.withReasoning('Some reasoning');
|
|
155
|
-
|
|
156
|
-
const newState = state.withContext([new UserMessage('Replaced')]);
|
|
157
|
-
|
|
158
|
-
expect(newState.step).toBe(5);
|
|
159
|
-
expect(newState.metadata).toEqual({ key: 'value' });
|
|
160
|
-
expect(newState.reasoning).toEqual(['Some reasoning']);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test('can replace with empty messages array', () => {
|
|
164
|
-
const state = AgentState.initial()
|
|
165
|
-
.withMessage(new UserMessage('First'))
|
|
166
|
-
.withMessage(new AssistantMessage('Second'));
|
|
167
|
-
|
|
168
|
-
const newState = state.withContext([]);
|
|
169
|
-
|
|
170
|
-
expect(newState.messages).toHaveLength(0);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('enables context window management patterns', () => {
|
|
174
|
-
// Simulate pruning old tool outputs
|
|
175
|
-
const state = AgentState.initial()
|
|
176
|
-
.withMessage(new UserMessage('Query 1'))
|
|
177
|
-
.withMessage(new AssistantMessage('Long tool output response 1'))
|
|
178
|
-
.withMessage(new UserMessage('Query 2'))
|
|
179
|
-
.withMessage(new AssistantMessage('Long tool output response 2'))
|
|
180
|
-
.withMessage(new UserMessage('Query 3'));
|
|
181
|
-
|
|
182
|
-
// Prune to keep only last 2 messages (simulating context management)
|
|
183
|
-
const prunedMessages = state.messages.slice(-2);
|
|
184
|
-
const newState = state.withContext([...prunedMessages]);
|
|
185
|
-
|
|
186
|
-
expect(newState.messages).toHaveLength(2);
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
describe('withStep()', () => {
|
|
191
|
-
test('updates step number', () => {
|
|
192
|
-
const state = AgentState.initial();
|
|
193
|
-
const newState = state.withStep(5);
|
|
194
|
-
|
|
195
|
-
expect(newState.step).toBe(5);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test('does not modify original state', () => {
|
|
199
|
-
const state = AgentState.initial();
|
|
200
|
-
state.withStep(5);
|
|
201
|
-
|
|
202
|
-
expect(state.step).toBe(0);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('withMetadata()', () => {
|
|
207
|
-
test('adds metadata entry', () => {
|
|
208
|
-
const state = AgentState.initial();
|
|
209
|
-
const newState = state.withMetadata('key', 'value');
|
|
210
|
-
|
|
211
|
-
expect(newState.metadata).toEqual({ key: 'value' });
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test('updates existing metadata entry', () => {
|
|
215
|
-
const state = AgentState.initial()
|
|
216
|
-
.withMetadata('key', 'old');
|
|
217
|
-
|
|
218
|
-
const newState = state.withMetadata('key', 'new');
|
|
219
|
-
|
|
220
|
-
expect(newState.metadata).toEqual({ key: 'new' });
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test('preserves other metadata entries', () => {
|
|
224
|
-
const state = AgentState.initial()
|
|
225
|
-
.withMetadata('a', 1)
|
|
226
|
-
.withMetadata('b', 2);
|
|
227
|
-
|
|
228
|
-
const newState = state.withMetadata('c', 3);
|
|
229
|
-
|
|
230
|
-
expect(newState.metadata).toEqual({ a: 1, b: 2, c: 3 });
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
describe('withReasoning()', () => {
|
|
235
|
-
test('appends reasoning trace', () => {
|
|
236
|
-
const state = AgentState.initial();
|
|
237
|
-
const newState = state.withReasoning('I should do X');
|
|
238
|
-
|
|
239
|
-
expect(newState.reasoning).toEqual(['I should do X']);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('appends to existing reasoning', () => {
|
|
243
|
-
const state = AgentState.initial()
|
|
244
|
-
.withReasoning('First thought');
|
|
245
|
-
|
|
246
|
-
const newState = state.withReasoning('Second thought');
|
|
247
|
-
|
|
248
|
-
expect(newState.reasoning).toEqual(['First thought', 'Second thought']);
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe('withPlan()', () => {
|
|
253
|
-
test('sets plan', () => {
|
|
254
|
-
const state = AgentState.initial();
|
|
255
|
-
const plan: PlanStep[] = [
|
|
256
|
-
{ id: '1', description: 'Step 1', dependsOn: [], status: 'pending' },
|
|
257
|
-
{ id: '2', description: 'Step 2', dependsOn: ['1'], status: 'pending' },
|
|
258
|
-
];
|
|
259
|
-
|
|
260
|
-
const newState = state.withPlan(plan);
|
|
261
|
-
|
|
262
|
-
expect(newState.plan).toHaveLength(2);
|
|
263
|
-
expect(newState.plan?.[0]?.description).toBe('Step 1');
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test('replaces existing plan', () => {
|
|
267
|
-
const state = AgentState.initial()
|
|
268
|
-
.withPlan([{ id: '1', description: 'Old', dependsOn: [], status: 'pending' }]);
|
|
269
|
-
|
|
270
|
-
const newPlan: PlanStep[] = [
|
|
271
|
-
{ id: '2', description: 'New', dependsOn: [], status: 'pending' },
|
|
272
|
-
];
|
|
273
|
-
|
|
274
|
-
const newState = state.withPlan(newPlan);
|
|
275
|
-
|
|
276
|
-
expect(newState.plan).toHaveLength(1);
|
|
277
|
-
expect(newState.plan?.[0]?.description).toBe('New');
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
describe('withSubagentTrace()', () => {
|
|
282
|
-
test('adds subagent trace', () => {
|
|
283
|
-
const state = AgentState.initial();
|
|
284
|
-
const trace: SubagentExecutionTrace = {
|
|
285
|
-
subagentId: 'agent-123',
|
|
286
|
-
subagentType: 'explorer',
|
|
287
|
-
parentToolCallId: 'tool-456',
|
|
288
|
-
prompt: 'Find all TypeScript files',
|
|
289
|
-
startTime: 1000,
|
|
290
|
-
endTime: 2000,
|
|
291
|
-
success: true,
|
|
292
|
-
result: 'Found 10 files',
|
|
293
|
-
toolExecutions: [
|
|
294
|
-
{
|
|
295
|
-
toolName: 'Glob',
|
|
296
|
-
toolCallId: 'glob-001',
|
|
297
|
-
arguments: { pattern: '**/*.ts' },
|
|
298
|
-
result: 'file1.ts, file2.ts',
|
|
299
|
-
duration: 50,
|
|
300
|
-
},
|
|
301
|
-
],
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
const newState = state.withSubagentTrace(trace);
|
|
305
|
-
|
|
306
|
-
expect(newState.subagentTraces).toHaveLength(1);
|
|
307
|
-
expect(newState.subagentTraces[0]).toEqual(trace);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
test('appends to existing traces', () => {
|
|
311
|
-
const trace1: SubagentExecutionTrace = {
|
|
312
|
-
subagentId: 'agent-1',
|
|
313
|
-
subagentType: 'explorer',
|
|
314
|
-
parentToolCallId: 'tool-1',
|
|
315
|
-
prompt: 'First task',
|
|
316
|
-
startTime: 1000,
|
|
317
|
-
endTime: 1500,
|
|
318
|
-
success: true,
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
const trace2: SubagentExecutionTrace = {
|
|
322
|
-
subagentId: 'agent-2',
|
|
323
|
-
subagentType: 'planner',
|
|
324
|
-
parentToolCallId: 'tool-2',
|
|
325
|
-
prompt: 'Second task',
|
|
326
|
-
startTime: 2000,
|
|
327
|
-
endTime: 2500,
|
|
328
|
-
success: true,
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const state = AgentState.initial()
|
|
332
|
-
.withSubagentTrace(trace1);
|
|
333
|
-
const newState = state.withSubagentTrace(trace2);
|
|
334
|
-
|
|
335
|
-
expect(newState.subagentTraces).toHaveLength(2);
|
|
336
|
-
expect(newState.subagentTraces[0]?.subagentId).toBe('agent-1');
|
|
337
|
-
expect(newState.subagentTraces[1]?.subagentId).toBe('agent-2');
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
test('does not modify original state', () => {
|
|
341
|
-
const state = AgentState.initial();
|
|
342
|
-
const trace: SubagentExecutionTrace = {
|
|
343
|
-
subagentId: 'agent-123',
|
|
344
|
-
subagentType: 'explorer',
|
|
345
|
-
parentToolCallId: 'tool-456',
|
|
346
|
-
prompt: 'Test',
|
|
347
|
-
startTime: 1000,
|
|
348
|
-
endTime: 2000,
|
|
349
|
-
success: true,
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
state.withSubagentTrace(trace);
|
|
353
|
-
|
|
354
|
-
expect(state.subagentTraces).toHaveLength(0);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test('preserves other state properties', () => {
|
|
358
|
-
const state = AgentState.initial()
|
|
359
|
-
.withMessage(new UserMessage('Hello'))
|
|
360
|
-
.withStep(3)
|
|
361
|
-
.withMetadata('key', 'value');
|
|
362
|
-
|
|
363
|
-
const trace: SubagentExecutionTrace = {
|
|
364
|
-
subagentId: 'agent-123',
|
|
365
|
-
subagentType: 'explorer',
|
|
366
|
-
parentToolCallId: 'tool-456',
|
|
367
|
-
prompt: 'Test',
|
|
368
|
-
startTime: 1000,
|
|
369
|
-
endTime: 2000,
|
|
370
|
-
success: true,
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const newState = state.withSubagentTrace(trace);
|
|
374
|
-
|
|
375
|
-
expect(newState.messages).toHaveLength(1);
|
|
376
|
-
expect(newState.step).toBe(3);
|
|
377
|
-
expect(newState.metadata).toEqual({ key: 'value' });
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
describe('serialization', () => {
|
|
382
|
-
test('toJSON() serializes all properties', () => {
|
|
383
|
-
const trace: SubagentExecutionTrace = {
|
|
384
|
-
subagentId: 'agent-123',
|
|
385
|
-
subagentType: 'explorer',
|
|
386
|
-
parentToolCallId: 'tool-456',
|
|
387
|
-
prompt: 'Test prompt',
|
|
388
|
-
startTime: 1000,
|
|
389
|
-
endTime: 2000,
|
|
390
|
-
success: true,
|
|
391
|
-
result: 'Test result',
|
|
392
|
-
toolExecutions: [{ toolName: 'Glob', arguments: {}, result: 'files' }],
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
const state = AgentState.initial()
|
|
396
|
-
.withMessage(new UserMessage('Hello'))
|
|
397
|
-
.withStep(3)
|
|
398
|
-
.withMetadata('key', 'value')
|
|
399
|
-
.withReasoning('Thinking...')
|
|
400
|
-
.withPlan([{ id: '1', description: 'Step', dependsOn: [], status: 'pending' }])
|
|
401
|
-
.withSubagentTrace(trace);
|
|
402
|
-
|
|
403
|
-
const json = state.toJSON();
|
|
404
|
-
|
|
405
|
-
expect(json.version).toBe('1.0.0');
|
|
406
|
-
expect(json.id).toBe(state.id);
|
|
407
|
-
expect(json.messages).toHaveLength(1);
|
|
408
|
-
expect(json.step).toBe(3);
|
|
409
|
-
expect(json.metadata).toEqual({ key: 'value' });
|
|
410
|
-
expect(json.reasoning).toEqual(['Thinking...']);
|
|
411
|
-
expect(json.plan).toHaveLength(1);
|
|
412
|
-
expect(json.subagentTraces).toHaveLength(1);
|
|
413
|
-
expect(json.subagentTraces?.[0]?.subagentId).toBe('agent-123');
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
test('toJSON() omits subagentTraces when empty', () => {
|
|
417
|
-
const state = AgentState.initial();
|
|
418
|
-
const json = state.toJSON();
|
|
419
|
-
|
|
420
|
-
expect(json.subagentTraces).toBeUndefined();
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
test('fromJSON() deserializes correctly', () => {
|
|
424
|
-
const trace: SubagentExecutionTrace = {
|
|
425
|
-
subagentId: 'agent-123',
|
|
426
|
-
subagentType: 'explorer',
|
|
427
|
-
parentToolCallId: 'tool-456',
|
|
428
|
-
prompt: 'Test',
|
|
429
|
-
startTime: 1000,
|
|
430
|
-
endTime: 2000,
|
|
431
|
-
success: true,
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const original = AgentState.initial()
|
|
435
|
-
.withMessage(new UserMessage('Hello'))
|
|
436
|
-
.withStep(3)
|
|
437
|
-
.withMetadata('key', 'value')
|
|
438
|
-
.withReasoning('Thinking...')
|
|
439
|
-
.withSubagentTrace(trace);
|
|
440
|
-
|
|
441
|
-
const json = original.toJSON();
|
|
442
|
-
const restored = AgentState.fromJSON(json);
|
|
443
|
-
|
|
444
|
-
expect(restored.id).toBe(original.id);
|
|
445
|
-
expect(restored.messages).toHaveLength(1);
|
|
446
|
-
expect(restored.step).toBe(3);
|
|
447
|
-
expect(restored.metadata).toEqual({ key: 'value' });
|
|
448
|
-
expect(restored.reasoning).toEqual(['Thinking...']);
|
|
449
|
-
expect(restored.subagentTraces).toHaveLength(1);
|
|
450
|
-
expect(restored.subagentTraces[0]?.subagentId).toBe('agent-123');
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
test('fromJSON() handles missing subagentTraces', () => {
|
|
454
|
-
const json = {
|
|
455
|
-
version: '1.0.0',
|
|
456
|
-
id: 'test-id',
|
|
457
|
-
messages: [],
|
|
458
|
-
step: 0,
|
|
459
|
-
metadata: {},
|
|
460
|
-
reasoning: [],
|
|
461
|
-
// No subagentTraces field
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
const restored = AgentState.fromJSON(json);
|
|
465
|
-
expect(restored.subagentTraces).toEqual([]);
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
test('round-trip preserves data', () => {
|
|
469
|
-
const trace: SubagentExecutionTrace = {
|
|
470
|
-
subagentId: 'agent-roundtrip',
|
|
471
|
-
subagentType: 'explorer',
|
|
472
|
-
parentToolCallId: 'tool-rt',
|
|
473
|
-
prompt: 'Round trip test',
|
|
474
|
-
startTime: 1000,
|
|
475
|
-
endTime: 2000,
|
|
476
|
-
success: true,
|
|
477
|
-
result: 'Found it',
|
|
478
|
-
toolExecutions: [
|
|
479
|
-
{
|
|
480
|
-
toolName: 'Glob',
|
|
481
|
-
toolCallId: 'glob-rt',
|
|
482
|
-
arguments: { pattern: '*.ts' },
|
|
483
|
-
result: 'file.ts',
|
|
484
|
-
duration: 100,
|
|
485
|
-
},
|
|
486
|
-
],
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
const original = AgentState.initial()
|
|
490
|
-
.withMessage(new UserMessage('Hello'))
|
|
491
|
-
.withMessage(new AssistantMessage('Hi'))
|
|
492
|
-
.withStep(5)
|
|
493
|
-
.withMetadata('count', 42)
|
|
494
|
-
.withReasoning('First')
|
|
495
|
-
.withReasoning('Second')
|
|
496
|
-
.withPlan([
|
|
497
|
-
{ id: '1', description: 'A', dependsOn: [], status: 'completed' },
|
|
498
|
-
{ id: '2', description: 'B', dependsOn: ['1'], status: 'pending' },
|
|
499
|
-
])
|
|
500
|
-
.withSubagentTrace(trace);
|
|
501
|
-
|
|
502
|
-
const json = original.toJSON();
|
|
503
|
-
const restored = AgentState.fromJSON(json);
|
|
504
|
-
|
|
505
|
-
expect(restored.id).toBe(original.id);
|
|
506
|
-
expect(restored.messages.length).toBe(original.messages.length);
|
|
507
|
-
expect(restored.step).toBe(original.step);
|
|
508
|
-
expect(restored.metadata).toEqual(original.metadata);
|
|
509
|
-
expect(restored.reasoning).toEqual([...original.reasoning]);
|
|
510
|
-
expect(restored.plan?.length).toBe(original.plan?.length);
|
|
511
|
-
expect(restored.subagentTraces.length).toBe(original.subagentTraces.length);
|
|
512
|
-
expect(restored.subagentTraces[0]?.subagentId).toBe('agent-roundtrip');
|
|
513
|
-
expect(restored.subagentTraces[0]?.toolExecutions?.[0]?.toolName).toBe('Glob');
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
test('fromJSON() throws on version mismatch', () => {
|
|
517
|
-
const json = {
|
|
518
|
-
version: '2.0.0',
|
|
519
|
-
id: 'test',
|
|
520
|
-
messages: [],
|
|
521
|
-
step: 0,
|
|
522
|
-
metadata: {},
|
|
523
|
-
reasoning: [],
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
expect(() => AgentState.fromJSON(json)).toThrow('Unsupported UAP version');
|
|
527
|
-
});
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
describe('immutability', () => {
|
|
531
|
-
test('all operations return new instances', () => {
|
|
532
|
-
const state = AgentState.initial();
|
|
533
|
-
const operations = [
|
|
534
|
-
() => state.withMessage(new UserMessage('test')),
|
|
535
|
-
() => state.withMessages([new UserMessage('test')]),
|
|
536
|
-
() => state.withContext([new UserMessage('test')]),
|
|
537
|
-
() => state.withStep(1),
|
|
538
|
-
() => state.withMetadata('key', 'value'),
|
|
539
|
-
() => state.withReasoning('thinking'),
|
|
540
|
-
() => state.withPlan([{ id: '1', description: 'test', dependsOn: [], status: 'pending' }]),
|
|
541
|
-
() => state.withSubagentTrace({
|
|
542
|
-
subagentId: 'test',
|
|
543
|
-
subagentType: 'explorer',
|
|
544
|
-
parentToolCallId: 'tool-1',
|
|
545
|
-
prompt: 'test',
|
|
546
|
-
startTime: 1000,
|
|
547
|
-
endTime: 2000,
|
|
548
|
-
success: true,
|
|
549
|
-
}),
|
|
550
|
-
];
|
|
551
|
-
|
|
552
|
-
for (const op of operations) {
|
|
553
|
-
const newState = op();
|
|
554
|
-
expect(newState).not.toBe(state);
|
|
555
|
-
expect(newState.id).not.toBe(state.id);
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
test('each state has unique ID', () => {
|
|
560
|
-
const ids = new Set<string>();
|
|
561
|
-
let state = AgentState.initial();
|
|
562
|
-
ids.add(state.id);
|
|
563
|
-
|
|
564
|
-
for (let i = 0; i < 10; i++) {
|
|
565
|
-
state = state.withStep(i);
|
|
566
|
-
expect(ids.has(state.id)).toBe(false);
|
|
567
|
-
ids.add(state.id);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
expect(ids.size).toBe(11);
|
|
571
|
-
});
|
|
572
|
-
});
|
|
573
|
-
});
|