@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,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
- }