@directive-run/knowledge 1.14.0 → 1.16.0

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 (43) hide show
  1. package/README.md +50 -15
  2. package/ai/ai-adapters.md +7 -0
  3. package/ai/ai-agents-streaming.md +187 -149
  4. package/ai/ai-budget-resilience.md +305 -132
  5. package/ai/ai-communication.md +220 -197
  6. package/ai/ai-debug-observability.md +259 -173
  7. package/ai/ai-guardrails-memory.md +191 -153
  8. package/ai/ai-mcp-rag.md +204 -199
  9. package/ai/ai-multi-agent.md +254 -153
  10. package/ai/ai-orchestrator.md +353 -114
  11. package/ai/ai-security.md +287 -180
  12. package/ai/ai-tasks.md +8 -1
  13. package/ai/ai-testing-evals.md +363 -256
  14. package/core/anti-patterns.md +18 -2
  15. package/core/constraints.md +13 -2
  16. package/core/core-patterns.md +12 -0
  17. package/core/error-boundaries.md +8 -0
  18. package/core/history.md +7 -0
  19. package/core/multi-module.md +15 -3
  20. package/core/naming.md +128 -90
  21. package/core/plugins.md +7 -0
  22. package/core/react-adapter.md +256 -174
  23. package/core/resolvers.md +10 -0
  24. package/core/schema-types.md +8 -0
  25. package/core/system-api.md +10 -0
  26. package/core/testing.md +257 -143
  27. package/examples/checkers.ts +15 -16
  28. package/examples/contact-form.ts +2 -2
  29. package/examples/counter-react.ts +1 -1
  30. package/examples/counter-svelte.ts +1 -1
  31. package/examples/counter-vue.ts +1 -1
  32. package/examples/counter.ts +1 -1
  33. package/examples/data-triggers.ts +4 -4
  34. package/examples/feature-flags.ts +2 -2
  35. package/examples/form-wizard.ts +2 -2
  36. package/examples/newsletter.ts +2 -2
  37. package/examples/server.ts +2 -2
  38. package/examples/shopping-cart.ts +1 -1
  39. package/examples/topic-guard.ts +1 -1
  40. package/package.json +3 -3
  41. package/sitemap.md +17 -11
  42. package/examples/debounce-constraints.ts +0 -95
  43. package/examples/multi-module.ts +0 -58
@@ -1,31 +1,38 @@
1
- # AI Orchestrator (Single-Agent)
1
+ # AI Orchestrator (single-agent)
2
2
 
3
- The `createAgentOrchestrator` configures a Directive-backed runtime for a single AI agent with constraints, resolvers, guardrails, memory, budgets, and hooks.
3
+ > Covers `@directive-run/ai` — `createAgentOrchestrator` for single-agent runs with constraints, resolvers, guardrails, memory, budgets, approval, breakpoints, retry, structured output, circuit breakers, checkpoints.
4
4
 
5
- ## Decision Tree: "How do I set up an orchestrator?"
5
+ `createAgentOrchestrator` builds a Directive-backed runtime for ONE AI agent with constraints, resolvers, guardrails, memory, budgets, approval, breakpoints, retry, structured output, circuit breakers, checkpoints, and observability hooks.
6
+
7
+ For multiple agents (pipelines, debates, DAGs), see `ai-multi-agent.md`.
8
+
9
+ ## Decision tree
6
10
 
7
11
  ```
8
- Need to run an AI agent?
9
- ├── Single agentcreateAgentOrchestrator (this file)
10
- ├── Multiple agentscreateMultiAgentOrchestrator (see ai-multi-agent.md)
11
-
12
- Setting up createAgentOrchestrator...
13
- ├── Need schema? → factsSchema with t.*() builders (NOT TS types)
14
- ├── Need guardrails? guardrails: { input: [...], output: [...] }
15
- ├── Need memory? → memory: createAgentMemory({ strategy, summarizer })
16
- ├── Need budget control? → maxTokenBudget + budgetWarningThreshold
17
- ├── Need streaming? → orchestrator.runStream(agent, prompt)
18
- └── Need approval workflow? → hooks.onBeforeRun returns { approved: boolean }
12
+ Setting up createAgentOrchestrator…
13
+ ├── Custom orchestrator state? factsSchema with t.*() builders (NOT TS types)
14
+ ├── Need input/output guardrails? guardrails: { input: [...], output: [...] }
15
+ ├── Need conversation memory? → memory: createAgentMemory({ strategy })
16
+ ├── Need token budget? → maxTokenBudget + onBudgetWarning (TOP-LEVEL — not in hooks)
17
+ ├── Need streaming? → orchestrator.runStream(agent, input) returns { stream, result, abort }
18
+ ├── Need approval before tool calls? onApprovalRequest + orchestrator.approve()/reject()
19
+ ├── Need human-in-the-loop pauses? → breakpoints: [...] + onBreakpoint callback
20
+ ├── Need observability? → hooks.onAgentStart / onAgentComplete / onAgentError / onAgentRetry / onGuardrailCheck
21
+ ├── Need automatic retry on failure? → agentRetry: { maxRetries, baseDelayMs }
22
+ ├── Need structured output? → outputSchema: zodSchema (with maxSchemaRetries)
23
+ ├── Need failure isolation? → circuitBreaker: createCircuitBreaker(...)
24
+ └── Need save/restore? → checkpointStore + orchestrator.checkpoint()/restore()
19
25
  ```
20
26
 
21
- ## Basic Setup
27
+ ## Basic setup
22
28
 
23
29
  ```typescript
24
- import { createAgentOrchestrator, t } from "@directive-run/ai";
30
+ import { createAgentOrchestrator } from "@directive-run/ai";
25
31
  import { createAnthropicRunner } from "@directive-run/ai/anthropic";
32
+ import { t } from "@directive-run/core";
26
33
 
27
34
  const runner = createAnthropicRunner({
28
- apiKey: process.env.ANTHROPIC_API_KEY,
35
+ apiKey: process.env.ANTHROPIC_API_KEY!,
29
36
  });
30
37
 
31
38
  const orchestrator = createAgentOrchestrator({
@@ -33,12 +40,10 @@ const orchestrator = createAgentOrchestrator({
33
40
  factsSchema: {
34
41
  confidence: t.number(),
35
42
  analysis: t.string(),
36
- cache: t.array<string>(),
37
43
  },
38
44
  init: (facts) => {
39
45
  facts.confidence = 0;
40
46
  facts.analysis = "";
41
- facts.cache = [];
42
47
  },
43
48
  constraints: {
44
49
  lowConfidence: {
@@ -48,180 +53,414 @@ const orchestrator = createAgentOrchestrator({
48
53
  },
49
54
  resolvers: {
50
55
  reAnalyze: {
51
- requirement: "RE_ANALYZE",
56
+ requirement: (req): req is { type: "RE_ANALYZE" } => req.type === "RE_ANALYZE",
52
57
  resolve: async (req, context) => {
53
- context.facts.confidence = 0;
58
+ context.facts.confidence = 1;
54
59
  },
55
60
  },
56
61
  },
57
- guardrails: {
58
- input: [createPIIGuardrail({ redact: true })],
59
- output: [createLengthGuardrail({ maxChars: 5000 })],
60
- },
61
- maxTokenBudget: 100000,
62
- budgetWarningThreshold: 0.8,
63
- memory: createAgentMemory({
64
- strategy: createSlidingWindowStrategy({ maxMessages: 50 }),
65
- summarizer: createKeyPointsSummarizer(),
66
- }),
67
- debug: true,
68
- hooks: {
69
- onStart: () => console.log("Orchestrator started"),
70
- onBeforeRun: (agent, prompt) => ({ approved: true }),
71
- onAfterRun: (agent, result) => console.log("Done", result.totalTokens),
72
- onError: (error) => console.error(error),
73
- onBudgetWarning: (usage) => console.warn("Budget:", usage),
74
- },
75
62
  });
63
+
64
+ const result = await orchestrator.run(
65
+ { name: "analyst", instructions: "You are a data analyst.", model: "claude-sonnet-4-5" },
66
+ "Analyze this dataset",
67
+ );
68
+
69
+ console.log(result.output, result.tokenUsage);
76
70
  ```
77
71
 
78
- ## Running the Agent
72
+ ## Running an agent
79
73
 
80
- ```typescript
81
- const agent = {
82
- name: "analyst",
83
- instructions: "You are a data analyst.",
84
- model: "claude-sonnet-4-5",
85
- };
74
+ `run(agent, input, options?)` resolves to `RunResult<T>`.
75
+ `runStream(agent, input, options?)` returns `{ stream, result, abort }` — `stream` is the async iterator, `result` is a promise for the final result, `abort` cancels the in-flight run.
86
76
 
77
+ ```typescript
87
78
  // Standard run
88
79
  const result = await orchestrator.run(agent, "Analyze this dataset");
89
- console.log(result.output);
80
+ console.log(result.output, result.tokenUsage);
81
+
82
+ // Streaming run — destructure the three handles
83
+ const { stream, result, abort } = orchestrator.runStream(agent, "Summarize findings");
90
84
 
91
- // Streaming run
92
- const stream = orchestrator.runStream(agent, "Summarize findings");
93
85
  for await (const chunk of stream) {
94
86
  if (chunk.type === "token") {
95
87
  process.stdout.write(chunk.data);
96
88
  }
89
+ if (chunk.type === "approval_required") {
90
+ // Show your approval UI; resolve with orchestrator.approve(chunk.requestId)
91
+ }
92
+ if (chunk.type === "guardrail_triggered") {
93
+ console.warn("guardrail:", chunk.guardrail);
94
+ }
97
95
  }
98
96
 
99
- // Wait for all constraints/resolvers to settle
100
- await orchestrator.system.settle();
97
+ const final = await result;
98
+
99
+ // Wait for any in-flight resolvers to drain
100
+ await orchestrator.waitForIdle();
101
+ ```
102
+
103
+ ## Reading orchestrator state
104
+
105
+ State lives on `orchestrator.facts.agent` — the `AgentState` is nested under `agent` because the orchestrator's facts shape is `OrchestratorState & F` (your custom facts merged with the orchestrator's built-ins).
106
+
107
+ ```typescript
108
+ // Built-in agent state
109
+ orchestrator.facts.agent.status; // "idle" | "running" | "paused" | "completed" | "error"
110
+ orchestrator.facts.agent.currentAgent; // string | null — the agent name when running
111
+ orchestrator.facts.agent.input; // string | null — current prompt
112
+ orchestrator.facts.agent.output; // unknown | null — last output
113
+ orchestrator.facts.agent.error; // string | null — last error message
114
+ orchestrator.facts.agent.tokenUsage; // number — cumulative tokens used
115
+ orchestrator.facts.agent.turnCount; // number — completed turns
116
+ orchestrator.facts.agent.startedAt; // number | null — ms timestamp
117
+ orchestrator.facts.agent.completedAt; // number | null
118
+
119
+ // Cumulative tokens (also exposed at the top of the orchestrator)
120
+ orchestrator.totalTokens;
121
+
122
+ // Approval queue (when autoApproveToolCalls: false)
123
+ orchestrator.facts.approval.pending; // ApprovalRequest[]
124
+ orchestrator.facts.approval.approved; // string[]
125
+ orchestrator.facts.approval.rejected; // RejectedRequest[]
126
+
127
+ // Conversation + tool call history
128
+ orchestrator.facts.conversation; // Message[]
129
+ orchestrator.facts.toolCalls; // ToolCall[]
130
+ ```
131
+
132
+ ## Token budget
133
+
134
+ `maxTokenBudget` is the cap. When usage crosses it the orchestrator pauses (sets `facts.agent.status = "paused"`). `budgetWarningThreshold` (0–1, default 0.8) fires `onBudgetWarning` BEFORE the cap. **Both options are top-level — NOT inside `hooks:`**.
135
+
136
+ ```typescript
137
+ const orchestrator = createAgentOrchestrator({
138
+ runner,
139
+ maxTokenBudget: 100_000,
140
+ budgetWarningThreshold: 0.8,
141
+ onBudgetWarning: ({ currentTokens, maxBudget, percentage }) => {
142
+ console.warn(`Token usage at ${Math.round(percentage * 100)}% (${currentTokens}/${maxBudget})`);
143
+ },
144
+ });
145
+ ```
146
+
147
+ ## Memory
148
+
149
+ ```typescript
150
+ import { createAgentMemory, createSlidingWindowStrategy, createKeyPointsSummarizer } from "@directive-run/ai";
151
+
152
+ const orchestrator = createAgentOrchestrator({
153
+ runner,
154
+ memory: createAgentMemory({
155
+ strategy: createSlidingWindowStrategy({ maxMessages: 50 }),
156
+ summarizer: createKeyPointsSummarizer(),
157
+ }),
158
+ });
101
159
  ```
102
160
 
103
- ## OrchestratorState Fields
161
+ When `memory` is configured, prior conversation context is auto-injected into each agent run, and the agent's response is auto-stored. See `ai-guardrails-memory.md` for memory strategies in depth.
162
+
163
+ ## Guardrails
104
164
 
105
165
  ```typescript
106
- // Access via orchestrator.system.facts
107
- orchestrator.system.facts.status; // "idle" | "running" | "paused" | "error"
108
- orchestrator.system.facts.tokenUsage; // { inputTokens, outputTokens, total }
109
- orchestrator.system.facts.runCount; // number of completed runs
110
- orchestrator.system.facts.lastError; // Error | null
166
+ import {
167
+ createPIIGuardrail,
168
+ createLengthGuardrail,
169
+ createPromptInjectionGuardrail,
170
+ } from "@directive-run/ai/guardrails";
171
+
172
+ const orchestrator = createAgentOrchestrator({
173
+ runner,
174
+ guardrails: {
175
+ input: [
176
+ createPIIGuardrail({ redact: true }),
177
+ createPromptInjectionGuardrail({ strictMode: true }),
178
+ ],
179
+ output: [
180
+ createLengthGuardrail({ maxCharacters: 5000 }),
181
+ ],
182
+ },
183
+ });
111
184
  ```
112
185
 
113
- ## Approval Workflow
186
+ See `ai-guardrails-memory.md` for the full list of guardrail factories and their actual option shapes.
187
+
188
+ ## Lifecycle hooks (observability)
189
+
190
+ These hooks fire purely for observability — they cannot block, deny, or modify runs. To block a run before it executes, use `breakpoints` (next section) or guardrails.
114
191
 
115
192
  ```typescript
116
193
  const orchestrator = createAgentOrchestrator({
117
194
  runner,
118
195
  hooks: {
119
- onBeforeRun: async (agent, prompt) => {
120
- const decision = await reviewPrompt(prompt);
196
+ onAgentStart: (e) => console.log(`▶ ${e.agentName} @ ${e.timestamp}`),
197
+ onAgentComplete: (e) => console.log(`✓ ${e.agentName} — ${e.tokenUsage} tokens in ${e.durationMs}ms`),
198
+ onAgentError: (e) => console.error(`✗ ${e.agentName}:`, e.error),
199
+ onAgentRetry: (e) => console.warn(`↻ ${e.agentName} attempt ${e.attempt}`),
200
+ onGuardrailCheck: (e) => console.log(`${e.passed ? "✓" : "✗"} ${e.guardrailName}`),
201
+ onBreakpoint: (req) => console.log(`⏸ breakpoint ${req.id}`),
202
+ },
203
+ });
204
+ ```
121
205
 
122
- return { approved: decision.ok, reason: decision.reason };
123
- },
206
+ The full event payload shapes live in `@directive-run/ai`'s `OrchestratorLifecycleHooks` interface.
207
+
208
+ ## Human-in-the-loop: breakpoints + approval
209
+
210
+ For approval workflows (pause before a sensitive tool call), Directive offers two layered mechanisms:
211
+
212
+ ### Tool-call approval (`autoApproveToolCalls: false`)
213
+
214
+ ```typescript
215
+ const orchestrator = createAgentOrchestrator({
216
+ runner,
217
+ autoApproveToolCalls: false,
218
+ onApprovalRequest: (request) => {
219
+ showApprovalDialog(request); // your UI
124
220
  },
221
+ approvalTimeoutMs: 5 * 60 * 1000,
125
222
  });
126
223
 
127
- // If not approved, run() throws ApprovalDeniedError
224
+ // In your UI handler:
225
+ orchestrator.approve(request.id);
226
+ // or
227
+ orchestrator.reject(request.id, "policy violation");
128
228
  ```
129
229
 
130
- ## Pause / Resume
230
+ ### Breakpoints (arbitrary pause points)
131
231
 
132
232
  ```typescript
133
- orchestrator.pause();
134
- // Agent work suspends; in-flight requests complete but new ones queue
233
+ import type { BreakpointConfig } from "@directive-run/ai";
234
+
235
+ const orchestrator = createAgentOrchestrator({
236
+ runner,
237
+ breakpoints: [
238
+ { id: "review-prompt", before: "agent_start" },
239
+ { id: "review-output", after: "agent_complete" },
240
+ ],
241
+ onBreakpoint: (request) => {
242
+ showReviewUI(request);
243
+ },
244
+ breakpointTimeoutMs: 5 * 60 * 1000,
245
+ });
246
+
247
+ // Resume (optionally with input modifications)
248
+ orchestrator.resumeBreakpoint("review-prompt");
135
249
 
136
- orchestrator.resume();
137
- // Queued work begins executing
250
+ // Or cancel
251
+ orchestrator.cancelBreakpoint("review-prompt", "user aborted");
138
252
  ```
139
253
 
140
- ## Checkpoints
254
+ ## Retry + circuit breaker + self-healing
141
255
 
142
256
  ```typescript
143
- // Save state
144
- const checkpoint = orchestrator.checkpoint();
145
- const serialized = JSON.stringify(checkpoint);
257
+ import { createCircuitBreaker } from "@directive-run/ai";
146
258
 
147
- // Restore state
148
- const restored = createAgentOrchestrator({
259
+ const orchestrator = createAgentOrchestrator({
149
260
  runner,
150
- checkpoint: JSON.parse(serialized),
261
+ agentRetry: {
262
+ maxRetries: 3,
263
+ baseDelayMs: 500,
264
+ maxDelayMs: 5000,
265
+ isRetryable: (err) => err.message.includes("rate_limit"),
266
+ onRetry: ({ attempt, error, delayMs }) => console.warn(`retry ${attempt} in ${delayMs}ms:`, error.message),
267
+ },
268
+ circuitBreaker: createCircuitBreaker({
269
+ failureThreshold: 5,
270
+ recoveryTimeMs: 60_000,
271
+ halfOpenMaxRequests: 1,
272
+ }),
151
273
  });
152
274
  ```
153
275
 
154
- ## Anti-Patterns
276
+ See `ai-budget-resilience.md` for the resilience surface in depth (retry, fallback, circuit breaker, health monitor, self-healing).
155
277
 
156
- ### #21: TypeScript types instead of t.*() for factsSchema
278
+ ## Structured output
157
279
 
158
280
  ```typescript
159
- // WRONG TS types are erased at runtime, no schema validation
281
+ import { z } from "zod";
282
+
160
283
  const orchestrator = createAgentOrchestrator({
161
284
  runner,
162
- factsSchema: {} as { confidence: number; analysis: string },
285
+ outputSchema: z.object({
286
+ summary: z.string(),
287
+ confidence: z.number().min(0).max(1),
288
+ }),
289
+ maxSchemaRetries: 2,
163
290
  });
164
291
 
165
- // CORRECT use t.*() builders for runtime schema
292
+ const result = await orchestrator.run(agent, "Summarize this article");
293
+ // result.output is typed and parsed by the Zod schema; up to 2 retries on validation failure
294
+ ```
295
+
296
+ `outputSchema` accepts anything with a `safeParse(value)` method — Zod, Valibot, or your own validator.
297
+
298
+ ## Pause / resume
299
+
300
+ ```typescript
301
+ orchestrator.pause(); // in-flight runs complete; new ones queue
302
+ orchestrator.resume(); // queued work begins executing
303
+ orchestrator.reset(); // clears conversation, approval, and tool-call state
304
+ ```
305
+
306
+ ## Checkpoints (save / restore)
307
+
308
+ `checkpoint()` is **async** — it returns a Promise. Restore on an EXISTING orchestrator instance with `restore()`; there is no constructor option that takes a checkpoint.
309
+
310
+ ```typescript
311
+ import { createInMemoryCheckpointStore } from "@directive-run/ai";
312
+
166
313
  const orchestrator = createAgentOrchestrator({
167
314
  runner,
168
- factsSchema: {
169
- confidence: t.number(),
170
- analysis: t.string(),
171
- },
315
+ checkpointStore: createInMemoryCheckpointStore(),
172
316
  });
317
+
318
+ // Save
319
+ const cp = await orchestrator.checkpoint({ label: "after-analysis" });
320
+ const serialized = JSON.stringify(cp);
321
+
322
+ // Restore — on the same instance, or a fresh one with the same config shape
323
+ const newOrchestrator = createAgentOrchestrator({ runner, /* same options */ });
324
+ newOrchestrator.restore(JSON.parse(serialized));
173
325
  ```
174
326
 
175
- ### #22: Mutating arrays/objects in place
327
+ ## Anti-patterns
328
+
329
+ ### TS types instead of `t.*()` for `factsSchema`
176
330
 
177
331
  ```typescript
178
- // WRONG proxy cannot detect in-place mutations
332
+ // WRONG TS types are erased at runtime; no schema validation, no defaults
333
+ factsSchema: {} as { confidence: number; analysis: string }
334
+
335
+ // CORRECT — t.*() builders provide runtime shape + types
336
+ factsSchema: { confidence: t.number(), analysis: t.string() }
337
+ ```
338
+
339
+ ### In-place array/object mutation
340
+
341
+ ```typescript
342
+ // WRONG — the proxy can't detect in-place mutations
179
343
  context.facts.cache.push("new-item");
180
344
 
181
- // CORRECT replace the entire value
345
+ // CORRECT replace the value
182
346
  context.facts.cache = [...context.facts.cache, "new-item"];
183
347
  ```
184
348
 
185
- ### #23: Returning data from resolve
349
+ ### Returning data from `resolve`
186
350
 
187
351
  ```typescript
188
- // WRONG resolvers return void, not data
352
+ // WRONG resolvers return void; return value is ignored
189
353
  resolve: async (req, context) => {
190
- const result = await analyzeData(req.input);
191
-
192
- return result; // Return value is ignored
193
- },
354
+ return await analyzeData(req.input);
355
+ }
194
356
 
195
- // CORRECT mutate context.facts to store results
357
+ // CORRECT mutate facts to store results
196
358
  resolve: async (req, context) => {
197
- const result = await analyzeData(req.input);
198
- context.facts.analysis = result;
199
- },
359
+ context.facts.analysis = await analyzeData(req.input);
360
+ }
361
+ ```
362
+
363
+ ### Confusing single-agent with multi-agent lifecycle
364
+
365
+ ```typescript
366
+ // Single-agent orchestrator — NO start() needed
367
+ const orch = createAgentOrchestrator({ runner });
368
+ await orch.run(agent, "prompt"); // ✓ ready
369
+
370
+ // Multi-agent orchestrator — start() IS required before runPattern
371
+ const multi = createMultiAgentOrchestrator({ agents, runner });
372
+ multi.start();
373
+ await multi.runPattern("pipeline", "prompt");
200
374
  ```
201
375
 
202
- ### #24: Forgetting start() for multi-agent
376
+ ### Hook names from a guess instead of the type
203
377
 
204
378
  ```typescript
205
- // WRONG multi-agent orchestrators require explicit start()
206
- const orchestrator = createMultiAgentOrchestrator({ agents, runner });
207
- const result = await orchestrator.runPattern("pipeline", "prompt");
379
+ // WRONG onStart / onBeforeRun / onAfterRun / onError do not exist on OrchestratorLifecycleHooks
380
+ hooks: {
381
+ onStart: () => {},
382
+ onBeforeRun: () => ({ approved: true }),
383
+ onAfterRun: () => {},
384
+ onError: () => {},
385
+ }
208
386
 
209
- // CORRECT call start() before running patterns
210
- const orchestrator = createMultiAgentOrchestrator({ agents, runner });
211
- orchestrator.start();
212
- const result = await orchestrator.runPattern("pipeline", "prompt");
387
+ // CORRECT actual names are onAgent*
388
+ hooks: {
389
+ onAgentStart: (e) => {},
390
+ onAgentComplete: (e) => {},
391
+ onAgentError: (e) => {},
392
+ onAgentRetry: (e) => {},
393
+ onGuardrailCheck: (e) => {},
394
+ }
213
395
  ```
214
396
 
215
- Note: Single-agent `createAgentOrchestrator` does NOT require `start()`. Only `createMultiAgentOrchestrator` does.
397
+ ### Putting `onBudgetWarning` inside `hooks`
398
+
399
+ ```typescript
400
+ // WRONG — onBudgetWarning is a TOP-LEVEL option
401
+ { hooks: { onBudgetWarning: () => {} } }
402
+
403
+ // CORRECT
404
+ { onBudgetWarning: (event) => {}, maxTokenBudget: 100_000 }
405
+ ```
406
+
407
+ ### Treating `checkpoint()` as sync
408
+
409
+ ```typescript
410
+ // WRONG — checkpoint() returns a Promise
411
+ const cp = orchestrator.checkpoint();
412
+ const json = JSON.stringify(cp); // serializes "[object Promise]"
413
+
414
+ // CORRECT — await it
415
+ const cp = await orchestrator.checkpoint({ label: "snapshot-1" });
416
+ const json = JSON.stringify(cp);
417
+ ```
418
+
419
+ ### Restoring via constructor option
420
+
421
+ ```typescript
422
+ // WRONG — there is no `checkpoint` option on createAgentOrchestrator
423
+ const orch = createAgentOrchestrator({ runner, checkpoint: savedCheckpoint });
424
+
425
+ // CORRECT — restore on an existing instance
426
+ const orch = createAgentOrchestrator({ runner });
427
+ orch.restore(savedCheckpoint);
428
+ ```
429
+
430
+ ### Iterating `runStream`'s return value directly
431
+
432
+ ```typescript
433
+ // WRONG — runStream returns { stream, result, abort }, not an AsyncIterable
434
+ const stream = orchestrator.runStream(agent, "prompt");
435
+ for await (const chunk of stream) { /* never iterates the right thing */ }
436
+
437
+ // CORRECT — destructure
438
+ const { stream, result, abort } = orchestrator.runStream(agent, "prompt");
439
+ for await (const chunk of stream) { /* … */ }
440
+ const final = await result;
441
+ ```
216
442
 
217
- ## Quick Reference
443
+ ## Quick reference
218
444
 
219
445
  | Method | Purpose |
220
446
  |---|---|
221
- | `orchestrator.run(agent, prompt)` | Run agent, return RunResult |
222
- | `orchestrator.runStream(agent, prompt)` | Run agent, return AsyncIterable<StreamChunk> |
223
- | `orchestrator.pause()` | Suspend new work |
224
- | `orchestrator.resume()` | Resume suspended work |
225
- | `orchestrator.checkpoint()` | Serialize current state |
226
- | `orchestrator.system.settle()` | Wait for all resolvers |
227
- | `orchestrator.system.facts` | Read orchestrator state |
447
+ | `orch.run(agent, input, opts?)` | Run agent `Promise<RunResult<T>>` |
448
+ | `orch.runStream(agent, input, opts?)` | Run agent → `{ stream, result, abort }` |
449
+ | `orch.approve(id)` / `orch.reject(id, reason?)` | Resolve a pending approval request |
450
+ | `orch.pause()` / `orch.resume()` / `orch.reset()` | Lifecycle control |
451
+ | `orch.waitForIdle(timeoutMs?)` | Resolves when orchestrator is back at idle |
452
+ | `orch.checkpoint(opts?)` | Async `Promise<Checkpoint>` |
453
+ | `orch.restore(cp, opts?)` | Restore state on an existing instance |
454
+ | `orch.resumeBreakpoint(id, mods?)` / `orch.cancelBreakpoint(id, reason?)` | Resume/cancel a paused breakpoint |
455
+ | `orch.facts.agent.*` | Read built-in agent state |
456
+ | `orch.totalTokens` | Cumulative token count |
457
+ | `orch.timeline` | DebugTimeline (when `debug: true`); `null` otherwise |
458
+
459
+ ## See also
460
+
461
+ - [`ai-multi-agent.md`](./ai-multi-agent.md) — the multi-agent flavor; 8 composition patterns and pattern factories
462
+ - [`ai-adapters.md`](./ai-adapters.md) — provider runners that produce the `runner` this orchestrator wraps
463
+ - [`ai-guardrails-memory.md`](./ai-guardrails-memory.md) — full surface of the `guardrails:` and `memory:` options on this orchestrator
464
+ - [`ai-budget-resilience.md`](./ai-budget-resilience.md) — full surface of the `circuitBreaker:`, `selfHealing:`, `agentRetry:`, `maxTokenBudget:` options
465
+ - [`ai-debug-observability.md`](./ai-debug-observability.md) — full surface of the `debug:`, `breakpoints:`, `onBreakpoint:` options + `orchestrator.timeline`
466
+ - [`ai-testing-evals.md`](./ai-testing-evals.md) — `createTestOrchestrator` for unit testing this surface