@directive-run/knowledge 1.14.0 → 1.15.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 +2 -0
  3. package/ai/ai-agents-streaming.md +182 -149
  4. package/ai/ai-budget-resilience.md +299 -132
  5. package/ai/ai-communication.md +215 -197
  6. package/ai/ai-debug-observability.md +253 -173
  7. package/ai/ai-guardrails-memory.md +185 -153
  8. package/ai/ai-mcp-rag.md +199 -199
  9. package/ai/ai-multi-agent.md +247 -153
  10. package/ai/ai-orchestrator.md +344 -114
  11. package/ai/ai-security.md +282 -180
  12. package/ai/ai-tasks.md +3 -1
  13. package/ai/ai-testing-evals.md +357 -256
  14. package/core/anti-patterns.md +9 -2
  15. package/core/constraints.md +6 -2
  16. package/core/core-patterns.md +2 -0
  17. package/core/error-boundaries.md +2 -0
  18. package/core/history.md +2 -0
  19. package/core/multi-module.md +8 -3
  20. package/core/naming.md +15 -6
  21. package/core/plugins.md +2 -0
  22. package/core/react-adapter.md +250 -174
  23. package/core/resolvers.md +2 -0
  24. package/core/schema-types.md +2 -0
  25. package/core/system-api.md +2 -0
  26. package/core/testing.md +251 -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,22 +1,28 @@
1
- # AI Multi-Agent Orchestrator
1
+ # AI multi-agent orchestrator
2
2
 
3
- `createMultiAgentOrchestrator` coordinates multiple agents using 8 composition patterns. Each agent becomes a namespaced Directive module with a shared coordinator.
3
+ > Covers `@directive-run/ai/multi-agent` — `createMultiAgentOrchestrator` + 8 composition patterns (parallel / sequential / supervisor / dag / reflect / race / debate / goal).
4
4
 
5
- ## Decision Tree: "Which pattern do I need?"
5
+ `createMultiAgentOrchestrator` coordinates multiple agents using 8 composition patterns. Each agent becomes a namespaced Directive module with a shared coordinator. Patterns are pure config objects you assemble with factory functions and pass via the `patterns:` option.
6
+
7
+ For a single agent, see `ai-orchestrator.md`.
8
+
9
+ ## Decision tree
6
10
 
7
11
  ```
8
12
  How should agents interact?
9
13
  ├── Independent, combine results → parallel()
10
- ├── One feeds the next → sequential()
11
- ├── One agent delegates → supervisor()
14
+ ├── One feeds the next → sequential()
15
+ ├── One agent delegates work → supervisor()
12
16
  ├── Complex dependency graph → dag()
13
- ├── Agent critiques own output → reflect()
14
- ├── First to finish wins → race()
15
- ├── Agents argue to consensus → debate()
16
- └── Iterate until goal met → goal()
17
+ ├── Producer + evaluator loop → reflect()
18
+ ├── First to finish wins → race()
19
+ ├── Multiple positions debated → debate()
20
+ └── Iterate until goal met → goal()
17
21
  ```
18
22
 
19
- ## Basic Setup
23
+ ## Basic setup
24
+
25
+ Import from the subpath barrel — the main `@directive-run/ai` barrel re-exports these with `@deprecated` notices for v2 removal.
20
26
 
21
27
  ```typescript
22
28
  import {
@@ -29,246 +35,334 @@ import {
29
35
  race,
30
36
  debate,
31
37
  goal,
32
- } from "@directive-run/ai";
38
+ } from "@directive-run/ai/multi-agent";
33
39
  import { createAnthropicRunner } from "@directive-run/ai/anthropic";
34
40
 
35
41
  const runner = createAnthropicRunner({
36
- apiKey: process.env.ANTHROPIC_API_KEY,
42
+ apiKey: process.env.ANTHROPIC_API_KEY!,
37
43
  });
38
44
 
39
45
  const orchestrator = createMultiAgentOrchestrator({
40
46
  agents: {
41
- researcher: {
42
- name: "researcher",
43
- instructions: "Research the topic thoroughly.",
44
- model: "claude-sonnet-4-5",
45
- },
46
- writer: {
47
- name: "writer",
48
- instructions: "Write clear, engaging content.",
49
- model: "claude-sonnet-4-5",
50
- },
51
- editor: {
52
- name: "editor",
53
- instructions: "Edit for clarity and correctness.",
54
- model: "claude-haiku-3-5",
55
- },
47
+ researcher: { name: "researcher", instructions: "Research the topic thoroughly.", model: "claude-sonnet-4-5" },
48
+ writer: { name: "writer", instructions: "Write clear, engaging content.", model: "claude-sonnet-4-5" },
49
+ editor: { name: "editor", instructions: "Edit for clarity and correctness.", model: "claude-haiku-4-5" },
56
50
  },
57
51
  patterns: {
58
- pipeline: sequential(["researcher", "writer", "editor"]),
59
- brainstorm: parallel(["researcher", "writer"], mergeResults),
60
- managed: supervisor("editor", ["researcher", "writer"]),
61
- workflow: dag([
62
- { id: "research", handler: "researcher" },
63
- { id: "write", handler: "writer", dependencies: ["research"] },
64
- { id: "edit", handler: "editor", dependencies: ["write"] },
65
- ]),
52
+ pipeline: sequential(["researcher", "writer", "editor"]),
53
+ brainstorm: parallel(["researcher", "writer"], (results) => results.map((r) => String(r.output)).join("\n\n")),
54
+ managed: supervisor("editor", ["researcher", "writer"]),
55
+ workflow: dag({
56
+ research: { handler: "researcher" },
57
+ write: { handler: "writer", deps: ["research"] },
58
+ edit: { handler: "editor", deps: ["write"] },
59
+ }),
66
60
  },
67
61
  runner,
68
62
  });
69
63
 
70
- // REQUIRED for multi-agent – must call start() before running patterns
64
+ // REQUIRED call start() before runPattern. Single-agent createAgentOrchestrator does NOT need this.
71
65
  orchestrator.start();
72
66
 
73
67
  const result = await orchestrator.runPattern("pipeline", "Write about AI");
74
68
  ```
75
69
 
76
- ## Pattern Details
70
+ ## Patterns in depth
71
+
72
+ ### `parallel(handlers, merge, options?)`
77
73
 
78
- ### parallel Run agents concurrently, merge results
74
+ Runs handlers concurrently and combines their results. **`merge` is REQUIRED as positional argument 2** — without it the call fails at registration. `options?` accepts `minSuccess` and `timeout`.
79
75
 
80
76
  ```typescript
81
77
  const brainstorm = parallel(
82
78
  ["researcher", "writer"],
83
- (results) => {
84
- // results: Map<string, RunResult>
85
- const combined = Array.from(results.values())
86
- .map((r) => r.output)
87
- .join("\n\n");
88
-
89
- return combined;
90
- },
79
+ (results) => results.map((r) => String(r.output)).join("\n\n"),
80
+ { minSuccess: 1, timeout: 30_000 },
91
81
  );
92
82
  ```
93
83
 
94
- ### sequential Chain agents in order
84
+ The `results` arg is `RunResult<unknown>[]` — array, in handler order.
85
+
86
+ ### `sequential(handlers, options?)`
87
+
88
+ Each handler's output feeds as input to the next. `options.transform(output, handlerId, index)` adapts the hand-off; `options.extract(finalOutput)` shapes the return.
95
89
 
96
90
  ```typescript
97
- // Each agent receives the previous agent's output as its prompt
98
91
  const pipeline = sequential(["researcher", "writer", "editor"]);
92
+
93
+ const writeReview = sequential(["writer", "reviewer"], {
94
+ transform: (output) => `Review this draft:\n${output}`,
95
+ });
99
96
  ```
100
97
 
101
- ### supervisor One agent delegates to workers
98
+ ### `supervisor(supervisorAgent, workers, options?)`
99
+
100
+ The supervisor runs first and delegates to workers based on its output. Repeats up to `maxRounds` until the supervisor signals completion.
102
101
 
103
102
  ```typescript
104
- // Editor decides which worker to invoke and when to stop
105
- const managed = supervisor("editor", ["researcher", "writer"]);
103
+ const managed = supervisor("manager", ["worker1", "worker2"], { maxRounds: 3 });
106
104
  ```
107
105
 
108
- ### dag Directed acyclic graph of dependencies
106
+ ### `dag(nodes, merge?, options?)`
107
+
108
+ **Nodes are a `Record<string, DagNode>`, NOT an array.** Each node's edges are listed in `deps` (not `dependencies`). The runtime validates the graph is acyclic and that all `deps` refer to declared nodes.
109
109
 
110
110
  ```typescript
111
- // DagNode shape
112
- interface DagNode {
113
- id: string;
114
- handler: string; // agent name
115
- dependencies?: string[]; // node IDs this depends on
116
- transform?: (input: string, depResults: Map<string, string>) => string;
117
- }
118
-
119
- const workflow = dag([
120
- { id: "research", handler: "researcher" },
121
- {
122
- id: "write",
123
- handler: "writer",
124
- dependencies: ["research"],
125
- transform: (input, deps) => {
126
- const research = deps.get("research");
111
+ import type { DagNode, DagExecutionContext } from "@directive-run/ai/multi-agent";
127
112
 
128
- return `Based on research:\n${research}\n\nWrite about: ${input}`;
129
- },
113
+ const workflow = dag(
114
+ {
115
+ research: { handler: "researcher" },
116
+ analyze: { handler: "analyzer", deps: ["research"] },
117
+ summarize: { handler: "summarizer", deps: ["analyze"] },
118
+ },
119
+ // optional merge — receives the execution context with .outputs Record
120
+ (context: DagExecutionContext) => context.outputs.summarize,
121
+ // optional config
122
+ {
123
+ timeout: 60_000,
124
+ maxConcurrent: 4,
125
+ onNodeError: "skip-downstream", // "fail" (default) | "skip-downstream" | "continue"
130
126
  },
131
- { id: "edit", handler: "editor", dependencies: ["write"] },
132
- ]);
127
+ );
133
128
  ```
134
129
 
135
- ### reflect Agent critiques and revises its own output
130
+ ### `reflect(handler, evaluator, options?)`
131
+
132
+ Producer + evaluator loop. The producer generates output, the evaluator scores it; if the score is below `threshold` the producer retries with feedback, up to `maxIterations`. **`evaluator` is REQUIRED as positional argument 2.**
136
133
 
137
134
  ```typescript
138
- const selfImprove = reflect("writer", {
135
+ const selfImprove = reflect("writer", "reviewer", {
139
136
  maxIterations: 3,
140
- stopWhen: (output, iteration) => {
141
- return output.includes("FINAL") || iteration >= 3;
142
- },
137
+ threshold: 0.8,
138
+ onExhausted: "accept-best", // "accept-last" | "accept-best" | "throw"
139
+ onIteration: (record) => console.log(`iter ${record.iteration}: ${record.score}`),
143
140
  });
144
141
  ```
145
142
 
146
- ### race – First agent to finish wins
143
+ ### `race(handlers, options?)`
144
+
145
+ All handlers start simultaneously; the first to complete successfully wins. Use `minSuccess` to wait for N before picking.
147
146
 
148
147
  ```typescript
149
- const fastest = race(["researcher", "writer"], {
150
- minSuccess: 1, // How many must complete (default: 1)
151
- timeout: 30000, // Cancel remaining after timeout
148
+ const fastest = race(["fast-model", "smart-model"], {
149
+ timeout: 5000,
150
+ minSuccess: 1, // must be handlers.length
152
151
  });
153
152
  ```
154
153
 
155
- ### debate – Agents argue to consensus
154
+ ### `debate(config)`
155
+
156
+ **Takes a single `DebateConfig` object.** The discriminator agents argue from different positions; the `evaluator` agent judges. There is no `judge` field — it's `evaluator`.
156
157
 
157
158
  ```typescript
158
- const consensus = debate(["researcher", "writer"], {
159
- maxRounds: 5,
160
- judge: "editor", // Agent that decides when consensus is reached
159
+ const consensus = debate({
160
+ handlers: ["optimist", "pessimist"],
161
+ evaluator: "judge",
162
+ maxRounds: 2,
161
163
  });
162
164
  ```
163
165
 
164
- ### goal Iterate until a condition is met
166
+ ### `goal(nodes, when, options?)`
167
+
168
+ Goal-driven execution. Each node declares what it `produces` and `requires`; the runtime infers the execution graph and runs agents until `when(facts)` returns true. There is no single-agent string form.
165
169
 
166
170
  ```typescript
167
- const iterative = goal("researcher", {
168
- maxIterations: 10,
169
- goalCheck: (output, facts) => {
170
- return facts.confidence > 0.9;
171
+ import type { GoalNode } from "@directive-run/ai/multi-agent";
172
+
173
+ const pipeline = goal(
174
+ {
175
+ researcher: {
176
+ handler: "researcher",
177
+ produces: ["research.findings"],
178
+ requires: ["research.topic"],
179
+ extractOutput: (r) => ({ "research.findings": r.output }),
180
+ },
181
+ writer: {
182
+ handler: "writer",
183
+ produces: ["article.draft"],
184
+ requires: ["research.findings"],
185
+ extractOutput: (r) => ({ "article.draft": r.output }),
186
+ },
171
187
  },
172
- });
188
+ (facts) => facts["article.draft"] != null,
189
+ {
190
+ maxSteps: 10,
191
+ extract: (facts) => facts["article.draft"],
192
+ satisfaction: (facts) => (facts["article.draft"] ? 1.0 : 0.0),
193
+ },
194
+ );
173
195
  ```
174
196
 
175
- ## Fact Propagation
197
+ ## Fact propagation
176
198
 
177
- Each agent has its own namespaced facts. The coordinator module (`__coord`) holds shared state:
199
+ Each agent has its own namespaced facts module. A coordinator module (`__coord`) tracks shared orchestration state.
178
200
 
179
201
  ```typescript
180
- // Read agent-specific facts
202
+ // Per-agent state — module is named after the agent's key in `agents:`
181
203
  orchestrator.system.facts.researcher.status;
182
- orchestrator.system.facts.writer.lastOutput;
204
+ orchestrator.system.facts.writer.output;
183
205
 
184
- // Read coordinator facts
206
+ // Coordinator state
185
207
  orchestrator.system.facts.__coord.activePattern;
186
208
  orchestrator.system.facts.__coord.completedAgents;
187
209
  ```
188
210
 
189
- ## Checkpoint Serialization
211
+ ## Checkpoints
212
+
213
+ Multi-agent uses the same checkpoint shape as single-agent. **`checkpoint()` is async** — returns a `Promise<Checkpoint>`. Restore on an existing instance via `orch.restore(cp)`. There is no `createMultiAgentOrchestrator({ checkpoint })` constructor option.
190
214
 
191
215
  ```typescript
192
- // Save entire multi-agent state
193
- const checkpoint = orchestrator.checkpoint();
194
- const serialized = JSON.stringify(checkpoint);
195
-
196
- // Restore from checkpoint
197
- const restored = createMultiAgentOrchestrator({
198
- agents,
199
- patterns,
200
- runner,
201
- checkpoint: JSON.parse(serialized),
202
- });
216
+ const cp = await orchestrator.checkpoint({ label: "after-pipeline" });
217
+ const serialized = JSON.stringify(cp);
218
+
219
+ // On a fresh orchestrator with the same agents/patterns/runner:
220
+ const restored = createMultiAgentOrchestrator({ agents, patterns, runner });
221
+ restored.restore(JSON.parse(serialized));
203
222
  restored.start();
204
223
  ```
205
224
 
206
- ## Tasks in Multi-Agent Patterns
225
+ ## Tasks alongside agents
207
226
 
208
- Tasks and agents share the DAG/sequential/parallel namespace. See `ai-tasks.md` for details.
227
+ Tasks (registered via `tasks:`) share the handler namespace with agents — a `dag` or `sequential` can refer to either by ID. See `ai-tasks.md` for the full task surface.
209
228
 
210
229
  ```typescript
211
- const workflow = dag([
212
- { id: "research", handler: "researcher" },
213
- { id: "format", handler: "formatter-task" }, // task, not agent
214
- { id: "edit", handler: "editor", dependencies: ["research", "format"] },
215
- ]);
230
+ const workflow = dag({
231
+ research: { handler: "researcher" },
232
+ format: { handler: "formatter-task" }, // a task, not an agent
233
+ edit: { handler: "editor", deps: ["research", "format"] },
234
+ });
216
235
  ```
217
236
 
218
- ## Anti-Patterns
237
+ ## Anti-patterns
219
238
 
220
- ### #24: Forgetting start() for multi-agent
239
+ ### Forgetting `start()` on multi-agent
221
240
 
222
241
  ```typescript
223
- // WRONG multi-agent orchestrators require explicit start()
224
- const orchestrator = createMultiAgentOrchestrator({ agents, runner });
225
- const result = await orchestrator.runPattern("pipeline", "prompt");
226
- // Error: Orchestrator not started
242
+ // WRONG multi-agent orchestrators require explicit start()
243
+ const orch = createMultiAgentOrchestrator({ agents, patterns, runner });
244
+ await orch.runPattern("pipeline", "prompt"); // throws — not started
227
245
 
228
246
  // CORRECT
229
- const orchestrator = createMultiAgentOrchestrator({ agents, runner });
230
- orchestrator.start();
231
- const result = await orchestrator.runPattern("pipeline", "prompt");
247
+ const orch = createMultiAgentOrchestrator({ agents, patterns, runner });
248
+ orch.start();
249
+ await orch.runPattern("pipeline", "prompt");
232
250
  ```
233
251
 
234
- ### #30: race minSuccess greater than agent count
252
+ Single-agent `createAgentOrchestrator` does NOT require `start()`. Only multi-agent does.
253
+
254
+ ### Array-form `dag` and `dependencies` key
235
255
 
236
256
  ```typescript
237
- // WRONG minSuccess cannot exceed the number of agents
238
- const broken = race(["researcher", "writer"], {
239
- minSuccess: 3, // Only 2 agents, will never satisfy
240
- });
257
+ // WRONG dag takes Record<string, DagNode>, not an array; and the edge key is `deps`
258
+ dag([
259
+ { id: "research", handler: "researcher" },
260
+ { id: "write", handler: "writer", dependencies: ["research"] },
261
+ ])
262
+
263
+ // CORRECT
264
+ dag({
265
+ research: { handler: "researcher" },
266
+ write: { handler: "writer", deps: ["research"] },
267
+ })
268
+ ```
269
+
270
+ ### `parallel` without the merge arg
271
+
272
+ ```typescript
273
+ // WRONG — merge is a REQUIRED positional argument, not in options
274
+ parallel(["researcher", "writer"], { minSuccess: 1 })
275
+
276
+ // CORRECT — merge first, options second
277
+ parallel(
278
+ ["researcher", "writer"],
279
+ (results) => results.map((r) => String(r.output)).join("\n"),
280
+ { minSuccess: 1 },
281
+ )
282
+ ```
283
+
284
+ ### `reflect` with a single agent
285
+
286
+ ```typescript
287
+ // WRONG — reflect needs a SEPARATE evaluator handler; stopWhen is not an option
288
+ reflect("writer", { maxIterations: 3, stopWhen: (out) => out.includes("FINAL") })
289
+
290
+ // CORRECT — handler + evaluator + threshold (the evaluator decides when to stop)
291
+ reflect("writer", "reviewer", { maxIterations: 3, threshold: 0.8 })
292
+ ```
293
+
294
+ ### `debate` with `judge:` option
295
+
296
+ ```typescript
297
+ // WRONG — debate uses `evaluator`, not `judge`; and it's a single config arg
298
+ debate(["researcher", "writer"], { maxRounds: 5, judge: "editor" })
241
299
 
242
- // CORRECT minSuccess <= agents.length
243
- const working = race(["researcher", "writer"], {
244
- minSuccess: 1,
300
+ // CORRECT single config object, evaluator key
301
+ debate({ handlers: ["researcher", "writer"], evaluator: "editor", maxRounds: 5 })
302
+ ```
303
+
304
+ ### `goal` with a single agent string
305
+
306
+ ```typescript
307
+ // WRONG — goal does not take a single agent + goalCheck; it takes a node map + when()
308
+ goal("researcher", { maxIterations: 10, goalCheck: (out, facts) => facts.confidence > 0.9 })
309
+
310
+ // CORRECT — declarative node map + a when() predicate
311
+ goal(
312
+ {
313
+ researcher: {
314
+ handler: "researcher",
315
+ produces: ["confidence"],
316
+ requires: ["topic"],
317
+ extractOutput: (r) => ({ confidence: (r.output as { confidence: number }).confidence }),
318
+ },
319
+ },
320
+ (facts) => (facts.confidence as number) > 0.9,
321
+ { maxSteps: 10 },
322
+ )
323
+ ```
324
+
325
+ ### Handler IDs that don't match `agents:` keys
326
+
327
+ ```typescript
328
+ // WRONG — handler refs must match the keys in agents/tasks
329
+ createMultiAgentOrchestrator({
330
+ agents: { researcher: {...}, writer: {...} },
331
+ patterns: { pipeline: sequential(["research-agent", "write-agent"]) }, // wrong names
245
332
  });
333
+
334
+ // CORRECT
335
+ patterns: { pipeline: sequential(["researcher", "writer"]) }
246
336
  ```
247
337
 
248
- ### Reusing agent names across patterns
338
+ ### `race` with `minSuccess` greater than the handler count
249
339
 
250
340
  ```typescript
251
- // WRONG agent names must match keys in the agents config
252
- patterns: {
253
- pipeline: sequential(["research-agent", "write-agent"]),
254
- // These don't match the keys "researcher", "writer"
255
- },
256
-
257
- // CORRECT – use the exact keys from agents config
258
- patterns: {
259
- pipeline: sequential(["researcher", "writer"]),
260
- },
341
+ // WRONG minSuccess can never exceed handlers.length
342
+ race(["researcher", "writer"], { minSuccess: 3 }) // 2 handlers, impossible
343
+ ```
344
+
345
+ ### Restoring via a constructor option
346
+
347
+ ```typescript
348
+ // WRONG — there is no `checkpoint` option on createMultiAgentOrchestrator
349
+ createMultiAgentOrchestrator({ agents, patterns, runner, checkpoint: cp })
350
+
351
+ // CORRECT — restore on an existing instance, then start()
352
+ const orch = createMultiAgentOrchestrator({ agents, patterns, runner });
353
+ orch.restore(cp);
354
+ orch.start();
261
355
  ```
262
356
 
263
- ## Quick Reference
357
+ ## Quick reference
264
358
 
265
- | Pattern | Use Case | Key Option |
359
+ | Pattern | Signature | Required arg 2 |
266
360
  |---|---|---|
267
- | `parallel()` | Independent work, merge results | merge function |
268
- | `sequential()` | Pipeline, each feeds next | agent order |
269
- | `supervisor()` | Dynamic delegation | supervisor agent |
270
- | `dag()` | Complex dependencies | DagNode[] |
271
- | `reflect()` | Self-improvement loop | maxIterations, stopWhen |
272
- | `race()` | Fastest wins | minSuccess, timeout |
273
- | `debate()` | Consensus building | maxRounds, judge |
274
- | `goal()` | Condition-driven iteration | goalCheck |
361
+ | `parallel(handlers, merge, options?)` | `string[], (results) => T, { minSuccess?, timeout? }` | merge |
362
+ | `sequential(handlers, options?)` | `string[], { transform?, extract?, continueOnError? }` | |
363
+ | `supervisor(supervisorAgent, workers, options?)` | `string, string[], { maxRounds?, extract? }` | workers |
364
+ | `dag(nodes, merge?, options?)` | `Record<string, DagNode>, (ctx) => T, { timeout?, maxConcurrent?, onNodeError? }` | |
365
+ | `reflect(handler, evaluator, options?)` | `string, string, { maxIterations?, threshold?, onExhausted?, onIteration? }` | evaluator |
366
+ | `race(handlers, options?)` | `string[], { timeout?, minSuccess?, extract? }` | — |
367
+ | `debate(config)` | `{ handlers, evaluator, maxRounds?, extract?, parseJudgement? }` | — (single arg) |
368
+ | `goal(nodes, when, options?)` | `Record<string, GoalNode>, (facts) => boolean, { maxSteps?, extract?, satisfaction? }` | when |