@vibe-agent-toolkit/vat-development-agents 0.1.13 → 0.1.14

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.
@@ -0,0 +1,628 @@
1
+ # Adding Runtime Adapters
2
+
3
+ This guide documents best practices for adding new runtime adapters to the Vibe Agent Toolkit.
4
+
5
+ ## Overview
6
+
7
+ A runtime adapter converts VAT agents into runtime-specific formats (tools, functions, etc.) for use with specific LLM frameworks (Vercel AI SDK, LangChain, OpenAI SDK, Claude Agent SDK, etc.).
8
+
9
+ ## Quick Start Checklist
10
+
11
+ - [ ] Create package structure in `packages/runtime-{name}/`
12
+ - [ ] Use shared test factories (zero code duplication)
13
+ - [ ] Use shared demo infrastructure (common-demo.ts)
14
+ - [ ] Add TypeScript project reference
15
+ - [ ] Implement both pure function and LLM analyzer adapters
16
+ - [ ] Write comprehensive tests with test helpers
17
+ - [ ] Create demo showcasing both providers (if applicable)
18
+ - [ ] Update root tsconfig.json
19
+
20
+ ## Package Structure
21
+
22
+ ```
23
+ packages/runtime-{name}/
24
+ ├── src/
25
+ │ ├── adapters/
26
+ │ │ ├── common-helpers.ts # Shared utilities across adapters
27
+ │ │ ├── pure-function.ts # Pure function agent adapter
28
+ │ │ └── llm-analyzer.ts # LLM analyzer agent adapter
29
+ │ ├── types.ts # TypeScript type definitions
30
+ │ └── index.ts # Public API exports
31
+ ├── test/
32
+ │ ├── pure-function.test.ts # Pure function adapter tests
33
+ │ ├── llm-analyzer.test.ts # LLM analyzer adapter tests
34
+ │ └── test-helpers.ts # Test utilities (extract when 2-3+ tests)
35
+ ├── examples/
36
+ │ └── demo.ts # Demo using common-demo infrastructure
37
+ ├── package.json
38
+ ├── tsconfig.json
39
+ └── README.md
40
+ ```
41
+
42
+ ## 1. Package Configuration
43
+
44
+ ### package.json
45
+
46
+ ```json
47
+ {
48
+ "name": "@vibe-agent-toolkit/runtime-{name}",
49
+ "version": "0.1.1",
50
+ "description": "{Runtime Name} runtime adapter for VAT agents",
51
+ "type": "module",
52
+ "exports": {
53
+ ".": {
54
+ "types": "./dist/index.d.ts",
55
+ "default": "./dist/index.js"
56
+ }
57
+ },
58
+ "files": ["dist"],
59
+ "scripts": {
60
+ "build": "tsc",
61
+ "demo": "tsx examples/demo.ts",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
64
+ "typecheck": "tsc --noEmit"
65
+ },
66
+ "dependencies": {
67
+ "@vibe-agent-toolkit/agent-runtime": "workspace:*",
68
+ "zod": "^3.24.1",
69
+ "{runtime-sdk}": "^x.y.z"
70
+ },
71
+ "devDependencies": {
72
+ "@types/node": "^22.10.5",
73
+ "@vibe-agent-toolkit/dev-tools": "workspace:*",
74
+ "@vibe-agent-toolkit/vat-example-cat-agents": "workspace:*",
75
+ "tsx": "^4.19.2",
76
+ "typescript": "^5.7.3",
77
+ "vitest": "^2.1.8"
78
+ }
79
+ }
80
+ ```
81
+
82
+ **Key points:**
83
+ - Use `workspace:*` for all internal dependencies
84
+ - Add runtime SDK as dependency
85
+ - Include dev-tools for test factories
86
+ - Include vat-example-cat-agents for testing
87
+
88
+ ### tsconfig.json
89
+
90
+ ```json
91
+ {
92
+ "extends": "../../tsconfig.base.json",
93
+ "compilerOptions": {
94
+ "composite": true,
95
+ "outDir": "./dist",
96
+ "rootDir": "./src"
97
+ },
98
+ "include": ["src/**/*"],
99
+ "references": [
100
+ { "path": "../agent-runtime" },
101
+ { "path": "../utils" }
102
+ ]
103
+ }
104
+ ```
105
+
106
+ **Key points:**
107
+ - Always set `composite: true` for monorepo builds
108
+ - Add references for packages you depend on
109
+ - Use `../../tsconfig.base.json` for consistency
110
+
111
+ ### Root tsconfig.json
112
+
113
+ Add your package to the root tsconfig.json references:
114
+
115
+ ```json
116
+ {
117
+ "references": [
118
+ // ... existing packages
119
+ { "path": "./packages/runtime-{name}" }
120
+ ]
121
+ }
122
+ ```
123
+
124
+ ## 2. Adapter Implementation
125
+
126
+ ### Common Pattern
127
+
128
+ Both pure function and LLM analyzer adapters follow similar patterns:
129
+
130
+ ```typescript
131
+ // src/adapters/pure-function.ts
132
+ import type { PureFunctionAgent } from '@vibe-agent-toolkit/agent-runtime';
133
+ import type { z } from 'zod';
134
+
135
+ export function convertPureFunctionToTool<TInput, TOutput>(
136
+ agent: PureFunctionAgent<TInput, TOutput>,
137
+ inputSchema: z.ZodType<TInput>,
138
+ outputSchema: z.ZodType<TOutput>,
139
+ ) {
140
+ // 1. Extract agent manifest
141
+ const { manifest } = agent;
142
+
143
+ // 2. Create runtime-specific tool/function
144
+ const tool = createRuntimeTool(
145
+ manifest.name,
146
+ manifest.description,
147
+ inputSchema,
148
+ async (input: TInput) => {
149
+ // 3. Validate input
150
+ const validatedInput = inputSchema.parse(input);
151
+
152
+ // 4. Execute agent
153
+ const result = await agent.execute(validatedInput);
154
+
155
+ // 5. Validate output
156
+ const validatedOutput = outputSchema.parse(result);
157
+
158
+ // 6. Return in runtime-specific format
159
+ return formatOutput(validatedOutput);
160
+ }
161
+ );
162
+
163
+ // 7. Return tool with metadata
164
+ return {
165
+ tool,
166
+ metadata: {
167
+ name: manifest.name,
168
+ description: manifest.description,
169
+ version: manifest.version,
170
+ archetype: 'pure-function',
171
+ },
172
+ };
173
+ }
174
+ ```
175
+
176
+ ### LLM Analyzer Adapter Pattern
177
+
178
+ ```typescript
179
+ // src/adapters/llm-analyzer.ts
180
+ export function convertLLMAnalyzerToTool<TInput, TOutput>(
181
+ agent: Agent<TInput, TOutput>,
182
+ inputSchema: z.ZodType<TInput>,
183
+ outputSchema: z.ZodType<TOutput>,
184
+ llmConfig: RuntimeLLMConfig,
185
+ ) {
186
+ // 1. Create LLM client
187
+ const client = createLLMClient(llmConfig);
188
+
189
+ // 2. Create callLLM function
190
+ const callLLM = async (prompt: string): Promise<string> => {
191
+ const response = await client.generate({ prompt, ...llmConfig });
192
+ return response.text;
193
+ };
194
+
195
+ // 3. Create tool that provides context
196
+ const tool = createRuntimeTool(
197
+ agent.manifest.name,
198
+ agent.manifest.description,
199
+ inputSchema,
200
+ async (input: TInput) => {
201
+ const context = {
202
+ mockable: false,
203
+ model: llmConfig.model,
204
+ temperature: llmConfig.temperature,
205
+ callLLM,
206
+ };
207
+
208
+ const result = await agent.execute(input, context);
209
+ return outputSchema.parse(result);
210
+ }
211
+ );
212
+
213
+ return { tool, metadata: { ... } };
214
+ }
215
+ ```
216
+
217
+ ### Common Helpers
218
+
219
+ Extract shared logic to `common-helpers.ts`:
220
+
221
+ ```typescript
222
+ // src/adapters/common-helpers.ts
223
+
224
+ /**
225
+ * Creates metadata object for single agent conversion
226
+ */
227
+ export function createSingleToolMetadata(
228
+ manifest: { name: string; description: string; version: string },
229
+ archetype: string,
230
+ ) {
231
+ return {
232
+ name: manifest.name,
233
+ description: manifest.description,
234
+ version: manifest.version,
235
+ archetype,
236
+ toolName: `${manifest.name}`,
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Creates metadata object for batch conversion
242
+ */
243
+ export function createBatchToolMetadata(
244
+ key: string,
245
+ manifest: { name: string; description: string; version: string },
246
+ archetype: string,
247
+ ) {
248
+ return {
249
+ name: manifest.name,
250
+ description: manifest.description,
251
+ version: manifest.version,
252
+ archetype,
253
+ toolName: key,
254
+ };
255
+ }
256
+ ```
257
+
258
+ ## 3. Testing with Shared Factories
259
+
260
+ **CRITICAL: Use shared test factories from dev-tools to maintain zero code duplication.**
261
+
262
+ ### Pure Function Tests
263
+
264
+ ```typescript
265
+ // test/pure-function.test.ts
266
+ import { createPureFunctionTestSuite } from '@vibe-agent-toolkit/dev-tools';
267
+ import { HaikuSchema, HaikuValidationResultSchema, haikuValidatorAgent } from '@vibe-agent-toolkit/vat-example-cat-agents';
268
+
269
+ import { convertPureFunctionToTool, convertPureFunctionsToTools } from '../src/adapters/pure-function.js';
270
+
271
+ // Generate complete test suite using factory
272
+ createPureFunctionTestSuite({
273
+ runtimeName: 'Your Runtime Name',
274
+ convertPureFunctionToTool,
275
+ convertPureFunctionsToTools: (configs) => {
276
+ // Convert and return tools/functions
277
+ const result = convertPureFunctionsToTools(configs);
278
+ return extractExecutableFunctions(result);
279
+ },
280
+ agent: haikuValidatorAgent,
281
+ inputSchema: HaikuSchema,
282
+ outputSchema: HaikuValidationResultSchema,
283
+ getToolFromResult: (result) => result.tool,
284
+ executeFunction: async (result, input) => {
285
+ // Execute the tool/function
286
+ return await result.tool.execute(input);
287
+ },
288
+ parseOutput: (output) => output,
289
+ assertToolStructure: (result) => {
290
+ expect(result.tool).toBeDefined();
291
+ expect(result.metadata.name).toBe('haiku-validator');
292
+ },
293
+ });
294
+ ```
295
+
296
+ ### LLM Analyzer Tests
297
+
298
+ ```typescript
299
+ // test/llm-analyzer.test.ts
300
+ import { createLLMAnalyzerTestSuite } from '@vibe-agent-toolkit/dev-tools';
301
+ import { nameGeneratorAgent, NameGeneratorInputSchema, NameSuggestionSchema } from '@vibe-agent-toolkit/vat-example-cat-agents';
302
+
303
+ import { convertLLMAnalyzerToTool, convertLLMAnalyzersToTools } from '../src/adapters/llm-analyzer.js';
304
+
305
+ createLLMAnalyzerTestSuite({
306
+ runtimeName: 'Your Runtime Name',
307
+ convertLLMAnalyzerToFunction: (agent, inputSchema, outputSchema, llmConfig) => {
308
+ const { tool } = convertLLMAnalyzerToTool(agent, inputSchema, outputSchema, llmConfig);
309
+ return (input) => tool.execute(input);
310
+ },
311
+ convertLLMAnalyzersToFunctions: (configs, llmConfig) => {
312
+ const { tools } = convertLLMAnalyzersToTools(configs, llmConfig);
313
+ return convertToolsToFunctions(tools);
314
+ },
315
+ agent: nameGeneratorAgent,
316
+ inputSchema: NameGeneratorInputSchema,
317
+ outputSchema: NameSuggestionSchema,
318
+ llmConfig: {
319
+ apiKey: 'test-key',
320
+ model: 'test-model',
321
+ },
322
+ });
323
+ ```
324
+
325
+ ### Test Helpers (Extract Early!)
326
+
327
+ **Rule: Extract test helpers after 2-3 similar tests to avoid duplication.**
328
+
329
+ ```typescript
330
+ // test/test-helpers.ts
331
+
332
+ /**
333
+ * Executes a runtime tool and returns the result
334
+ */
335
+ export async function executeToolHandler(
336
+ handler: (input: unknown) => Promise<unknown>,
337
+ input: unknown,
338
+ ): Promise<unknown> {
339
+ const response = await handler(input);
340
+ return parseToolResponse(response);
341
+ }
342
+
343
+ /**
344
+ * Creates an executor function for a single tool
345
+ */
346
+ export function createToolExecutor(
347
+ tool: RuntimeTool,
348
+ ): (input: unknown) => Promise<unknown> {
349
+ return async (input: unknown) => {
350
+ return await executeToolHandler(tool.handler, input);
351
+ };
352
+ }
353
+
354
+ /**
355
+ * Creates executor functions for multiple tools
356
+ */
357
+ export function createBatchToolExecutors(
358
+ tools: Record<string, RuntimeTool>,
359
+ ): Record<string, (input: unknown) => Promise<unknown>> {
360
+ const executors: Record<string, (input: unknown) => Promise<unknown>> = {};
361
+
362
+ for (const [key, tool] of Object.entries(tools)) {
363
+ executors[key] = createToolExecutor(tool);
364
+ }
365
+
366
+ return executors;
367
+ }
368
+ ```
369
+
370
+ ## 4. Demo with Common Infrastructure
371
+
372
+ **CRITICAL: Use shared common-demo infrastructure to avoid duplicating demo code.**
373
+
374
+ ```typescript
375
+ // examples/demo.ts
376
+ import { convertLLMAnalyzerToTool } from '../src/adapters/llm-analyzer.js';
377
+ import { convertPureFunctionToTool, convertPureFunctionsToTools } from '../src/adapters/pure-function.js';
378
+
379
+ import type { RuntimeAdapter } from '../../runtime-vercel-ai-sdk/examples/common-demo.js';
380
+ import { runCommonDemo } from '../../runtime-vercel-ai-sdk/examples/common-demo.js';
381
+
382
+ /**
383
+ * Your Runtime Adapter implementation
384
+ */
385
+ const yourRuntimeAdapter: RuntimeAdapter = {
386
+ name: 'Your Runtime Name',
387
+
388
+ convertPureFunctionToTool: convertPureFunctionToTool as unknown as RuntimeAdapter['convertPureFunctionToTool'],
389
+
390
+ convertPureFunctionsToTools: convertPureFunctionsToTools as unknown as RuntimeAdapter['convertPureFunctionsToTools'],
391
+
392
+ convertLLMAnalyzerToFunction: (agent, inputSchema, outputSchema, llmConfig) => {
393
+ const { tool } = convertLLMAnalyzerToTool(agent, inputSchema, outputSchema, llmConfig);
394
+ return (input) => tool.execute(input);
395
+ },
396
+
397
+ createPrimaryLLMConfig: () => ({
398
+ provider: 'primary-provider',
399
+ apiKey: process.env['PRIMARY_API_KEY'],
400
+ model: 'primary-model',
401
+ temperature: 0.9,
402
+ }),
403
+
404
+ // Optional: Add secondary provider for comparison
405
+ createSecondaryLLMConfig: () => ({
406
+ provider: 'secondary-provider',
407
+ apiKey: process.env['SECONDARY_API_KEY'],
408
+ model: 'secondary-model',
409
+ temperature: 0.9,
410
+ }),
411
+
412
+ // Optional: Add tool calling demo if your runtime supports it
413
+ demoToolCalling: async (tool, prompt) => {
414
+ const result = await yourRuntime.callWithTools({
415
+ tools: [tool],
416
+ prompt,
417
+ });
418
+ return {
419
+ text: result.text,
420
+ toolCalls: result.toolCalls,
421
+ toolResults: result.toolResults,
422
+ };
423
+ },
424
+ };
425
+
426
+ // Run the common demo with your runtime adapter
427
+ runCommonDemo(yourRuntimeAdapter).catch(console.error);
428
+ ```
429
+
430
+ **Demo benefits:**
431
+ - ✅ No duplicated demo code across runtimes
432
+ - ✅ Consistent demo experience
433
+ - ✅ Automatic multi-provider support
434
+ - ✅ Tool calling demo (if supported)
435
+ - ✅ Formatted output with colors
436
+
437
+ ## 5. Best Practices
438
+
439
+ ### Zero Code Duplication
440
+
441
+ **CRITICAL: Maintain zero code duplication across all tests.**
442
+
443
+ **When to extract helpers:**
444
+ 1. After writing 2-3 similar test blocks
445
+ 2. When you copy-paste test setup code
446
+ 3. When you see similar assertion patterns
447
+
448
+ **Example - Before extraction:**
449
+ ```typescript
450
+ // ❌ BAD: Duplicated across 3 test files
451
+ describe('Pure Function Tests', () => {
452
+ beforeEach(async () => {
453
+ tempDir = await mkdtemp(join(tmpdir(), 'test-'));
454
+ // ... 8 more lines of setup
455
+ });
456
+
457
+ afterEach(async () => {
458
+ await rm(tempDir, { recursive: true, force: true });
459
+ // ... cleanup logic
460
+ });
461
+ });
462
+ ```
463
+
464
+ **Example - After extraction:**
465
+ ```typescript
466
+ // ✅ GOOD: Shared helper
467
+ const suite = setupTestSuite('my-test-');
468
+
469
+ describe('Pure Function Tests', () => {
470
+ beforeEach(suite.beforeEach);
471
+ afterEach(suite.afterEach);
472
+
473
+ it('should work', () => {
474
+ // Use suite.tempDir, suite.registry, etc.
475
+ });
476
+ });
477
+ ```
478
+
479
+ ### TypeScript Best Practices
480
+
481
+ 1. **Always use `composite: true`** in tsconfig.json
482
+ 2. **Always add project references** for dependencies
483
+ 3. **Use `workspace:*`** for internal dependencies in package.json
484
+ 4. **Export types** from `types.ts`, not inline
485
+ 5. **Use Zod schemas** for runtime validation
486
+
487
+ ### Testing Best Practices
488
+
489
+ 1. **Use shared test factories** from dev-tools
490
+ 2. **Extract helpers early** (2-3 rule)
491
+ 3. **Test both single and batch conversions**
492
+ 4. **Test with real agent examples** from vat-example-cat-agents
493
+ 5. **Run `bun run duplication-check`** before committing
494
+
495
+ ### Adapter Design Principles
496
+
497
+ 1. **Keep it synchronous if possible** - async adapters complicate testing
498
+ 2. **Validate inputs and outputs** - use Zod schemas
499
+ 3. **Extract common logic** to common-helpers.ts
500
+ 4. **Support batch conversion** - multiple agents in one call
501
+ 5. **Return structured metadata** - name, version, archetype, etc.
502
+
503
+ ## 6. Validation Checklist
504
+
505
+ Before committing your new runtime adapter:
506
+
507
+ ```bash
508
+ # Build
509
+ bun run build
510
+
511
+ # Type check
512
+ bun run typecheck
513
+
514
+ # Lint (must pass with zero warnings)
515
+ bun run lint
516
+
517
+ # Test
518
+ bun run test:unit
519
+
520
+ # Check for duplication (MUST be zero!)
521
+ bun run duplication-check
522
+
523
+ # Run demo
524
+ cd packages/runtime-{name}
525
+ bun run demo
526
+
527
+ # Full validation
528
+ vv validate
529
+ ```
530
+
531
+ **CRITICAL: `duplication-check` must pass with 0 clones before committing.**
532
+
533
+ ## 7. Common Pitfalls
534
+
535
+ ### ❌ Don't: Duplicate demo code
536
+ ```typescript
537
+ // BAD: Don't create custom demo from scratch
538
+ console.log('Testing pure functions...');
539
+ const result = await agent.execute(input);
540
+ console.log(`Result: ${JSON.stringify(result)}`);
541
+ ```
542
+
543
+ ### ✅ Do: Use common demo infrastructure
544
+ ```typescript
545
+ // GOOD: Use RuntimeAdapter interface
546
+ const adapter: RuntimeAdapter = {
547
+ name: 'Your Runtime',
548
+ // ... implement interface
549
+ };
550
+
551
+ runCommonDemo(adapter);
552
+ ```
553
+
554
+ ### ❌ Don't: Duplicate test setup
555
+ ```typescript
556
+ // BAD: Repeated in multiple test files
557
+ beforeEach(async () => {
558
+ tempDir = await mkdtemp(join(tmpdir(), 'test-'));
559
+ // ... more setup
560
+ });
561
+ ```
562
+
563
+ ### ✅ Do: Extract to test helper
564
+ ```typescript
565
+ // GOOD: Shared setup function
566
+ const suite = setupTestSuite('my-test-');
567
+ beforeEach(suite.beforeEach);
568
+ afterEach(suite.afterEach);
569
+ ```
570
+
571
+ ### ❌ Don't: Create async adapters unless necessary
572
+ ```typescript
573
+ // BAD: Unnecessary async
574
+ export async function convertPureFunctionToTool(...) {
575
+ // Makes testing harder, breaks interface compatibility
576
+ }
577
+ ```
578
+
579
+ ### ✅ Do: Keep adapters synchronous when possible
580
+ ```typescript
581
+ // GOOD: Synchronous adapter
582
+ export function convertPureFunctionToTool(...) {
583
+ // Simpler testing, better compatibility
584
+ }
585
+ ```
586
+
587
+ ## 8. Examples to Reference
588
+
589
+ **Best Practice Implementations:**
590
+
591
+ 1. **Claude Agent SDK** (`packages/runtime-claude-agent-sdk/`)
592
+ - Clean synchronous adapter (Anthropic-only)
593
+ - Multi-provider demo (Anthropic + OpenAI wrapper)
594
+ - Zero test duplication with helpers
595
+ - Uses shared test factories
596
+
597
+ 2. **Vercel AI SDK** (`packages/runtime-vercel-ai-sdk/`)
598
+ - Multi-provider support (OpenAI + Anthropic)
599
+ - Tool calling demo
600
+ - Common demo infrastructure
601
+ - Shared test factories
602
+
603
+ 3. **LangChain** (`packages/runtime-langchain/`)
604
+ - LangChain tool format
605
+ - Uses common demo
606
+ - Shared test factories
607
+
608
+ ## 9. Getting Help
609
+
610
+ - Review existing runtime packages for patterns
611
+ - Check dev-tools test factories for test infrastructure
612
+ - See common-demo.ts for demo infrastructure
613
+ - Run `bun run duplication-check` frequently
614
+ - Ask questions early rather than duplicating code
615
+
616
+ ## Summary
617
+
618
+ **Key Takeaways:**
619
+ 1. ✅ Use shared test factories from dev-tools (zero duplication)
620
+ 2. ✅ Use common-demo infrastructure for demos
621
+ 3. ✅ Extract test helpers after 2-3 similar tests
622
+ 4. ✅ Keep adapters synchronous when possible
623
+ 5. ✅ Always run duplication-check (must be 0 clones)
624
+ 6. ✅ Use `workspace:*` for internal dependencies
625
+ 7. ✅ Add project references to tsconfig.json
626
+ 8. ✅ Support both single and batch conversion
627
+
628
+ **This guide will evolve as we learn more. PRs welcome!**