@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,95 +0,0 @@
1
- /**
2
- * Checkpoint Types
3
- *
4
- * Type definitions for step-level persistence and session resume.
5
- *
6
- * @see UAP-1.0 Spec Section 12.4
7
- */
8
-
9
- import type { AgentStateJSON } from '../state/types.ts';
10
-
11
- /**
12
- * Checkpoint store interface.
13
- *
14
- * Implementations handle persistence of agent state at step boundaries
15
- * for crash recovery and session resume.
16
- *
17
- * @example
18
- * ```typescript
19
- * const store = fileCheckpoints({ dir: './checkpoints' });
20
- *
21
- * // Save checkpoint
22
- * await store.save('session-123', state.toJSON());
23
- *
24
- * // Load checkpoint
25
- * const saved = await store.load('session-123');
26
- * if (saved) {
27
- * const restored = AgentState.fromJSON(saved);
28
- * }
29
- * ```
30
- */
31
- export interface CheckpointStore {
32
- /**
33
- * Save a checkpoint at the current state.
34
- *
35
- * @param sessionId - Session identifier
36
- * @param state - Serialized agent state
37
- */
38
- save(sessionId: string, state: AgentStateJSON): Promise<void>;
39
-
40
- /**
41
- * Load the most recent checkpoint for a session.
42
- *
43
- * @param sessionId - Session identifier
44
- * @returns Serialized state or null if not found
45
- */
46
- load(sessionId: string): Promise<AgentStateJSON | null>;
47
-
48
- /**
49
- * Delete all checkpoints for a session.
50
- *
51
- * @param sessionId - Session identifier
52
- */
53
- delete(sessionId: string): Promise<void>;
54
-
55
- /**
56
- * List all session IDs with checkpoints.
57
- *
58
- * @returns Array of session IDs
59
- */
60
- list(): Promise<string[]>;
61
- }
62
-
63
- /**
64
- * Options for file-based checkpoint store.
65
- */
66
- export interface FileCheckpointOptions {
67
- /** Directory for checkpoint files. Default: ".checkpoints" */
68
- dir?: string;
69
- }
70
-
71
- /**
72
- * Checkpoint metadata stored alongside state.
73
- */
74
- export interface CheckpointMetadata {
75
- /** Session identifier */
76
- sessionId: string;
77
- /** Unique checkpoint ID */
78
- checkpointId: string;
79
- /** ISO 8601 timestamp */
80
- timestamp: string;
81
- /** Step number at checkpoint */
82
- step: number;
83
- /** Agent instance ID */
84
- agentId: string;
85
- }
86
-
87
- /**
88
- * Full checkpoint data including state and metadata.
89
- */
90
- export interface CheckpointData {
91
- /** Checkpoint metadata */
92
- metadata: CheckpointMetadata;
93
- /** Serialized agent state */
94
- state: AgentStateJSON;
95
- }
@@ -1,37 +0,0 @@
1
- export { loop } from './loop.ts';
2
- export { react } from './react.ts';
3
- export { plan } from './plan.ts';
4
-
5
- // Tool ordering utilities
6
- export {
7
- orderToolCalls,
8
- hasToolDependencies,
9
- hasCallDependencies,
10
- } from './tool-ordering.ts';
11
- export type { ExecutionGroup } from './tool-ordering.ts';
12
-
13
- export type {
14
- ExecutionStrategy,
15
- ExecutionContext,
16
- ExecutionResult,
17
- LoopOptions,
18
- ReactOptions,
19
- PlanOptions,
20
- AgentStrategy,
21
- GenerateResult,
22
- AgentStreamResult,
23
- AgentStreamEvent,
24
- UAPEventType,
25
- // Tool dependency types (Section 8.5)
26
- ToolDependencyOptions,
27
- ToolWithDependencies,
28
- OrderedToolCall,
29
- // Sub-agent event types (Section 8.7)
30
- SubagentEventType,
31
- SubagentEventBase,
32
- SubagentStartEvent,
33
- SubagentInnerEvent,
34
- SubagentEndEvent,
35
- SubagentEvent,
36
- OnSubagentEvent,
37
- } from './types.ts';
@@ -1,497 +0,0 @@
1
- import type { Turn, StreamEvent } from '@providerprotocol/ai';
2
- import { UserMessage } from '@providerprotocol/ai';
3
- import type { PlanStep } from '../state/index.ts';
4
- import { generateUUID } from '../utils/uuid.ts';
5
- import type {
6
- ExecutionStrategy,
7
- ExecutionContext,
8
- ExecutionResult,
9
- PlanOptions,
10
- AgentStreamResult,
11
- AgentStreamEvent,
12
- } from './types.ts';
13
-
14
- const DEFAULT_PLAN_OPTIONS: Required<PlanOptions> = {
15
- maxPlanSteps: Infinity,
16
- allowReplan: true,
17
- planSchema: {
18
- type: 'object',
19
- properties: {
20
- steps: {
21
- type: 'array',
22
- items: {
23
- type: 'object',
24
- properties: {
25
- id: { type: 'string', description: 'Unique step identifier' },
26
- description: { type: 'string', description: 'What this step does' },
27
- tool: { type: 'string', description: 'Tool to use (if applicable)' },
28
- dependsOn: {
29
- type: 'array',
30
- items: { type: 'string' },
31
- description: 'IDs of steps this depends on',
32
- },
33
- },
34
- required: ['id', 'description', 'dependsOn'],
35
- },
36
- },
37
- },
38
- required: ['steps'],
39
- },
40
- };
41
-
42
- const PLAN_PROMPT = `Create a detailed execution plan to accomplish the task.
43
- Break it down into clear steps, specifying which tool to use for each step if applicable.
44
- Include dependencies between steps (which steps must complete before others can start).
45
- Return your plan as a JSON object with a "steps" array.`;
46
-
47
- /**
48
- * Create a plan-then-execute strategy.
49
- *
50
- * Behavior:
51
- * 1. Plan: LLM generates structured plan with steps and dependencies
52
- * 2. Execute: Execute each plan step respecting dependency order
53
- * 3. Replan: If a step fails and allowReplan is true, generate new plan
54
- *
55
- * @param options - Plan configuration options
56
- * @returns ExecutionStrategy
57
- */
58
- export function plan(options: PlanOptions = {}): ExecutionStrategy {
59
- const opts = { ...DEFAULT_PLAN_OPTIONS, ...options };
60
-
61
- return {
62
- name: 'plan',
63
-
64
- async execute(context: ExecutionContext): Promise<ExecutionResult> {
65
- const { llm, input, state, strategy, signal } = context;
66
-
67
- // Add input message to state and set agentId in metadata
68
- // This ensures checkpoints include the full conversation
69
- let currentState = state
70
- .withMessage(input)
71
- .withMetadata('agentId', context.agent.id);
72
- let step = 0;
73
- let finalTurn: Turn | undefined;
74
-
75
- // Messages for LLM generation (includes input we just added)
76
- const messages = [...currentState.messages];
77
-
78
- // PLANNING PHASE
79
- step++;
80
- currentState = currentState.withStep(step);
81
-
82
- if (signal?.aborted) {
83
- throw new Error('Execution aborted');
84
- }
85
-
86
- strategy.onStepStart?.(step, currentState);
87
-
88
- // Generate the plan
89
- const planMessages = [
90
- ...messages,
91
- new UserMessage(PLAN_PROMPT),
92
- ];
93
-
94
- const planTurn = await llm.generate(planMessages);
95
-
96
- // Parse the plan from the response
97
- let planData: { steps: Array<{ id: string; description: string; tool?: string; dependsOn: string[] }> };
98
-
99
- try {
100
- if (planTurn.data) {
101
- planData = planTurn.data as typeof planData;
102
- } else {
103
- // Try to parse from text
104
- const jsonMatch = planTurn.response.text.match(/\{[\s\S]*\}/);
105
- if (jsonMatch) {
106
- planData = JSON.parse(jsonMatch[0]) as typeof planData;
107
- } else {
108
- throw new Error('Could not parse plan from response');
109
- }
110
- }
111
- } catch (err) {
112
- throw new Error(`Failed to parse execution plan: ${err instanceof Error ? err.message : String(err)}`);
113
- }
114
-
115
- // Convert to PlanStep format
116
- let planSteps: PlanStep[] = planData.steps.map((s) => ({
117
- id: s.id || generateUUID(),
118
- description: s.description,
119
- tool: s.tool,
120
- dependsOn: s.dependsOn || [],
121
- status: 'pending' as const,
122
- }));
123
-
124
- // Apply maxPlanSteps limit
125
- if (opts.maxPlanSteps !== Infinity && planSteps.length > opts.maxPlanSteps) {
126
- planSteps = planSteps.slice(0, opts.maxPlanSteps);
127
- }
128
-
129
- currentState = currentState.withPlan(planSteps);
130
- messages.push(...planTurn.messages);
131
-
132
- strategy.onStepEnd?.(step, { turn: planTurn, state: currentState });
133
-
134
- // EXECUTION PHASE
135
- const completedSteps = new Set<string>();
136
-
137
- // Execute steps in topological order
138
- while (planSteps.some((s) => s.status === 'pending')) {
139
- // Find next executable step (all dependencies completed)
140
- const nextStep = planSteps.find(
141
- (s) => s.status === 'pending'
142
- && s.dependsOn.every((depId) => completedSteps.has(depId)),
143
- );
144
-
145
- if (!nextStep) {
146
- // No step can be executed - either done or cyclic dependency
147
- break;
148
- }
149
-
150
- step++;
151
- currentState = currentState.withStep(step);
152
-
153
- if (signal?.aborted) {
154
- throw new Error('Execution aborted');
155
- }
156
-
157
- strategy.onStepStart?.(step, currentState);
158
-
159
- // Update step status to in_progress
160
- nextStep.status = 'in_progress';
161
- currentState = currentState.withPlan([...planSteps]);
162
-
163
- // Execute the step
164
- const stepPrompt = new UserMessage(
165
- `Execute step "${nextStep.id}": ${nextStep.description}${nextStep.tool ? ` using the ${nextStep.tool} tool` : ''}`,
166
- );
167
- messages.push(stepPrompt);
168
-
169
- try {
170
- const stepTurn = await llm.generate(messages);
171
- finalTurn = stepTurn;
172
-
173
- messages.push(...stepTurn.messages);
174
- currentState = currentState.withMessages(stepTurn.messages);
175
-
176
- if (stepTurn.response.hasToolCalls) {
177
- strategy.onAct?.(step, stepTurn.response.toolCalls ?? []);
178
- }
179
-
180
- if (stepTurn.toolExecutions && stepTurn.toolExecutions.length > 0) {
181
- strategy.onObserve?.(step, stepTurn.toolExecutions);
182
- }
183
-
184
- // Mark step as completed
185
- nextStep.status = 'completed';
186
- completedSteps.add(nextStep.id);
187
- currentState = currentState.withPlan([...planSteps]);
188
-
189
- strategy.onStepEnd?.(step, { turn: stepTurn, state: currentState });
190
- } catch (err) {
191
- nextStep.status = 'failed';
192
- currentState = currentState.withPlan([...planSteps]);
193
-
194
- if (opts.allowReplan) {
195
- // Could implement replanning here
196
- // For now, just continue and let the error propagate
197
- }
198
-
199
- throw err;
200
- }
201
-
202
- // Check stop condition
203
- const shouldStop = await strategy.stopCondition?.(currentState);
204
- if (shouldStop) {
205
- break;
206
- }
207
- }
208
-
209
- if (!finalTurn) {
210
- finalTurn = planTurn; // Use plan turn if no execution happened
211
- }
212
-
213
- // Include sessionId in state metadata if checkpointing is enabled
214
- let finalState = currentState;
215
- if (context.sessionId) {
216
- finalState = currentState.withMetadata('sessionId', context.sessionId);
217
- }
218
-
219
- const result: ExecutionResult = {
220
- turn: finalTurn,
221
- state: finalState,
222
- };
223
-
224
- strategy.onComplete?.(result);
225
-
226
- return result;
227
- },
228
-
229
- stream(context: ExecutionContext): AgentStreamResult {
230
- const { llm, input, state, strategy, signal } = context;
231
- const agentId = context.agent.id;
232
-
233
- let aborted = false;
234
- const abortController = new AbortController();
235
-
236
- if (signal) {
237
- signal.addEventListener('abort', () => abortController.abort());
238
- }
239
-
240
- let resolveResult: (result: ExecutionResult) => void;
241
- let rejectResult: (error: Error) => void;
242
-
243
- const resultPromise = new Promise<ExecutionResult>((resolve, reject) => {
244
- resolveResult = resolve;
245
- rejectResult = reject;
246
- });
247
-
248
- async function* generateEvents(): AsyncGenerator<AgentStreamEvent> {
249
- // Add input message to state and set agentId in metadata
250
- // This ensures checkpoints include the full conversation
251
- let currentState = state
252
- .withMessage(input)
253
- .withMetadata('agentId', context.agent.id);
254
- let step = 0;
255
- let finalTurn: Turn | undefined;
256
-
257
- // Messages for LLM generation (includes input we just added)
258
- const messages = [...currentState.messages];
259
-
260
- try {
261
- // PLANNING PHASE
262
- step++;
263
- currentState = currentState.withStep(step);
264
-
265
- if (abortController.signal.aborted) {
266
- throw new Error('Execution aborted');
267
- }
268
-
269
- strategy.onStepStart?.(step, currentState);
270
-
271
- yield {
272
- source: 'uap',
273
- uap: {
274
- type: 'step_start',
275
- step,
276
- agentId,
277
- data: { phase: 'planning' },
278
- },
279
- };
280
-
281
- const planMessages = [
282
- ...messages,
283
- new UserMessage(PLAN_PROMPT),
284
- ];
285
-
286
- const planStream = llm.stream(planMessages);
287
-
288
- for await (const event of planStream as AsyncIterable<StreamEvent>) {
289
- if (abortController.signal.aborted) {
290
- throw new Error('Execution aborted');
291
- }
292
-
293
- yield { source: 'upp', upp: event };
294
- }
295
-
296
- const planTurn = await planStream.turn;
297
-
298
- let planData: { steps: Array<{ id: string; description: string; tool?: string; dependsOn: string[] }> };
299
-
300
- try {
301
- if (planTurn.data) {
302
- planData = planTurn.data as typeof planData;
303
- } else {
304
- const jsonMatch = planTurn.response.text.match(/\{[\s\S]*\}/);
305
- if (jsonMatch) {
306
- planData = JSON.parse(jsonMatch[0]) as typeof planData;
307
- } else {
308
- throw new Error('Could not parse plan from response');
309
- }
310
- }
311
- } catch (err) {
312
- throw new Error(`Failed to parse execution plan: ${err instanceof Error ? err.message : String(err)}`);
313
- }
314
-
315
- let planSteps: PlanStep[] = planData.steps.map((s) => ({
316
- id: s.id || generateUUID(),
317
- description: s.description,
318
- tool: s.tool,
319
- dependsOn: s.dependsOn || [],
320
- status: 'pending' as const,
321
- }));
322
-
323
- if (opts.maxPlanSteps !== Infinity && planSteps.length > opts.maxPlanSteps) {
324
- planSteps = planSteps.slice(0, opts.maxPlanSteps);
325
- }
326
-
327
- currentState = currentState.withPlan(planSteps);
328
- messages.push(...planTurn.messages);
329
-
330
- yield {
331
- source: 'uap',
332
- uap: {
333
- type: 'plan_created',
334
- step,
335
- agentId,
336
- data: { plan: planSteps },
337
- },
338
- };
339
-
340
- strategy.onStepEnd?.(step, { turn: planTurn, state: currentState });
341
-
342
- yield {
343
- source: 'uap',
344
- uap: {
345
- type: 'step_end',
346
- step,
347
- agentId,
348
- data: { phase: 'planning' },
349
- },
350
- };
351
-
352
- // EXECUTION PHASE
353
- const completedSteps = new Set<string>();
354
-
355
- while (planSteps.some((s) => s.status === 'pending') && !aborted) {
356
- const nextStep = planSteps.find(
357
- (s) => s.status === 'pending'
358
- && s.dependsOn.every((depId) => completedSteps.has(depId)),
359
- );
360
-
361
- if (!nextStep) {
362
- break;
363
- }
364
-
365
- step++;
366
- currentState = currentState.withStep(step);
367
-
368
- if (abortController.signal.aborted) {
369
- throw new Error('Execution aborted');
370
- }
371
-
372
- strategy.onStepStart?.(step, currentState);
373
-
374
- yield {
375
- source: 'uap',
376
- uap: {
377
- type: 'plan_step_start',
378
- step,
379
- agentId,
380
- data: { planStep: nextStep },
381
- },
382
- };
383
-
384
- nextStep.status = 'in_progress';
385
- currentState = currentState.withPlan([...planSteps]);
386
-
387
- const stepPrompt = new UserMessage(
388
- `Execute step "${nextStep.id}": ${nextStep.description}${nextStep.tool ? ` using the ${nextStep.tool} tool` : ''}`,
389
- );
390
- messages.push(stepPrompt);
391
-
392
- const stepStream = llm.stream(messages);
393
-
394
- for await (const event of stepStream as AsyncIterable<StreamEvent>) {
395
- if (abortController.signal.aborted) {
396
- throw new Error('Execution aborted');
397
- }
398
-
399
- yield { source: 'upp', upp: event };
400
- }
401
-
402
- const stepTurn = await stepStream.turn;
403
- finalTurn = stepTurn;
404
-
405
- messages.push(...stepTurn.messages);
406
- currentState = currentState.withMessages(stepTurn.messages);
407
-
408
- if (stepTurn.response.hasToolCalls) {
409
- strategy.onAct?.(step, stepTurn.response.toolCalls ?? []);
410
-
411
- yield {
412
- source: 'uap',
413
- uap: {
414
- type: 'action',
415
- step,
416
- agentId,
417
- data: { toolCalls: stepTurn.response.toolCalls },
418
- },
419
- };
420
- }
421
-
422
- if (stepTurn.toolExecutions && stepTurn.toolExecutions.length > 0) {
423
- strategy.onObserve?.(step, stepTurn.toolExecutions);
424
-
425
- yield {
426
- source: 'uap',
427
- uap: {
428
- type: 'observation',
429
- step,
430
- agentId,
431
- data: { observations: stepTurn.toolExecutions },
432
- },
433
- };
434
- }
435
-
436
- nextStep.status = 'completed';
437
- completedSteps.add(nextStep.id);
438
- currentState = currentState.withPlan([...planSteps]);
439
-
440
- strategy.onStepEnd?.(step, { turn: stepTurn, state: currentState });
441
-
442
- yield {
443
- source: 'uap',
444
- uap: {
445
- type: 'plan_step_end',
446
- step,
447
- agentId,
448
- data: { planStep: nextStep },
449
- },
450
- };
451
-
452
- const shouldStop = await strategy.stopCondition?.(currentState);
453
- if (shouldStop) {
454
- break;
455
- }
456
- }
457
-
458
- if (!finalTurn) {
459
- finalTurn = planTurn;
460
- }
461
-
462
- // Include sessionId in state metadata if checkpointing is enabled
463
- let finalState = currentState;
464
- if (context.sessionId) {
465
- finalState = currentState.withMetadata('sessionId', context.sessionId);
466
- }
467
-
468
- const result: ExecutionResult = {
469
- turn: finalTurn,
470
- state: finalState,
471
- };
472
-
473
- strategy.onComplete?.(result);
474
- resolveResult(result);
475
- } catch (error) {
476
- const err = error instanceof Error ? error : new Error(String(error));
477
- strategy.onError?.(err, currentState);
478
- rejectResult(err);
479
- throw err;
480
- }
481
- }
482
-
483
- const iterator = generateEvents();
484
-
485
- return {
486
- [Symbol.asyncIterator]() {
487
- return iterator;
488
- },
489
- result: resultPromise,
490
- abort() {
491
- aborted = true;
492
- abortController.abort();
493
- },
494
- };
495
- },
496
- };
497
- }