@directive-run/claude-plugin 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 (83) hide show
  1. package/.claude-plugin/plugin.json +6 -0
  2. package/LICENSE +26 -0
  3. package/README.md +113 -0
  4. package/dist/index.cjs +2 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +66 -0
  7. package/dist/index.d.ts +66 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/package.json +64 -0
  11. package/skills/README.md +9 -0
  12. package/skills/building-ai-agents/SKILL.md +414 -0
  13. package/skills/building-ai-agents/ai-adapters.md +257 -0
  14. package/skills/building-ai-agents/ai-agents-streaming.md +307 -0
  15. package/skills/building-ai-agents/ai-communication.md +304 -0
  16. package/skills/building-ai-agents/api-skeleton.md +5 -0
  17. package/skills/building-ai-agents/examples.md +849 -0
  18. package/skills/building-ai-orchestrators/SKILL.md +401 -0
  19. package/skills/building-ai-orchestrators/ai-multi-agent.md +375 -0
  20. package/skills/building-ai-orchestrators/ai-orchestrator.md +466 -0
  21. package/skills/building-ai-orchestrators/ai-tasks.md +268 -0
  22. package/skills/building-ai-orchestrators/api-skeleton.md +5 -0
  23. package/skills/building-ai-orchestrators/examples.md +1609 -0
  24. package/skills/building-directive-systems/SKILL.md +499 -0
  25. package/skills/building-directive-systems/api-skeleton.md +5 -0
  26. package/skills/building-directive-systems/examples.md +4149 -0
  27. package/skills/building-directive-systems/multi-module.md +327 -0
  28. package/skills/building-directive-systems/plugins.md +361 -0
  29. package/skills/building-directive-systems/react-adapter.md +376 -0
  30. package/skills/building-directive-systems/system-api.md +463 -0
  31. package/skills/getting-started-with-directive/SKILL.md +198 -0
  32. package/skills/getting-started-with-directive/api-skeleton.md +5 -0
  33. package/skills/getting-started-with-directive/core-patterns.md +240 -0
  34. package/skills/getting-started-with-directive/examples.md +87 -0
  35. package/skills/getting-started-with-directive/sitemap.md +202 -0
  36. package/skills/hardening-ai-systems/SKILL.md +377 -0
  37. package/skills/hardening-ai-systems/ai-budget-resilience.md +408 -0
  38. package/skills/hardening-ai-systems/ai-guardrails-memory.md +370 -0
  39. package/skills/hardening-ai-systems/ai-security.md +400 -0
  40. package/skills/hardening-ai-systems/api-skeleton.md +5 -0
  41. package/skills/hardening-ai-systems/examples.md +628 -0
  42. package/skills/migrating-to-directive/SKILL.md +249 -0
  43. package/skills/migrating-to-directive/anti-patterns.md +382 -0
  44. package/skills/migrating-to-directive/api-skeleton.md +5 -0
  45. package/skills/migrating-to-directive/core-patterns.md +240 -0
  46. package/skills/migrating-to-directive/examples.md +511 -0
  47. package/skills/migrating-to-directive/schema-types.md +273 -0
  48. package/skills/reviewing-directive-code/SKILL.md +232 -0
  49. package/skills/reviewing-directive-code/anti-patterns.md +382 -0
  50. package/skills/reviewing-directive-code/api-skeleton.md +5 -0
  51. package/skills/reviewing-directive-code/core-patterns.md +240 -0
  52. package/skills/reviewing-directive-code/examples.md +463 -0
  53. package/skills/reviewing-directive-code/naming.md +321 -0
  54. package/skills/scaffolding-directive-modules/SKILL.md +218 -0
  55. package/skills/scaffolding-directive-modules/api-skeleton.md +5 -0
  56. package/skills/scaffolding-directive-modules/core-patterns.md +240 -0
  57. package/skills/scaffolding-directive-modules/examples.md +1386 -0
  58. package/skills/scaffolding-directive-modules/naming.md +321 -0
  59. package/skills/scaffolding-directive-modules/schema-types.md +273 -0
  60. package/skills/testing-ai-systems/SKILL.md +398 -0
  61. package/skills/testing-ai-systems/ai-debug-observability.md +329 -0
  62. package/skills/testing-ai-systems/ai-mcp-rag.md +293 -0
  63. package/skills/testing-ai-systems/ai-testing-evals.md +485 -0
  64. package/skills/testing-ai-systems/api-skeleton.md +5 -0
  65. package/skills/testing-ai-systems/examples.md +1261 -0
  66. package/skills/testing-directive-code/SKILL.md +479 -0
  67. package/skills/testing-directive-code/api-skeleton.md +5 -0
  68. package/skills/testing-directive-code/examples.md +156 -0
  69. package/skills/testing-directive-code/history.md +351 -0
  70. package/skills/testing-directive-code/testing.md +412 -0
  71. package/skills/writing-directive-constraints/SKILL.md +486 -0
  72. package/skills/writing-directive-constraints/api-skeleton.md +5 -0
  73. package/skills/writing-directive-constraints/constraints.md +274 -0
  74. package/skills/writing-directive-constraints/error-boundaries.md +330 -0
  75. package/skills/writing-directive-constraints/examples.md +1361 -0
  76. package/skills/writing-directive-constraints/resolvers.md +367 -0
  77. package/skills/writing-directive-modules/SKILL.md +342 -0
  78. package/skills/writing-directive-modules/anti-patterns.md +382 -0
  79. package/skills/writing-directive-modules/api-skeleton.md +5 -0
  80. package/skills/writing-directive-modules/core-patterns.md +240 -0
  81. package/skills/writing-directive-modules/examples.md +1639 -0
  82. package/skills/writing-directive-modules/naming.md +321 -0
  83. package/skills/writing-directive-modules/schema-types.md +273 -0
@@ -0,0 +1,485 @@
1
+ # AI testing and evaluations
2
+
3
+ > Covers `@directive-run/ai/testing` and `@directive-run/ai/evals` — `createMockAgentRunner`, `createTestOrchestrator`, simulators, `createEvalSuite` + `eval*` criteria.
4
+
5
+ Mock runners, test orchestrators, snapshot helpers, simulators, and a dataset-driven evaluation framework. Import all testing utilities from `@directive-run/ai/testing`; eval criteria factories come from `@directive-run/ai/evals`.
6
+
7
+ ## Decision tree
8
+
9
+ ```
10
+ What are you testing?
11
+ ├── Single-agent runs (unit/integration) → createTestOrchestrator
12
+ ├── Multi-agent patterns → createTestMultiAgentOrchestrator
13
+ ├── Just the runner contract → createMockAgentRunner
14
+ ├── Approval workflow → createApprovalSimulator (built into createTestOrchestrator)
15
+ ├── Breakpoint pauses → createBreakpointSimulator
16
+ ├── DAG topology → createTestDag + assertDagExecution
17
+ ├── Failure modes (timeouts, errors) → createFailingRunner
18
+ ├── Snapshot constraint behavior → createConstraintRecorder
19
+ └── Time-dependent logic → createTimeController
20
+
21
+ What are you evaluating?
22
+ ├── Output quality → createEvalSuite + eval* criteria
23
+ ├── Token / latency budgets → evalCost + evalLatency
24
+ ├── Output shape / size → evalOutputLength + evalStructure
25
+ ├── Safety / PII → evalSafety
26
+ ├── Reference match → evalMatch
27
+ ├── Faithfulness / relevance / coherence → evalFaithfulness / evalRelevance / evalCoherence
28
+ ├── LLM-as-judge → evalJudge
29
+ └── Custom assertion → evalAssert
30
+ ```
31
+
32
+ ## Mock runner
33
+
34
+ `createMockAgentRunner` returns `{ run, getCalls, getCallsFor, clearCalls, setResponse, setDefaultResponse }`. The factory takes a SINGLE options object — responses are keyed by **agent name**, NOT input pattern. (For input-pattern matching, use `generate` inside a response.) **The function is named `createMockAgentRunner`, not `createMockRunner`**.
35
+
36
+ ```typescript
37
+ import { createMockAgentRunner } from "@directive-run/ai/testing";
38
+
39
+ const mock = createMockAgentRunner({
40
+ responses: {
41
+ analyst: { output: "Analysis: positive trend", totalTokens: 100 },
42
+ writer: { output: "Draft article…", totalTokens: 200, delay: 50 },
43
+ failing: { output: "n/a", error: new Error("simulated 503") },
44
+ },
45
+ defaultResponse: { output: "mock response", totalTokens: 10 },
46
+ recordCalls: true,
47
+ });
48
+
49
+ const orchestrator = createAgentOrchestrator({ runner: mock.run });
50
+
51
+ await orchestrator.run({ name: "analyst", instructions: "…", model: "x" }, "Analyze this");
52
+
53
+ mock.getCalls(); // RecordedCall[] — every recorded run
54
+ mock.getCallsFor("analyst"); // RecordedCall[] — only this agent
55
+ mock.setResponse("analyst", { output: "different reply", totalTokens: 80 });
56
+ mock.clearCalls();
57
+ ```
58
+
59
+ For dynamic responses keyed on input, use the `generate` callback inside the config:
60
+
61
+ ```typescript
62
+ const mock = createMockAgentRunner({
63
+ responses: {
64
+ classifier: {
65
+ output: "fallback",
66
+ totalTokens: 10,
67
+ generate: (input) => /summarize/i.test(input)
68
+ ? { output: "Summary: …", totalTokens: 50 }
69
+ : { output: "Default reply", totalTokens: 10 },
70
+ },
71
+ },
72
+ });
73
+ ```
74
+
75
+ ## Test orchestrator
76
+
77
+ `createTestOrchestrator` extends `AgentOrchestrator` with mock-runner wiring, an approval simulator, and call recording. Pass `mockResponses` and Directive constraints/resolvers exactly as you would to the production factory.
78
+
79
+ ```typescript
80
+ import { createTestOrchestrator } from "@directive-run/ai/testing";
81
+ import { t } from "@directive-run/core";
82
+
83
+ const test = createTestOrchestrator({
84
+ mockResponses: {
85
+ analyst: { output: "Analysis: positive trend", totalTokens: 100 },
86
+ },
87
+ factsSchema: {
88
+ result: t.string(),
89
+ confidence: t.number(),
90
+ },
91
+ init: (facts) => {
92
+ facts.result = "";
93
+ facts.confidence = 0;
94
+ },
95
+ constraints: {
96
+ lowConfidence: {
97
+ when: (facts) => facts.confidence < 0.5,
98
+ require: { type: "RE_ANALYZE" },
99
+ },
100
+ },
101
+ resolvers: {
102
+ reAnalyze: {
103
+ requirement: (r): r is { type: "RE_ANALYZE" } => r.type === "RE_ANALYZE",
104
+ resolve: async (req, context) => { context.facts.confidence = 0.8; },
105
+ },
106
+ },
107
+ });
108
+
109
+ const result = await test.run({ name: "analyst", instructions: "…", model: "x" }, "Analyze");
110
+
111
+ test.getCalls(); // RecordedCall[]
112
+ test.getApprovalRequests(); // ApprovalRequest[] from the built-in simulator
113
+ test.mockRunner; // the underlying MockAgentRunner
114
+ test.approvalSimulator; // the built-in ApprovalSimulator
115
+ test.resetAll(); // reset orchestrator + clear calls + clear approvals
116
+ ```
117
+
118
+ ## Test multi-agent orchestrator
119
+
120
+ ```typescript
121
+ import {
122
+ createTestMultiAgentOrchestrator,
123
+ createMockAgentRunner,
124
+ assertMultiAgentState,
125
+ } from "@directive-run/ai/testing";
126
+ import { sequential } from "@directive-run/ai/multi-agent";
127
+
128
+ const mock = createMockAgentRunner({
129
+ responses: {
130
+ researcher: { output: "research notes", totalTokens: 150 },
131
+ writer: { output: "draft article", totalTokens: 200 },
132
+ },
133
+ });
134
+
135
+ const orch = createTestMultiAgentOrchestrator({
136
+ agents: {
137
+ researcher: { name: "researcher", instructions: "research", model: "x" },
138
+ writer: { name: "writer", instructions: "write", model: "x" },
139
+ },
140
+ patterns: {
141
+ pipeline: sequential(["researcher", "writer"]),
142
+ },
143
+ runner: mock.run,
144
+ });
145
+
146
+ orch.start();
147
+ await orch.runPattern("pipeline", "Write about AI");
148
+
149
+ assertMultiAgentState(orch, {
150
+ status: "completed",
151
+ });
152
+ ```
153
+
154
+ ## Snapshot / introspection helpers
155
+
156
+ | Helper | Purpose |
157
+ |---|---|
158
+ | `createConstraintRecorder()` | Records every constraint evaluation as a `ConstraintSnapshot`; pair with `assertOrchestratorState`. |
159
+ | `assertOrchestratorState(orch, expected)` | Diffs facts, status, and constraint state against an expected shape. |
160
+ | `assertMultiAgentState(orch, expected)` | Same shape, for multi-agent. |
161
+ | `assertScratchpadState(orch, expected)` | Checks the multi-agent scratchpad contents. |
162
+ | `assertDerivedValues(orch, expected)` | Diffs derivation values. |
163
+ | `assertTimelineEvents(timeline, expected)` | Asserts a sequence of timeline event types/payloads. |
164
+ | `assertDagExecution(events, expected)` | Asserts DAG node execution order. |
165
+ | `assertBreakpointHit(events, expected)` | Asserts a breakpoint was hit with given metadata. |
166
+ | `assertRerouted(events, expected)` | Asserts a self-healing reroute fired (when configured). |
167
+ | `assertAgentHealth(monitor, agentId, expected)` | Asserts a health score range / passing failed counts. |
168
+ | `assertCheckpoint(cp, expected)` | Asserts checkpoint shape (state version, conversation length, etc.). |
169
+ | `assertMultiplexedStream(stream, expected)` | Asserts a multiplexed stream produced expected per-agent chunks. |
170
+
171
+ There is no `assertAgentCalled` / `assertAgentNotCalled` / `assertTokensUsed` / `assertGuardrailPassed` / `assertGuardrailBlocked` — those don't exist. Use `mock.getCalls()` + `expect()` from your test framework, and inspect `result.tokenUsage` and `result.guardrailEvents` directly.
172
+
173
+ ```typescript
174
+ import { expect } from "vitest";
175
+
176
+ const calls = mock.getCallsFor("analyst");
177
+ expect(calls).toHaveLength(1);
178
+ expect(calls[0].input).toMatch(/analyze/i);
179
+
180
+ expect(result.tokenUsage).toBeLessThan(500);
181
+ ```
182
+
183
+ ## Simulators
184
+
185
+ ```typescript
186
+ import {
187
+ createFailingRunner,
188
+ createApprovalSimulator,
189
+ createBreakpointSimulator,
190
+ createTimeController,
191
+ createTestCheckpointStore,
192
+ createTestReflectionEvaluator,
193
+ createTestEmbedder,
194
+ } from "@directive-run/ai/testing";
195
+ ```
196
+
197
+ ### `createFailingRunner(error?, options?)`
198
+
199
+ Returns an `AgentRunner` that throws (or fails after N successes). Replaces the imagined `createErrorSimulator` / `createLatencySimulator`.
200
+
201
+ ```typescript
202
+ const failing = createFailingRunner(new Error("rate_limit"), {
203
+ delay: 100,
204
+ failAfter: 2, // first 2 calls succeed, then it starts throwing
205
+ });
206
+
207
+ const orchestrator = createAgentOrchestrator({
208
+ runner: failing,
209
+ selfHealing: { fallbackRunners: [backupRunner] },
210
+ });
211
+ ```
212
+
213
+ For latency-only simulation, set `delay` on a `MockAgentConfig`:
214
+
215
+ ```typescript
216
+ const slow = createMockAgentRunner({
217
+ responses: { writer: { output: "slow reply", totalTokens: 50, delay: 800 } },
218
+ });
219
+ ```
220
+
221
+ ### `createApprovalSimulator(options?)`
222
+
223
+ Drives the approval workflow deterministically — auto-approve, auto-reject, or queue.
224
+
225
+ ```typescript
226
+ const sim = createApprovalSimulator({ defaultAction: "approve" });
227
+
228
+ const orchestrator = createAgentOrchestrator({
229
+ runner,
230
+ autoApproveToolCalls: false,
231
+ onApprovalRequest: (req) => sim.handle(req),
232
+ });
233
+
234
+ const requests = sim.getRequests();
235
+ sim.clearRequests();
236
+ ```
237
+
238
+ ### `createBreakpointSimulator(options?)`
239
+
240
+ Resolves breakpoints automatically, with optional modifications. Pair with the orchestrator's `breakpoints:` config.
241
+
242
+ ### `createTimeController(startTime?)`
243
+
244
+ Drives `Date.now()` and `setTimeout` deterministically — required when testing budget windows, retry backoffs, or memory expiry.
245
+
246
+ ### `createTestCheckpointStore()`
247
+
248
+ In-memory checkpoint store for round-tripping `checkpoint()` + `restore()` without disk IO.
249
+
250
+ ### `createTestReflectionEvaluator(options?)`
251
+
252
+ Deterministic evaluator for `reflect()` patterns — scores can be hard-coded per iteration.
253
+
254
+ ### `createTestEmbedder()`
255
+
256
+ Deterministic embedder (no network calls) for `createSemanticCache` testing.
257
+
258
+ ## Evaluation framework
259
+
260
+ `createEvalSuite` runs a dataset of test cases through one or more agents, scores each output with one or more `EvalCriterion`, and returns aggregate results. Criteria are NOT a `criteria.*()` namespace — they're top-level `eval*` factories.
261
+
262
+ ```typescript
263
+ import {
264
+ createEvalSuite,
265
+ evalCost,
266
+ evalLatency,
267
+ evalOutputLength,
268
+ evalSafety,
269
+ evalStructure,
270
+ evalMatch,
271
+ evalJudge,
272
+ evalFaithfulness,
273
+ evalRelevance,
274
+ evalCoherence,
275
+ evalAssert,
276
+ } from "@directive-run/ai/evals";
277
+
278
+ const suite = createEvalSuite({
279
+ agents: [analystAgent, writerAgent],
280
+ runner,
281
+ dataset: [
282
+ { id: "ts-basics", input: "What is TypeScript?", expected: "TypeScript is…", tags: ["basics"] },
283
+ { id: "monads", input: "Explain monads", expected: "A monad is…", tags: ["advanced"] },
284
+ { id: "ssr-tradeoffs", input: "Server vs client rendering", expected: "Tradeoffs…", tags: ["advanced"] },
285
+ ],
286
+ criteria: {
287
+ cost: evalCost({ maxTokensPerRun: 500 }),
288
+ latency: evalLatency({ maxMs: 5000 }),
289
+ safety: evalSafety(),
290
+ relevance: evalRelevance({ embedder: embedderFn, minSimilarity: 0.7 }),
291
+ judge: evalJudge({ runner, judge: judgeAgent }),
292
+ },
293
+ concurrency: 4,
294
+ onCaseComplete: (caseResult) => console.log(`✓ ${caseResult.caseId} — ${caseResult.score.toFixed(2)}`),
295
+ });
296
+
297
+ const results = await suite.run();
298
+
299
+ console.log(results.summary.averageScore); // 0.82
300
+ console.log(results.summary.passRate); // 0.90
301
+ console.log(results.cases); // EvalCaseResult[]
302
+ console.log(results.agentSummaries); // EvalAgentSummary[] (per-agent breakdown)
303
+ console.log(results.totalTokens, results.durationMs);
304
+ ```
305
+
306
+ ### Custom criterion
307
+
308
+ A criterion is `{ name, fn, threshold?, weight? }` where `fn` returns `{ score, passed?, reason?, durationMs? }`. **Scorers must be pure** — no side effects.
309
+
310
+ ```typescript
311
+ const codeQuality = {
312
+ name: "code-quality",
313
+ threshold: 0.6,
314
+ weight: 1.0,
315
+ fn: (ctx) => {
316
+ const start = Date.now();
317
+ const output = String(ctx.result.output);
318
+ const hasCode = /\b(function|const|class)\b/.test(output);
319
+ const hasExplanation = output.length > 100;
320
+
321
+ const score = hasCode && hasExplanation ? 1.0 : hasCode ? 0.7 : 0.2;
322
+ return {
323
+ score,
324
+ passed: score >= 0.6,
325
+ reason: hasCode && hasExplanation ? "code with explanation"
326
+ : hasCode ? "code only"
327
+ : "no code",
328
+ durationMs: Date.now() - start,
329
+ };
330
+ },
331
+ };
332
+
333
+ const suite = createEvalSuite({
334
+ agents: [coderAgent],
335
+ runner,
336
+ dataset,
337
+ criteria: { codeQuality },
338
+ });
339
+ ```
340
+
341
+ ### LLM-as-judge
342
+
343
+ ```typescript
344
+ import { evalJudge } from "@directive-run/ai/evals";
345
+
346
+ const judge = evalJudge({
347
+ runner, // any AgentRunner — typically a smaller/cheaper model
348
+ judge: judgeAgent, // the AgentLike to invoke
349
+ promptTemplate: `…`, // optional — defaults to a JSON-output rubric
350
+ timeoutMs: 30_000,
351
+ });
352
+ ```
353
+
354
+ The judge returns `{ score: number, reason?: string }` parsed from JSON. The default prompt enforces "Respond with ONLY a JSON object: …" — override `promptTemplate` if you need a different rubric.
355
+
356
+ ### CI quality gates
357
+
358
+ ```typescript
359
+ const results = await suite.run();
360
+
361
+ if (results.summary.averageScore < 0.75) {
362
+ console.error(`Quality gate failed: ${results.summary.averageScore} < 0.75`);
363
+ process.exit(1);
364
+ }
365
+
366
+ for (const [criterionName, summary] of Object.entries(results.summary.byCriterion)) {
367
+ if (summary.averageScore < 0.6) {
368
+ console.error(`${criterionName} regressed: ${summary.averageScore}`);
369
+ process.exit(1);
370
+ }
371
+ }
372
+ ```
373
+
374
+ ## Anti-patterns
375
+
376
+ ### Calling `createMockRunner` (singular, with input-pattern array)
377
+
378
+ ```typescript
379
+ // WRONG — createMockRunner does not exist; the array-pattern form does not exist
380
+ createMockRunner([{ input: /analyze/, output: "…" }])
381
+
382
+ // CORRECT — keyed by agent name
383
+ createMockAgentRunner({
384
+ responses: { analyst: { output: "…", totalTokens: 100 } },
385
+ })
386
+ ```
387
+
388
+ ### Importing hallucinated assertions
389
+
390
+ ```typescript
391
+ // WRONG — none of these exist
392
+ import {
393
+ assertAgentCalled,
394
+ assertAgentNotCalled,
395
+ assertTokensUsed,
396
+ assertGuardrailPassed,
397
+ assertGuardrailBlocked,
398
+ } from "@directive-run/ai/testing";
399
+
400
+ // CORRECT — use the mock's recorder + your test framework's assertions
401
+ const calls = mock.getCallsFor("analyst");
402
+ expect(calls).toHaveLength(1);
403
+ expect(result.tokenUsage).toBeLessThan(200);
404
+ ```
405
+
406
+ ### Importing `createEvaluator`, `criteria.*()`, `createLLMJudge`, `createEvaluationSuite`
407
+
408
+ ```typescript
409
+ // WRONG — these are all hallucinated names
410
+ import { createEvaluator, criteria, createLLMJudge, createEvaluationSuite } from "@directive-run/ai/testing";
411
+ const evaluator = createEvaluator({ criteria: [criteria.relevance(), criteria.coherence()] });
412
+ const judge = createLLMJudge({ runner, model: "…", criteria: ["accuracy"], rubric: "…" });
413
+
414
+ // CORRECT — top-level eval factories + createEvalSuite
415
+ import { createEvalSuite, evalRelevance, evalCoherence, evalJudge } from "@directive-run/ai/evals";
416
+ const suite = createEvalSuite({
417
+ agents: [agent],
418
+ runner,
419
+ dataset,
420
+ criteria: {
421
+ relevance: evalRelevance({ embedder, minSimilarity: 0.7 }),
422
+ coherence: evalCoherence({ embedder, minSimilarity: 0.5 }),
423
+ judge: evalJudge({ runner, judge: judgeAgent }),
424
+ },
425
+ });
426
+ ```
427
+
428
+ ### Importing `createErrorSimulator` / `createLatencySimulator`
429
+
430
+ ```typescript
431
+ // WRONG — neither exists
432
+ import { createErrorSimulator, createLatencySimulator } from "@directive-run/ai/testing";
433
+
434
+ // CORRECT — createFailingRunner for errors; delay in MockAgentConfig for latency
435
+ import { createFailingRunner, createMockAgentRunner } from "@directive-run/ai/testing";
436
+ const failing = createFailingRunner(new Error("503"), { failAfter: 2, delay: 100 });
437
+ const slow = createMockAgentRunner({ responses: { writer: { output: "…", delay: 800 } } });
438
+ ```
439
+
440
+ ### Side-effecting scorers
441
+
442
+ ```typescript
443
+ // WRONG — scorers run inside the eval suite and must be pure
444
+ {
445
+ name: "quality",
446
+ fn: (ctx) => {
447
+ fs.writeFileSync("eval.log", String(ctx.result.output)); // ← side effect
448
+ metrics.increment("evals"); // ← side effect
449
+ return { score: 0.8 };
450
+ },
451
+ }
452
+
453
+ // CORRECT — log/metrics in onCaseComplete or after suite.run()
454
+ const suite = createEvalSuite({
455
+ …,
456
+ onCaseComplete: (caseResult) => {
457
+ metrics.increment("evals");
458
+ fs.appendFileSync("eval.log", JSON.stringify(caseResult) + "\n");
459
+ },
460
+ });
461
+ ```
462
+
463
+ ## Quick reference
464
+
465
+ | API | Import path | Purpose |
466
+ |---|---|---|
467
+ | `createMockAgentRunner` | `@directive-run/ai/testing` | Deterministic mock runner with call recording |
468
+ | `createTestOrchestrator` | `@directive-run/ai/testing` | Single-agent orchestrator with mock + approval simulator |
469
+ | `createTestMultiAgentOrchestrator` | `@directive-run/ai/testing` | Multi-agent orchestrator wired to a mock runner |
470
+ | `createFailingRunner` | `@directive-run/ai/testing` | Runner that throws (or fails after N successes) |
471
+ | `createApprovalSimulator` | `@directive-run/ai/testing` | Deterministic approve/reject driver |
472
+ | `createBreakpointSimulator` | `@directive-run/ai/testing` | Deterministic breakpoint resolver |
473
+ | `createTimeController` | `@directive-run/ai/testing` | Virtual clock for budgets / backoffs / TTLs |
474
+ | `createTestCheckpointStore` | `@directive-run/ai/testing` | In-memory checkpoint store |
475
+ | `createTestReflectionEvaluator` | `@directive-run/ai/testing` | Deterministic evaluator for `reflect()` |
476
+ | `createTestEmbedder` | `@directive-run/ai/testing` | Deterministic embedder for cache tests |
477
+ | `assertOrchestratorState` / `assertMultiAgentState` / `assertScratchpadState` / `assertDerivedValues` / `assertTimelineEvents` / `assertDagExecution` / `assertBreakpointHit` / `assertRerouted` / `assertAgentHealth` / `assertCheckpoint` / `assertMultiplexedStream` | `@directive-run/ai/testing` | State/topology/event assertions |
478
+ | `createEvalSuite` | `@directive-run/ai/evals` | Dataset-driven multi-criterion eval runner |
479
+ | `evalCost` / `evalLatency` / `evalOutputLength` / `evalSafety` / `evalStructure` / `evalMatch` / `evalJudge` / `evalFaithfulness` / `evalRelevance` / `evalCoherence` / `evalAssert` | `@directive-run/ai/evals` | Built-in criterion factories |
480
+
481
+ ## See also
482
+
483
+ - [`ai-orchestrator.md`](./ai-orchestrator.md) — `createTestOrchestrator` wraps this orchestrator; the prod surface is what your tests are exercising
484
+ - [`ai-multi-agent.md`](./ai-multi-agent.md) — `createTestMultiAgentOrchestrator` for testing pattern execution
485
+ - [`ai-debug-observability.md`](./ai-debug-observability.md) — recorded debug events feed `assertTimelineEvents` / `assertDagExecution` / `assertBreakpointHit`
@@ -0,0 +1,5 @@
1
+ # API Skeleton
2
+
3
+ > Auto-generated. Do not edit.
4
+
5
+ > Source JSON not found. Build website docs first.