@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.
Files changed (74) hide show
  1. package/README.md +333 -6
  2. package/dist/checkpoint/index.d.ts +43 -0
  3. package/dist/checkpoint/index.js +64 -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-PHI5ULBV.js +427 -0
  10. package/dist/chunk-PHI5ULBV.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-BhX9uD_d.d.ts +91 -0
  26. package/dist/types-DR02gtFv.d.ts +270 -0
  27. package/dist/types-NGQMdnaD.d.ts +65 -0
  28. package/package.json +40 -8
  29. package/.claude/settings.local.json +0 -27
  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,384 +0,0 @@
1
- import {
2
- llm,
3
- UserMessage,
4
- } from '@providerprotocol/ai';
5
- import type {
6
- LLMInstance,
7
- Message,
8
- Turn,
9
- } from '@providerprotocol/ai';
10
- import { generateUUID } from '../utils/uuid.ts';
11
- import { AgentState } from '../state/index.ts';
12
- import { loop } from '../execution/loop.ts';
13
- import type {
14
- ExecutionContext,
15
- GenerateResult,
16
- AgentStreamResult,
17
- AgentStrategy,
18
- } from '../execution/types.ts';
19
- import type { Middleware, MiddlewareContext } from '../middleware/types.ts';
20
- import type { Agent, AgentOptions } from './types.ts';
21
-
22
- /**
23
- * Create an agent instance.
24
- *
25
- * @param options - Agent configuration
26
- * @returns Agent instance
27
- *
28
- * @example
29
- * ```typescript
30
- * import { agent, AgentState } from '@providerprotocol/agents';
31
- * import { anthropic } from '@providerprotocol/ai/anthropic';
32
- *
33
- * const coder = agent({
34
- * model: anthropic('claude-sonnet-4-20250514'),
35
- * tools: [Bash, Read, Write],
36
- * system: 'You are a coding assistant.',
37
- * });
38
- *
39
- * const state = AgentState.initial();
40
- * const { turn, state: newState } = await coder.generate('Hello', state);
41
- * ```
42
- */
43
- export function agent(options: AgentOptions): Agent {
44
- const {
45
- // UAP-specific options
46
- execution = loop(),
47
- middleware = [],
48
- strategy = {},
49
- checkpoints,
50
- sessionId: providedSessionId,
51
- _llmInstance,
52
- // LLM options (passthrough to UPP)
53
- model,
54
- params = {},
55
- config,
56
- tools = [],
57
- system,
58
- structure,
59
- toolStrategy,
60
- } = options;
61
-
62
- const agentId = generateUUID();
63
- // Generate sessionId (UUIDv4) if checkpoints provided but no sessionId
64
- // Per UAP spec Section 3.4: Session IDs MUST be UUIDv4
65
- const sessionId = checkpoints ? (providedSessionId ?? generateUUID()) : providedSessionId;
66
-
67
- // Create the LLM instance with full UPP passthrough (or use injected instance for testing)
68
- const llmInstance: LLMInstance = _llmInstance ?? llm({
69
- model,
70
- params,
71
- config,
72
- system,
73
- structure,
74
- tools,
75
- toolStrategy,
76
- });
77
-
78
- /**
79
- * Normalize input to a Message.
80
- */
81
- function normalizeInput(input: string | Message): Message {
82
- if (typeof input === 'string') {
83
- return new UserMessage(input);
84
- }
85
- return input;
86
- }
87
-
88
- /**
89
- * Run middleware before hooks.
90
- */
91
- async function runBeforeMiddleware(
92
- middlewares: Middleware[],
93
- context: MiddlewareContext,
94
- ): Promise<MiddlewareContext> {
95
- let currentContext = context;
96
-
97
- for (const mw of middlewares) {
98
- if (mw.before) {
99
- const result = await mw.before(currentContext);
100
- if (result) {
101
- currentContext = result;
102
- }
103
- }
104
- }
105
-
106
- return currentContext;
107
- }
108
-
109
- /**
110
- * Run middleware after hooks (in reverse order).
111
- */
112
- async function runAfterMiddleware(
113
- middlewares: Middleware[],
114
- context: MiddlewareContext,
115
- result: GenerateResult,
116
- ): Promise<GenerateResult> {
117
- let currentResult = result;
118
-
119
- // Run in reverse order
120
- for (let i = middlewares.length - 1; i >= 0; i--) {
121
- const mw = middlewares[i];
122
- if (mw?.after) {
123
- currentResult = await mw.after(context, currentResult);
124
- }
125
- }
126
-
127
- return currentResult;
128
- }
129
-
130
- /**
131
- * Run middleware error hooks (in reverse order).
132
- */
133
- async function runErrorMiddleware(
134
- middlewares: Middleware[],
135
- context: MiddlewareContext,
136
- error: Error,
137
- ): Promise<GenerateResult | undefined> {
138
- // Run in reverse order
139
- for (let i = middlewares.length - 1; i >= 0; i--) {
140
- const mw = middlewares[i];
141
- if (mw?.onError) {
142
- const result = await mw.onError(context, error);
143
- if (result) {
144
- return result;
145
- }
146
- }
147
- }
148
-
149
- return undefined;
150
- }
151
-
152
- /**
153
- * Build execution context.
154
- */
155
- function buildExecutionContext(
156
- input: Message,
157
- state: AgentState,
158
- resolvedStrategy: AgentStrategy,
159
- signal?: AbortSignal,
160
- ): ExecutionContext {
161
- return {
162
- agent: { id: agentId, system },
163
- llm: llmInstance,
164
- input,
165
- state,
166
- tools,
167
- strategy: resolvedStrategy,
168
- signal,
169
- checkpoints,
170
- sessionId,
171
- };
172
- }
173
-
174
- const agentInstance: Agent = {
175
- id: agentId,
176
- model,
177
- tools,
178
- system,
179
-
180
- async generate(
181
- input: string | Message,
182
- state: AgentState,
183
- ): Promise<GenerateResult> {
184
- const normalizedInput = normalizeInput(input);
185
-
186
- // Create middleware context
187
- const middlewareContext: MiddlewareContext = {
188
- agent: { id: agentId, system },
189
- input: normalizedInput,
190
- state,
191
- metadata: new Map(),
192
- };
193
-
194
- try {
195
- // Run before middleware
196
- const processedContext = await runBeforeMiddleware(middleware, middlewareContext);
197
-
198
- // Build execution context
199
- const executionContext = buildExecutionContext(
200
- processedContext.input,
201
- processedContext.state,
202
- strategy,
203
- );
204
-
205
- // Execute strategy
206
- const result = await execution.execute(executionContext);
207
-
208
- // Run after middleware
209
- const finalResult = await runAfterMiddleware(
210
- middleware,
211
- processedContext,
212
- result,
213
- );
214
-
215
- return finalResult;
216
- } catch (error) {
217
- const err = error instanceof Error ? error : new Error(String(error));
218
-
219
- // Try to recover with error middleware
220
- const recovered = await runErrorMiddleware(middleware, middlewareContext, err);
221
- if (recovered) {
222
- return recovered;
223
- }
224
-
225
- throw err;
226
- }
227
- },
228
-
229
- stream(
230
- input: string | Message,
231
- state: AgentState,
232
- ): AgentStreamResult {
233
- const normalizedInput = normalizeInput(input);
234
-
235
- // Create middleware context
236
- const middlewareContext: MiddlewareContext = {
237
- agent: { id: agentId, system },
238
- input: normalizedInput,
239
- state,
240
- metadata: new Map(),
241
- };
242
-
243
- // We need to run before middleware synchronously enough to get the context
244
- // but streaming is inherently async. We'll handle this by wrapping the stream.
245
- let aborted = false;
246
- const abortController = new AbortController();
247
-
248
- let resolveResult: (result: GenerateResult) => void;
249
- let rejectResult: (error: Error) => void;
250
-
251
- const resultPromise = new Promise<GenerateResult>((resolve, reject) => {
252
- resolveResult = resolve;
253
- rejectResult = reject;
254
- });
255
-
256
- const createStream = async function* () {
257
- try {
258
- // Run before middleware
259
- const processedContext = await runBeforeMiddleware(middleware, middlewareContext);
260
-
261
- // Build execution context
262
- const executionContext = buildExecutionContext(
263
- processedContext.input,
264
- processedContext.state,
265
- strategy,
266
- abortController.signal,
267
- );
268
-
269
- // Get the stream from the execution strategy
270
- const streamResult = execution.stream(executionContext);
271
-
272
- // Yield events from the stream
273
- for await (const event of streamResult) {
274
- if (aborted) {
275
- break;
276
- }
277
- yield event;
278
- }
279
-
280
- // Get the final result
281
- const result = await streamResult.result;
282
-
283
- // Run after middleware
284
- const finalResult = await runAfterMiddleware(
285
- middleware,
286
- processedContext,
287
- result,
288
- );
289
-
290
- resolveResult(finalResult);
291
- } catch (error) {
292
- const err = error instanceof Error ? error : new Error(String(error));
293
-
294
- // Try to recover with error middleware
295
- const recovered = await runErrorMiddleware(middleware, middlewareContext, err);
296
- if (recovered) {
297
- resolveResult(recovered);
298
- return;
299
- }
300
-
301
- rejectResult(err);
302
- throw err;
303
- }
304
- };
305
-
306
- const iterator = createStream();
307
-
308
- return {
309
- [Symbol.asyncIterator]() {
310
- return iterator;
311
- },
312
- result: resultPromise,
313
- abort() {
314
- aborted = true;
315
- abortController.abort();
316
- },
317
- };
318
- },
319
-
320
- async ask(
321
- input: string | Message,
322
- state: AgentState,
323
- ): Promise<GenerateResult> {
324
- const normalizedInput = normalizeInput(input);
325
-
326
- // Generate with original state - execution strategy adds input to LLM call
327
- const result = await agentInstance.generate(normalizedInput, state);
328
-
329
- // Build final state with correct message order:
330
- // original messages + input + response messages from this turn
331
- const responseMessages = result.state.messages.slice(state.messages.length);
332
- const finalState = state
333
- .withMessage(normalizedInput)
334
- .withMessages(responseMessages)
335
- .withStep(result.state.step);
336
-
337
- // Preserve metadata from execution
338
- let stateWithMetadata = finalState;
339
- for (const [key, value] of Object.entries(result.state.metadata)) {
340
- stateWithMetadata = stateWithMetadata.withMetadata(key, value);
341
- }
342
-
343
- // Preserve reasoning traces
344
- for (const reasoning of result.state.reasoning) {
345
- stateWithMetadata = stateWithMetadata.withReasoning(reasoning);
346
- }
347
-
348
- // Preserve plan if present
349
- if (result.state.plan) {
350
- stateWithMetadata = stateWithMetadata.withPlan([...result.state.plan]);
351
- }
352
-
353
- return {
354
- turn: result.turn,
355
- state: stateWithMetadata,
356
- };
357
- },
358
-
359
- async query(input: string | Message): Promise<Turn> {
360
- const initialState = AgentState.initial();
361
- const result = await agentInstance.generate(input, initialState);
362
- return result.turn;
363
- },
364
- };
365
-
366
- return agentInstance;
367
- }
368
-
369
- export type { Agent, AgentOptions } from './types.ts';
370
- export type {
371
- GenerateResult,
372
- AgentStreamResult,
373
- AgentStreamEvent,
374
- UAPEventType,
375
- AgentStrategy,
376
- // Sub-agent event types (Section 8.7)
377
- SubagentEventType,
378
- SubagentEventBase,
379
- SubagentStartEvent,
380
- SubagentInnerEvent,
381
- SubagentEndEvent,
382
- SubagentEvent,
383
- OnSubagentEvent,
384
- } from '../execution/types.ts';
@@ -1,91 +0,0 @@
1
- import type {
2
- ModelReference,
3
- Tool,
4
- Turn,
5
- Message,
6
- LLMInstance,
7
- LLMOptions,
8
- } from '@providerprotocol/ai';
9
- import type { AgentState } from '../state/index.ts';
10
- import type {
11
- ExecutionStrategy,
12
- AgentStrategy,
13
- GenerateResult,
14
- AgentStreamResult,
15
- } from '../execution/types.ts';
16
- import type { Middleware } from '../middleware/types.ts';
17
- import type { CheckpointStore } from '../checkpoint/types.ts';
18
-
19
- /**
20
- * Options for creating an agent.
21
- * Extends LLMOptions for full UPP passthrough.
22
- */
23
- export interface AgentOptions extends Partial<Omit<LLMOptions, 'model'>> {
24
- /** Model reference from a UPP provider factory */
25
- model: ModelReference;
26
- /** Execution strategy. Default: loop() */
27
- execution?: ExecutionStrategy;
28
- /** Ordered middleware pipeline */
29
- middleware?: Middleware[];
30
- /** Agent lifecycle hooks */
31
- strategy?: AgentStrategy;
32
- /** Checkpoint store for step-level persistence */
33
- checkpoints?: CheckpointStore;
34
- /** Session identifier for checkpointing (auto-generated if not provided) */
35
- sessionId?: string;
36
- /** @internal Pre-created LLM instance for testing */
37
- _llmInstance?: LLMInstance;
38
- }
39
-
40
- /**
41
- * Agent interface.
42
- */
43
- export interface Agent {
44
- /** Unique agent identifier (UUIDv4) */
45
- readonly id: string;
46
- /** The bound model */
47
- readonly model: ModelReference;
48
- /** Available tools */
49
- readonly tools: Tool[];
50
- /** System prompt */
51
- readonly system?: string;
52
-
53
- /**
54
- * Execute agent and return Turn with new state.
55
- *
56
- * @param input - User input (string or Message)
57
- * @param state - Current immutable state
58
- * @returns Promise resolving to { turn, state }
59
- */
60
- generate(input: string | Message, state: AgentState): Promise<GenerateResult>;
61
-
62
- /**
63
- * Execute agent with streaming.
64
- *
65
- * @param input - User input (string or Message)
66
- * @param state - Current immutable state
67
- * @returns AgentStreamResult with async iterator and result promise
68
- */
69
- stream(input: string | Message, state: AgentState): AgentStreamResult;
70
-
71
- /**
72
- * Multi-turn execution with automatic history management.
73
- * Appends input to state, calls generate(), appends response to returned state.
74
- *
75
- * @param input - User input (string or Message)
76
- * @param state - Current immutable state
77
- * @returns Promise resolving to { turn, state }
78
- */
79
- ask(input: string | Message, state: AgentState): Promise<GenerateResult>;
80
-
81
- /**
82
- * Stateless single-turn execution.
83
- * Creates ephemeral state, executes, and discards state.
84
- *
85
- * @param input - User input (string or Message)
86
- * @returns Promise resolving to Turn
87
- */
88
- query(input: string | Message): Promise<Turn>;
89
- }
90
-
91
- export type { GenerateResult, AgentStreamResult, AgentStrategy } from '../execution/types.ts';
@@ -1,126 +0,0 @@
1
- /**
2
- * File-based Checkpoint Store
3
- *
4
- * Reference implementation of CheckpointStore using the filesystem.
5
- *
6
- * @see UAP-1.0 Spec Section 12.4.3
7
- */
8
-
9
- import { mkdir, readdir, rm } from 'node:fs/promises';
10
- import { join } from 'node:path';
11
- import type { AgentStateJSON } from '../state/types.ts';
12
- import type { CheckpointStore, FileCheckpointOptions, CheckpointMetadata } from './types.ts';
13
- import { generateUUID } from '../utils/uuid.ts';
14
-
15
- const DEFAULT_DIR = '.checkpoints';
16
-
17
- /**
18
- * Create a file-based checkpoint store.
19
- *
20
- * Stores checkpoints as JSON files in a directory structure:
21
- * ```
22
- * {dir}/
23
- * {sessionId}/
24
- * checkpoint.json # Latest state
25
- * metadata.json # Session metadata
26
- * ```
27
- *
28
- * @param options - Configuration options
29
- * @returns CheckpointStore implementation
30
- *
31
- * @example
32
- * ```typescript
33
- * import { fileCheckpoints } from '@providerprotocol/agents/checkpoint';
34
- *
35
- * const store = fileCheckpoints({ dir: './my-checkpoints' });
36
- *
37
- * // Save a checkpoint
38
- * await store.save('session-123', state.toJSON());
39
- *
40
- * // Load a checkpoint
41
- * const saved = await store.load('session-123');
42
- * ```
43
- */
44
- export function fileCheckpoints(options: FileCheckpointOptions = {}): CheckpointStore {
45
- const dir = options.dir ?? DEFAULT_DIR;
46
-
47
- /**
48
- * Ensure session directory exists.
49
- */
50
- async function ensureSessionDir(sessionId: string): Promise<string> {
51
- const sessionDir = join(dir, sessionId);
52
- await mkdir(sessionDir, { recursive: true });
53
- return sessionDir;
54
- }
55
-
56
- /**
57
- * Get paths for checkpoint files.
58
- */
59
- function getPaths(sessionId: string): { checkpointPath: string; metadataPath: string } {
60
- const sessionDir = join(dir, sessionId);
61
- return {
62
- checkpointPath: join(sessionDir, 'checkpoint.json'),
63
- metadataPath: join(sessionDir, 'metadata.json'),
64
- };
65
- }
66
-
67
- return {
68
- async save(sessionId: string, state: AgentStateJSON): Promise<void> {
69
- await ensureSessionDir(sessionId);
70
- const { checkpointPath, metadataPath } = getPaths(sessionId);
71
-
72
- // Build metadata
73
- const metadata: CheckpointMetadata = {
74
- sessionId,
75
- checkpointId: generateUUID(),
76
- timestamp: new Date().toISOString(),
77
- step: state.step,
78
- agentId: state.metadata.agentId as string ?? 'unknown',
79
- };
80
-
81
- // Write checkpoint first, then metadata (sequential to avoid race conditions)
82
- await Bun.write(checkpointPath, JSON.stringify(state, null, 2));
83
- await Bun.write(metadataPath, JSON.stringify(metadata, null, 2));
84
- },
85
-
86
- async load(sessionId: string): Promise<AgentStateJSON | null> {
87
- const { checkpointPath } = getPaths(sessionId);
88
-
89
- try {
90
- const file = Bun.file(checkpointPath);
91
- const exists = await file.exists();
92
- if (!exists) {
93
- return null;
94
- }
95
- const content = await file.text();
96
- return JSON.parse(content) as AgentStateJSON;
97
- } catch {
98
- // File doesn't exist or is invalid
99
- return null;
100
- }
101
- },
102
-
103
- async delete(sessionId: string): Promise<void> {
104
- const sessionDir = join(dir, sessionId);
105
- try {
106
- await rm(sessionDir, { recursive: true, force: true });
107
- } catch {
108
- // Directory might not exist, ignore
109
- }
110
- },
111
-
112
- async list(): Promise<string[]> {
113
- try {
114
- // Ensure base directory exists
115
- await mkdir(dir, { recursive: true });
116
-
117
- const entries = await readdir(dir, { withFileTypes: true });
118
- return entries
119
- .filter((entry) => entry.isDirectory())
120
- .map((entry) => entry.name);
121
- } catch {
122
- return [];
123
- }
124
- },
125
- };
126
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * Checkpoint Module
3
- *
4
- * Step-level persistence for crash recovery and session resume.
5
- *
6
- * @example
7
- * ```typescript
8
- * import { fileCheckpoints } from '@providerprotocol/agents/checkpoint';
9
- * import { agent, AgentState } from '@providerprotocol/agents';
10
- *
11
- * const store = fileCheckpoints({ dir: './checkpoints' });
12
- *
13
- * // Resume or start fresh
14
- * const saved = await store.load('my-session');
15
- * const initialState = saved
16
- * ? AgentState.fromJSON(saved)
17
- * : AgentState.initial();
18
- *
19
- * // Execute with checkpointing
20
- * const coder = agent({
21
- * model: anthropic('claude-sonnet-4-20250514'),
22
- * tools: [Bash, Read],
23
- * checkpoints: store,
24
- * sessionId: 'my-session',
25
- * });
26
- *
27
- * const { turn, state } = await coder.generate('Fix the bug', initialState);
28
- * ```
29
- *
30
- * @packageDocumentation
31
- */
32
-
33
- export { fileCheckpoints } from './file.ts';
34
-
35
- export type {
36
- CheckpointStore,
37
- FileCheckpointOptions,
38
- CheckpointMetadata,
39
- CheckpointData,
40
- } from './types.ts';