@dex-ai/sdk 0.1.30

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 (85) hide show
  1. package/README.md +308 -0
  2. package/dist/agent.d.ts +181 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +41 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/context.d.ts +68 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +8 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/create-agent.d.ts +7 -0
  11. package/dist/create-agent.d.ts.map +1 -0
  12. package/dist/create-agent.js +205 -0
  13. package/dist/create-agent.js.map +1 -0
  14. package/dist/extension.d.ts +162 -0
  15. package/dist/extension.d.ts.map +1 -0
  16. package/dist/extension.js +20 -0
  17. package/dist/extension.js.map +1 -0
  18. package/dist/generate.d.ts +10 -0
  19. package/dist/generate.d.ts.map +1 -0
  20. package/dist/generate.js +839 -0
  21. package/dist/generate.js.map +1 -0
  22. package/dist/index.d.ts +26 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +16 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/message.d.ts +89 -0
  27. package/dist/message.d.ts.map +1 -0
  28. package/dist/message.js +17 -0
  29. package/dist/message.js.map +1 -0
  30. package/dist/messages.d.ts +98 -0
  31. package/dist/messages.d.ts.map +1 -0
  32. package/dist/messages.js +339 -0
  33. package/dist/messages.js.map +1 -0
  34. package/dist/model.d.ts +39 -0
  35. package/dist/model.d.ts.map +1 -0
  36. package/dist/model.js +11 -0
  37. package/dist/model.js.map +1 -0
  38. package/dist/provider.d.ts +157 -0
  39. package/dist/provider.d.ts.map +1 -0
  40. package/dist/provider.js +39 -0
  41. package/dist/provider.js.map +1 -0
  42. package/dist/resolve-schema.d.ts +44 -0
  43. package/dist/resolve-schema.d.ts.map +1 -0
  44. package/dist/resolve-schema.js +367 -0
  45. package/dist/resolve-schema.js.map +1 -0
  46. package/dist/schema.d.ts +80 -0
  47. package/dist/schema.d.ts.map +1 -0
  48. package/dist/schema.js +90 -0
  49. package/dist/schema.js.map +1 -0
  50. package/dist/tool-dispatch.d.ts +24 -0
  51. package/dist/tool-dispatch.d.ts.map +1 -0
  52. package/dist/tool-dispatch.js +120 -0
  53. package/dist/tool-dispatch.js.map +1 -0
  54. package/dist/tool-result-cache.d.ts +43 -0
  55. package/dist/tool-result-cache.d.ts.map +1 -0
  56. package/dist/tool-result-cache.js +118 -0
  57. package/dist/tool-result-cache.js.map +1 -0
  58. package/dist/tool.d.ts +96 -0
  59. package/dist/tool.d.ts.map +1 -0
  60. package/dist/tool.js +29 -0
  61. package/dist/tool.js.map +1 -0
  62. package/dist/util.d.ts +26 -0
  63. package/dist/util.d.ts.map +1 -0
  64. package/dist/util.js +104 -0
  65. package/dist/util.js.map +1 -0
  66. package/package.json +41 -0
  67. package/src/agent.ts +235 -0
  68. package/src/context.ts +82 -0
  69. package/src/create-agent.ts +237 -0
  70. package/src/extension.ts +244 -0
  71. package/src/generate.ts +943 -0
  72. package/src/index.ts +113 -0
  73. package/src/message.ts +114 -0
  74. package/src/messages.test.ts +299 -0
  75. package/src/messages.ts +423 -0
  76. package/src/model.ts +43 -0
  77. package/src/provider.ts +187 -0
  78. package/src/resolve-schema.test.ts +351 -0
  79. package/src/resolve-schema.ts +426 -0
  80. package/src/schema.ts +131 -0
  81. package/src/tool-dispatch.ts +166 -0
  82. package/src/tool-result-cache.test.ts +182 -0
  83. package/src/tool-result-cache.ts +164 -0
  84. package/src/tool.ts +110 -0
  85. package/src/util.ts +110 -0
package/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # @dex-ai/sdk
2
+
3
+ An agent SDK for TypeScript + Bun. One package: interfaces, `Agent.create()`, and the generate loop.
4
+
5
+ ## Layout
6
+
7
+ The SDK lives in its own repo (`dex-ai-sdk`). Provider implementations live in a sibling monorepo (`dex-ai-providers`):
8
+
9
+ ```
10
+ workplace/
11
+ ├── dex-ai-sdk/ (this repo — @dex-ai/sdk)
12
+ │ ├── src/ # types + define* helpers + Agent.create + generate loop
13
+ │ ├── examples/ # 8 runnable .ts examples
14
+ │ └── docs/ # SVG diagrams
15
+ └── dex-ai-providers/ (separate repo — providers monorepo)
16
+ └── providers/
17
+ └── openai/ # @dex-ai/openai — OpenAI Chat Completions
18
+ ```
19
+
20
+ `@dex-ai/openai` is referenced from `examples/package.json` via `file:../../dex-ai-providers/providers/openai`. Clone both side by side for local dev.
21
+
22
+ ## Usage
23
+
24
+ ```ts
25
+ import { Agent, Tool } from '@dex-ai/sdk';
26
+ import { openai } from '@dex-ai/openai';
27
+ import { z } from 'zod';
28
+
29
+ const getWeather = Tool.define({
30
+ name: 'get_weather',
31
+ description: 'Look up current weather.',
32
+ parameters: z.object({ city: z.string() }),
33
+ async execute({ city }) {
34
+ return { type: 'json', value: { city, tempC: 18 } };
35
+ },
36
+ });
37
+
38
+ const agent = await Agent.create({
39
+ name: 'demo',
40
+ provider: openai({ modelId: 'gpt-4.1' }),
41
+ extensions: [{ name: 'tools', tools: [getWeather] }],
42
+ });
43
+
44
+ // Non-streaming:
45
+ const r = await agent.generate({
46
+ input: [{ role: 'user', content: [{ type: 'text', text: 'Weather in Paris?' }] }],
47
+ }).result;
48
+
49
+ // Streaming:
50
+ const h = agent.generate({ input: [/* ... */] });
51
+ for await (const part of h) {
52
+ if (part.type === 'text-delta') process.stdout.write(part.delta);
53
+ if (part.type === 'message-committed') saveToDb(part.message);
54
+ }
55
+ const result = await h.result;
56
+ ```
57
+
58
+ ## Design decisions
59
+
60
+ ### One package, two shapes
61
+
62
+ `@dex-ai/sdk` ships both the *types* (so third-party providers and extensions can depend on only the type surface via `import type`) and the *runtime* (`Agent.create` + the loop). There is no separate types-only package.
63
+
64
+ ### Content model (flat, ai-sdk aligned)
65
+
66
+ ```ts
67
+ type Content =
68
+ | { type: 'text'; text }
69
+ | { type: 'image'; image: string|Uint8Array|URL; mediaType? }
70
+ | { type: 'file'; data: string|Uint8Array|URL; mediaType; name? }
71
+ | { type: 'reasoning'; text }
72
+ | { type: 'tool-call'; toolCallId; toolName; input }
73
+ | { type: 'tool-result'; toolCallId; toolName; output: ToolOutput };
74
+
75
+ type ToolOutput =
76
+ | { type: 'content'; value: ContentPayload[] } // text / image / file
77
+ | { type: 'json'; value: unknown }
78
+ | { type: 'text'; value: string }
79
+ | { type: 'error-text'; value: string }
80
+ | { type: 'error-json'; value: unknown };
81
+ ```
82
+
83
+ ### Two contexts, two lifetimes
84
+
85
+ `AgentContext` is persistent — created by `Agent.create()`, owns `messages: Message[]`, dies with `agent.dispose()`. `GenerateContext` is fresh per `generate()` call, owns the in-flight assistant `content: Content[]`, lives across all iterations of one generate.
86
+
87
+ ### One `generate()` method
88
+
89
+ `agent.generate(opts)` returns an `AgentStream`: async-iterable AND has a `.result: Promise<GenerateResult>`. Same run drives both.
90
+
91
+ ### Approval via `onToolCall`
92
+
93
+ No dedicated approval surface. An approver extension implements `onToolCall`: return a `ToolResult` (with `error-text`) to reject, a rewritten `ToolCall` to modify, or `void` to pass through. Rejections flow back to the LLM as data.
94
+
95
+ ### Schema-agnostic
96
+
97
+ `Tool.parameters` takes any [Standard Schema](https://standardschema.dev) implementation — zod, valibot, arktype. The provider converts to JSON Schema.
98
+
99
+ ## Core Loop Architecture
100
+
101
+ The generate loop is event-driven. Extensions subscribe to events via `ext.on['event-name']`. The loop orchestrates model calls, tool dispatch, and context injection purely through events.
102
+
103
+ ### Agent Creation (`Agent.create`)
104
+
105
+ 1. Collect extensions from options
106
+ 2. Run `ext.init(actx)` on each extension (registration order)
107
+ 3. Set up tool result cache (if configured)
108
+ 4. Assemble system prompt into a single system message:
109
+ - `opts.systemPrompt` (base identity/instructions)
110
+ - **Skills** from extensions: evaluate `skill.when()`, render `skill.content`, build catalog
111
+ - **`session-start`** event: extensions yield `AsyncIterable<Content>` for system prompt
112
+ 5. Reorder messages: system first, then conversation history
113
+ 6. Append seed messages
114
+
115
+ ### Generate Loop Phases
116
+
117
+ Each `agent.generate(opts)` call runs:
118
+
119
+ #### Phase A: Input Processing
120
+
121
+ | Step | Action | Event |
122
+ |------|--------|-------|
123
+ | A1 | Extensions augment input messages | `generate-input` |
124
+ | A2 | Commit input to `actx.messages` | `message-committed` (stream) |
125
+ | A3 | Collect ephemeral turn context | `generate-start` → `AsyncIterable<Content>` |
126
+ | A4 | Build ephemeral context message (step 0 only, never persisted) | — |
127
+
128
+ #### Phase B: Iteration Loop (one model call + tool execution)
129
+
130
+ | Step | Action | Event |
131
+ |------|--------|-------|
132
+ | B1 | Build `ModelRequest` (messages + tools + params) | — |
133
+ | B2 | Inject ephemeral context (step 0 only) | — |
134
+ | B3 | Extensions transform the request (reducer) | `model-start` → `ModelRequest \| void` |
135
+ | B4 | Compute cache breakpoints (Anthropic-style) | — |
136
+ | B5 | Iteration begins | `message-start` |
137
+ | B6 | Stream from model — consume `model.stream(req)` | `text-delta`, `reasoning-delta`, `tool-call-delta`, `tool-call`, `message-stop`, `finish` |
138
+ | B7 | Commit assistant message | `message-committed` (stream) |
139
+ | B8 | Assistant output finalized | `message-stop` |
140
+ | B9 | Update `actx.tokenCount` | — |
141
+ | B10 | Model call complete | `model-stop` |
142
+
143
+ #### Phase C: Tool Dispatch (per tool call)
144
+
145
+ | Step | Action | Event |
146
+ |------|--------|-------|
147
+ | C1 | Extensions intercept/modify/short-circuit | `tool-start` → `ToolCall \| ToolResult \| void` |
148
+ | C2 | Validate input against tool schema | — |
149
+ | C3 | Execute tool (raced with timeout + abort) | `tool-execute-start` (stream) |
150
+ | C4 | Extensions transform result (reducer) | `tool-stop` → `ToolResult \| void` |
151
+ | C5 | Commit tool-result message | `tool-execute-finish` + `message-committed` (stream) |
152
+
153
+ #### Phase D: Continue or Stop
154
+
155
+ ```
156
+ shouldContinue = finishReason == "tool-calls"
157
+ && pendingToolCalls > 0
158
+ && step + 1 < maxSteps
159
+ ```
160
+
161
+ If continuing → increment step, go back to Phase B.
162
+
163
+ #### Phase E: Finalization
164
+
165
+ | Step | Action | Event |
166
+ |------|--------|-------|
167
+ | E1 | Turn complete | `generate-stop` (always fires, even on error) |
168
+ | E2 | Build `GenerateResult` | — |
169
+ | E3 | Close stream | `generate-finish` (stream) |
170
+
171
+ ### Event Hook Summary
172
+
173
+ ```
174
+ ┌─ Session Lifecycle ─────────────────────────────────────────────┐
175
+ │ session-start → yield Content for system prompt │
176
+ │ session-stop → cleanup on agent.dispose() │
177
+ └─────────────────────────────────────────────────────────────────┘
178
+
179
+ ┌─ Per generate() call ───────────────────────────────────────────┐
180
+ │ generate-input → augment input messages │
181
+ │ generate-start → yield ephemeral turn context │
182
+ │ │
183
+ │ ┌─ Per iteration (model call) ──────────────────────────────┐ │
184
+ │ │ model-start → transform ModelRequest (reducer) │ │
185
+ │ │ message-start → observe │ │
186
+ │ │ text-delta → observe streaming text │ │
187
+ │ │ reasoning-delta → observe reasoning tokens │ │
188
+ │ │ tool-call-delta → observe tool JSON streaming │ │
189
+ │ │ message-stop → observe committed message │ │
190
+ │ │ model-stop → observe │ │
191
+ │ │ │ │
192
+ │ │ ┌─ Per tool call ─────────────────────────────────────┐ │ │
193
+ │ │ │ tool-start → intercept / modify / short-circuit │ │ │
194
+ │ │ │ tool-stop → transform result (reducer) │ │ │
195
+ │ │ └─────────────────────────────────────────────────────┘ │ │
196
+ │ └───────────────────────────────────────────────────────────┘ │
197
+ │ │
198
+ │ generate-stop → finalize (always fires) │
199
+ └─────────────────────────────────────────────────────────────────┘
200
+
201
+ ┌─ Cross-cutting ─────────────────────────────────────────────────┐
202
+ │ error → observe any error + ErrorSource context │
203
+ └─────────────────────────────────────────────────────────────────┘
204
+ ```
205
+
206
+ ### Extension Interaction Patterns
207
+
208
+ | Pattern | Events | Description |
209
+ |---------|--------|-------------|
210
+ | **Reducer** | `model-start`, `tool-start`, `tool-stop` | Return transformed value or void (no-op) |
211
+ | **Observer** | `text-delta`, `message-stop`, `generate-stop` | Observe but don't modify |
212
+ | **Context injection** | `session-start`, `generate-start`, `generate-input` | Yield content for system prompt or turn context |
213
+ | **Short-circuit** | `tool-start` | Return a `ToolResult` to skip tool execution |
214
+ | **Ephemeral context** | `generate-start` | Content injected into step 0 request but never persisted |
215
+
216
+ ### Stream Parts
217
+
218
+ The `AgentStream` merges **provider parts** with **loop-synthesized parts**:
219
+
220
+ - **Provider**: `response-start`, `text-delta`, `reasoning-delta`, `tool-call-delta`, `tool-call`, `message-stop`, `finish`, `response-stop`, `error`, `abort`
221
+ - **Loop**: `generate-start`, `generate-finish`, `iteration-start`, `iteration-finish`, `tool-execute-start`, `tool-execute-finish`, `message-committed`, `extension-info`
222
+
223
+ ### Message Integrity (`Messages` class)
224
+
225
+ The conversation history is protected by the `Messages` class — a guarded container that prevents extensions from corrupting the message sequence.
226
+
227
+ #### The problem
228
+
229
+ Extensions previously cast `actx.messages as Message[]` to bypass the `ReadonlyArray` type and directly mutate the array (splice, push, etc.). This caused multiple corruption bugs where tool-calls had no matching tool-results, or role alternation was violated.
230
+
231
+ #### The solution
232
+
233
+ `actx.messages` is now backed by a **Proxy** that traps all direct mutations at runtime:
234
+
235
+ ```ts
236
+ // These all throw at runtime — even with `as any` casts:
237
+ actx.messages.push(msg); // ❌ "Direct .push() is not allowed"
238
+ actx.messages.splice(0, 1); // ❌ "Direct .splice() is not allowed"
239
+ actx.messages[0] = msg; // ❌ "Direct index assignment is not allowed"
240
+ actx.messages.length = 0; // ❌ "Direct array mutation is not allowed"
241
+ ```
242
+
243
+ Extensions MUST use `actx.messageStore` for controlled mutations:
244
+
245
+ ```ts
246
+ const store = actx.messageStore;
247
+
248
+ // Append (validates after)
249
+ store.append(message);
250
+
251
+ // Splice with validation
252
+ store.splice(start, deleteCount, ...replacements);
253
+
254
+ // Replace a range
255
+ store.replaceRange(start, end, newMessages);
256
+
257
+ // Replace single message
258
+ store.replace(index, newMessage);
259
+
260
+ // Batch mode — defer validation for multi-step mutations
261
+ store.batch();
262
+ store.splice(0, 10, ...compressed);
263
+ store.append(summary);
264
+ store.commit(); // validates here, throws if invalid
265
+
266
+ // Or use transaction() for auto-commit:
267
+ store.transaction(() => {
268
+ store.splice(warmStart, warmCount, ...summarized);
269
+ });
270
+ ```
271
+
272
+ #### Invariants enforced
273
+
274
+ Every mutation (or `commit()` in batch mode) validates:
275
+
276
+ 1. **System messages at front** — contiguous prefix only
277
+ 2. **First non-system is `user`** — conversation must start with a user turn
278
+ 3. **Role alternation** — `user` → `assistant` → `tool`* → `user` (tool only after assistant)
279
+ 4. **Tool-call/tool-result pairing** — every `tool-call` in an assistant message must have a matching `tool-result`
280
+ 5. **No orphaned tool-results** — every `tool-result` must reference a preceding `tool-call`
281
+
282
+ Violations throw with a detailed error listing the indices and what went wrong.
283
+
284
+ ### Error Handling
285
+
286
+ - **`reportError(err, source)`** — structured logging + emits `extension-info` error part + calls `ext.on['error']` on all extensions
287
+ - **Orphaned tool-call repair** — if the loop throws mid-tool-dispatch, synthesizes error `tool-result` messages so history stays valid
288
+ - **`generate-stop` always fires** — even in catch path, so session persistence can flush
289
+
290
+ ## Diagrams
291
+
292
+ | File | Shows |
293
+ |------|-------|
294
+ | `docs/architecture.svg` | Top-level layers: App → Agent → {Provider, Extensions → Tools}. |
295
+ | `docs/packages.svg` | Package graph: @dex-ai/sdk, @dex-ai/openai, examples, deps. |
296
+ | `docs/interfaces.svg` | Every exported interface on one canvas. |
297
+ | `docs/contexts.svg` | AgentContext vs GenerateContext — lifetimes, mutation, integrity. |
298
+ | `docs/core-loop-sequence.svg` | Sequence diagram of one `generate()` call with hook fire points. |
299
+ | `docs/extension-interface.svg` | All hooks grouped by lifetime and kind. |
300
+ | `docs/stream-parts.svg` | `AgentStreamPart` timeline: provider + loop-synthesized parts. |
301
+
302
+ ## Dev
303
+
304
+ ```bash
305
+ bun install
306
+ bun run typecheck
307
+ bun run examples/01-non-streaming.ts
308
+ ```
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Agent — the interface AND the core loop.
3
+ *
4
+ * One method: generate(). It always returns an AgentStream: an object
5
+ * that is BOTH an AsyncIterable<AgentStreamPart> AND carries a
6
+ * .result: Promise<GenerateResult>. Callers pick the consumption mode.
7
+ *
8
+ * // non-streaming
9
+ * const r = await agent.generate(opts).result;
10
+ *
11
+ * // streaming
12
+ * const h = agent.generate(opts);
13
+ * for await (const part of h) render(part);
14
+ * const r = await h.result;
15
+ *
16
+ * The loop runs exactly once per generate() call regardless of which
17
+ * consumer drives it. Both .result and the iterator reflect the same run.
18
+ *
19
+ * Each generate() call:
20
+ * 1. appends input messages to agent.context.messages
21
+ * 2. creates a fresh GenerateContext
22
+ * 3. runs the loop: request → stream → tool dispatch → repeat
23
+ * 4. commits the final assistant Message to agent.context.messages
24
+ * 5. resolves .result with GenerateResult
25
+ */
26
+ import type { Message } from "./message";
27
+ import type { StreamPart, FinishReason, Usage } from "./provider";
28
+ import type { ToolCall, ToolResult } from "./tool";
29
+ import type { Extension } from "./extension";
30
+ import type { AgentContext } from "./context";
31
+ import type { ThinkingLevel } from "./model";
32
+ import type { ToolResultCacheConfig } from "./tool-result-cache";
33
+ /**
34
+ * Loop-synthesized parts. Not emitted by the Provider; emitted by the
35
+ * Agent around the provider stream. Let stream consumers observe the
36
+ * loop lifecycle without subscribing to hooks.
37
+ */
38
+ export interface GenerateStartPart {
39
+ readonly type: "generate-start";
40
+ readonly maxSteps: number;
41
+ readonly startedAt: number;
42
+ readonly generateId: string;
43
+ }
44
+ export interface GenerateFinishPart {
45
+ readonly type: "generate-finish";
46
+ readonly result: GenerateResult;
47
+ }
48
+ export interface IterationStartPart {
49
+ readonly type: "iteration-start";
50
+ readonly step: number;
51
+ readonly startedAt: number;
52
+ }
53
+ export interface IterationFinishPart {
54
+ readonly type: "iteration-finish";
55
+ readonly step: number;
56
+ readonly endedAt: number;
57
+ readonly finishReason: FinishReason;
58
+ readonly usage: Usage;
59
+ }
60
+ export interface ToolExecuteStartPart {
61
+ readonly type: "tool-execute-start";
62
+ readonly call: ToolCall;
63
+ readonly startedAt: number;
64
+ }
65
+ export interface ToolExecuteFinishPart {
66
+ readonly type: "tool-execute-finish";
67
+ readonly call: ToolCall;
68
+ readonly result: ToolResult;
69
+ readonly startedAt: number;
70
+ readonly endedAt: number;
71
+ }
72
+ /**
73
+ * Emitted when the loop commits a message to AgentContext.messages.
74
+ * Fires for every committed message: tool-result messages (mid-loop)
75
+ * and the final assistant message (end of loop).
76
+ */
77
+ export interface MessageCommittedPart {
78
+ readonly type: "message-committed";
79
+ readonly message: Message;
80
+ /** Index of the committed message in AgentContext.messages. */
81
+ readonly index: number;
82
+ }
83
+ /** Emitted by extensions via emit() for display-only info. Never enters model context. */
84
+ export interface ExtensionInfoPart {
85
+ readonly type: "extension-info";
86
+ readonly extension: string;
87
+ readonly level: "debug" | "info" | "warn" | "error";
88
+ readonly text: string;
89
+ }
90
+ export type LoopStreamPart = GenerateStartPart | GenerateFinishPart | IterationStartPart | IterationFinishPart | ToolExecuteStartPart | ToolExecuteFinishPart | MessageCommittedPart | ExtensionInfoPart;
91
+ /**
92
+ * The unified stream-part union exposed through agent.generate().
93
+ * Superset of provider StreamPart with loop-synthesized parts.
94
+ */
95
+ export type AgentStreamPart = StreamPart | LoopStreamPart;
96
+ export interface CreateAgentOptions {
97
+ readonly name?: string;
98
+ /** Provider extension name (must match an extension with models). */
99
+ readonly provider: string;
100
+ /** Model ID within the provider. */
101
+ readonly model: string;
102
+ readonly extensions?: ReadonlyArray<Extension>;
103
+ /** System prompt — prepended as a system message. */
104
+ readonly systemPrompt?: string;
105
+ /** Optional seed conversation (priming, restored history). */
106
+ readonly messages?: ReadonlyArray<Message>;
107
+ readonly signal?: AbortSignal;
108
+ readonly metadata?: Readonly<Record<string, unknown>>;
109
+ /** Configuration for caching large tool results to disk. */
110
+ readonly toolResultCache?: ToolResultCacheConfig;
111
+ }
112
+ /**
113
+ * input: typically a single user turn, but an array so callers can
114
+ * prime a conversation with synthetic messages. All messages are
115
+ * appended to agent.context.messages in order, before iter 1.
116
+ */
117
+ export interface GenerateOptions {
118
+ readonly input: ReadonlyArray<Message>;
119
+ readonly maxSteps?: number;
120
+ readonly toolConcurrency?: number;
121
+ readonly signal?: AbortSignal;
122
+ readonly temperature?: number;
123
+ readonly topP?: number;
124
+ readonly maxTokens?: number;
125
+ readonly stopSequences?: ReadonlyArray<string>;
126
+ readonly seed?: number;
127
+ readonly providerOptions?: Readonly<Record<string, unknown>>;
128
+ /** Override provider for this call (matches extension name). */
129
+ readonly provider?: string;
130
+ /** Override model for this call (matches model id within the provider). */
131
+ readonly model?: string;
132
+ /** Tool execution timeout in ms. Default: 120000 (2 min). */
133
+ readonly toolTimeoutMs?: number;
134
+ /**
135
+ * Enable extended thinking / chain-of-thought reasoning.
136
+ * Pass a ThinkingLevel or `{ budgetTokens: N }` for explicit budget.
137
+ * The provider maps levels to token budgets.
138
+ */
139
+ readonly thinking?: ThinkingLevel | {
140
+ readonly budgetTokens: number;
141
+ };
142
+ }
143
+ export interface GenerateResult {
144
+ /** Unique identifier for this generate() call. All committed messages share this ID. */
145
+ readonly generateId: string;
146
+ /** The assistant message produced by this generate (also committed to history). */
147
+ readonly message: Message;
148
+ /** Tool-result messages committed during this generate, in order. */
149
+ readonly toolResultMessages: ReadonlyArray<Message>;
150
+ readonly usage: Usage;
151
+ readonly finishReason: FinishReason;
152
+ /** Number of iterations executed (>= 1). */
153
+ readonly steps: number;
154
+ }
155
+ /**
156
+ * The return value of agent.generate().
157
+ *
158
+ * AsyncIterable<AgentStreamPart> — yields every provider part plus
159
+ * loop-synthesized parts live, in order.
160
+ * .result — Promise resolving to the final GenerateResult. Resolves
161
+ * whether or not anyone iterates; rejects on unrecovered error.
162
+ *
163
+ * Implementations in dex-core drive the loop once, then fan out to
164
+ * both the iterator and the .result promise.
165
+ */
166
+ export interface AgentStream extends AsyncIterable<AgentStreamPart> {
167
+ readonly result: Promise<GenerateResult>;
168
+ }
169
+ export interface Agent {
170
+ readonly context: AgentContext;
171
+ /** Run the loop. Returns a handle that supports both streaming and awaited-result consumption. */
172
+ generate(opts: GenerateOptions): AgentStream;
173
+ /** Release long-lived resources. Calls dispose() on every extension in reverse order. */
174
+ dispose(): Promise<void>;
175
+ }
176
+ /** Signature for Agent.create. Useful when passing the factory around. */
177
+ export type CreateAgent = (opts: CreateAgentOptions) => Promise<Agent>;
178
+ export declare const Agent: {
179
+ create: (opts: CreateAgentOptions) => Promise<Agent>;
180
+ };
181
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Agent — the interface AND the core loop.
3
+ *
4
+ * One method: generate(). It always returns an AgentStream: an object
5
+ * that is BOTH an AsyncIterable<AgentStreamPart> AND carries a
6
+ * .result: Promise<GenerateResult>. Callers pick the consumption mode.
7
+ *
8
+ * // non-streaming
9
+ * const r = await agent.generate(opts).result;
10
+ *
11
+ * // streaming
12
+ * const h = agent.generate(opts);
13
+ * for await (const part of h) render(part);
14
+ * const r = await h.result;
15
+ *
16
+ * The loop runs exactly once per generate() call regardless of which
17
+ * consumer drives it. Both .result and the iterator reflect the same run.
18
+ *
19
+ * Each generate() call:
20
+ * 1. appends input messages to agent.context.messages
21
+ * 2. creates a fresh GenerateContext
22
+ * 3. runs the loop: request → stream → tool dispatch → repeat
23
+ * 4. commits the final assistant Message to agent.context.messages
24
+ * 5. resolves .result with GenerateResult
25
+ */
26
+ /**
27
+ * Agent namespace — the factory for creating agent instances.
28
+ *
29
+ * Use `Agent.create({...})` to wire a Provider + extensions into a
30
+ * running Agent. Async because extensions can have async `init` hooks.
31
+ *
32
+ * Unlike the `*.define()` helpers (which are pure declarations), this
33
+ * allocates a running thing with a lifecycle — call `agent.dispose()`
34
+ * when done.
35
+ */
36
+ import { createAgentImpl } from "./create-agent";
37
+ // eslint-disable-next-line @typescript-eslint/no-redeclare
38
+ export const Agent = {
39
+ create: (opts) => createAgentImpl(opts),
40
+ };
41
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAoMH;;;;;;;;;GASG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,KAAK,GAAG;IACpB,MAAM,EAAE,CAAC,IAAwB,EAAkB,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;CAC3E,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Two contexts, two lifetimes.
3
+ *
4
+ * AgentContext — the agent-wide, persistent carrier.
5
+ * GenerateContext — the per-generate carrier.
6
+ */
7
+ import type { Message, Content } from "./message";
8
+ import type { Usage } from "./provider";
9
+ import type { Extension } from "./extension";
10
+ import type { Model } from "./model";
11
+ import type { AgentStreamPart } from "./agent";
12
+ import type { Messages } from "./messages";
13
+ export interface AgentContext {
14
+ /** Stable identifier for the agent. */
15
+ readonly name: string;
16
+ /** Registered extensions in registration order. */
17
+ readonly extensions: ReadonlyArray<Extension>;
18
+ /** Conversation history. Read-only view — use appendMessage() or messageStore to mutate. */
19
+ readonly messages: ReadonlyArray<Message>;
20
+ /**
21
+ * The guarded Messages instance. Use this for controlled mutations
22
+ * (splice, replaceRange, replace). Direct array mutation on `messages`
23
+ * will throw at runtime.
24
+ */
25
+ readonly messageStore: Messages;
26
+ /** Total tokens (input + output) accumulated across all generate turns in the session. Updated by the generate loop. */
27
+ tokenCount: number;
28
+ /** Agent-wide per-extension scratchpad. Keyed by extension name. */
29
+ readonly state: Map<string, unknown>;
30
+ /** Optional agent-level abort signal. */
31
+ readonly signal?: AbortSignal;
32
+ /** Optional metadata supplied at createAgent time. */
33
+ readonly metadata?: Readonly<Record<string, unknown>>;
34
+ /** Active provider extension name. Mutable — can be changed mid-session. */
35
+ providerName: string;
36
+ /** Active model ID within the provider. Mutable — can be changed mid-session. */
37
+ modelId: string;
38
+ /** The resolved active model. Call model.stream(req) for LLM access. */
39
+ readonly model: Model;
40
+ /** Append message(s) to conversation history. Assigns ID if missing. */
41
+ appendMessage(message: Message | ReadonlyArray<Message>): void;
42
+ /**
43
+ * Emit an event part to the caller's stream.
44
+ * TODO: Wire up in create-agent.ts — needs reference to the PushStream.
45
+ */
46
+ emit?(part: AgentStreamPart): void;
47
+ }
48
+ export interface GenerateContext {
49
+ /** Upward reference to the agent-wide context. */
50
+ readonly agent: AgentContext;
51
+ /** Unique identifier for this generate() call. All messages committed during this call share this ID. */
52
+ readonly generateId: string;
53
+ /**
54
+ * Assistant output being built up over the course of this generate.
55
+ */
56
+ readonly content: Content[];
57
+ /** Per-generate per-extension scratchpad. Keyed by extension name. */
58
+ readonly state: Map<string, unknown>;
59
+ /** 0-indexed step counter. */
60
+ readonly stepCount: number;
61
+ /** Upper bound on steps within this generate. */
62
+ readonly maxSteps: number;
63
+ /** Accumulated usage across all provider calls in this generate. */
64
+ usage: Usage;
65
+ /** Per-generate abort signal (merged with AgentContext.signal). */
66
+ readonly signal?: AbortSignal;
67
+ }
68
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAM3C,MAAM,WAAW,YAAY;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAC9C,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC;IAChC,wHAAwH;IACxH,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,yCAAyC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtD,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAEhB,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,wEAAwE;IACxE,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAE/D;;;OAGG;IACH,IAAI,CAAC,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;CACnC;AAMD,MAAM,WAAW,eAAe;IAC/B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,yGAAyG;IACzG,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,KAAK,EAAE,KAAK,CAAC;IACb,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Two contexts, two lifetimes.
3
+ *
4
+ * AgentContext — the agent-wide, persistent carrier.
5
+ * GenerateContext — the per-generate carrier.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,7 @@
1
+ /** createAgentImpl — wires extensions into an Agent instance. */
2
+ import type { Agent as AgentType, CreateAgentOptions } from "./agent";
3
+ import type { Extension } from "./extension";
4
+ import type { Model } from "./model";
5
+ export declare function resolveModel(extensions: ReadonlyArray<Extension>, providerName: string, modelId: string): Model;
6
+ export declare function createAgentImpl(opts: CreateAgentOptions): Promise<AgentType>;
7
+ //# sourceMappingURL=create-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-agent.d.ts","sourceRoot":"","sources":["../src/create-agent.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,OAAO,KAAK,EAAE,KAAK,IAAI,SAAS,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,KAAK,EAAE,SAAS,EAAS,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AASrC,wBAAgB,YAAY,CAC3B,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,EACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACb,KAAK,CAOP;AAED,wBAAsB,eAAe,CACpC,IAAI,EAAE,kBAAkB,GACtB,OAAO,CAAC,SAAS,CAAC,CA8MpB"}