wraptc 1.0.2 → 1.0.4

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 (71) hide show
  1. package/bin/wraptc +4 -4
  2. package/package.json +2 -2
  3. package/src/cli/__tests__/cli.test.ts +337 -0
  4. package/src/cli/index.ts +149 -0
  5. package/src/core/__tests__/fixtures/configs/project-config.json +14 -0
  6. package/src/core/__tests__/fixtures/configs/system-config.json +14 -0
  7. package/src/core/__tests__/fixtures/configs/user-config.json +15 -0
  8. package/src/core/__tests__/integration/integration.test.ts +241 -0
  9. package/src/core/__tests__/integration/mock-coder-adapter.test.ts +243 -0
  10. package/src/core/__tests__/test-utils.ts +136 -0
  11. package/src/core/__tests__/unit/adapters/runner.test.ts +302 -0
  12. package/src/core/__tests__/unit/basic-test.test.ts +44 -0
  13. package/src/core/__tests__/unit/basic.test.ts +12 -0
  14. package/src/core/__tests__/unit/config.test.ts +244 -0
  15. package/src/core/__tests__/unit/error-patterns.test.ts +181 -0
  16. package/src/core/__tests__/unit/memory-monitor.test.ts +354 -0
  17. package/src/core/__tests__/unit/plugin/registry.test.ts +356 -0
  18. package/src/core/__tests__/unit/providers/codex.test.ts +173 -0
  19. package/src/core/__tests__/unit/providers/configurable.test.ts +429 -0
  20. package/src/core/__tests__/unit/providers/gemini.test.ts +251 -0
  21. package/src/core/__tests__/unit/providers/opencode.test.ts +258 -0
  22. package/src/core/__tests__/unit/providers/qwen-code.test.ts +195 -0
  23. package/src/core/__tests__/unit/providers/simple-codex.test.ts +18 -0
  24. package/src/core/__tests__/unit/router.test.ts +967 -0
  25. package/src/core/__tests__/unit/state.test.ts +1079 -0
  26. package/src/core/__tests__/unit/unified/capabilities.test.ts +186 -0
  27. package/src/core/__tests__/unit/wrap-terminalcoder.test.ts +32 -0
  28. package/src/core/adapters/builtin/codex.ts +35 -0
  29. package/src/core/adapters/builtin/gemini.ts +34 -0
  30. package/src/core/adapters/builtin/index.ts +31 -0
  31. package/src/core/adapters/builtin/mock-coder.ts +148 -0
  32. package/src/core/adapters/builtin/qwen.ts +34 -0
  33. package/src/core/adapters/define.ts +48 -0
  34. package/src/core/adapters/index.ts +43 -0
  35. package/src/core/adapters/loader.ts +143 -0
  36. package/src/core/adapters/provider-bridge.ts +190 -0
  37. package/src/core/adapters/runner.ts +437 -0
  38. package/src/core/adapters/types.ts +172 -0
  39. package/src/core/config.ts +290 -0
  40. package/src/core/define-provider.ts +212 -0
  41. package/src/core/error-patterns.ts +147 -0
  42. package/src/core/index.ts +130 -0
  43. package/src/core/memory-monitor.ts +171 -0
  44. package/src/core/plugin/builtin.ts +87 -0
  45. package/src/core/plugin/index.ts +34 -0
  46. package/src/core/plugin/registry.ts +350 -0
  47. package/src/core/plugin/types.ts +209 -0
  48. package/src/core/provider-factory.ts +397 -0
  49. package/src/core/provider-loader.ts +171 -0
  50. package/src/core/providers/codex.ts +56 -0
  51. package/src/core/providers/configurable.ts +637 -0
  52. package/src/core/providers/custom.ts +261 -0
  53. package/src/core/providers/gemini.ts +41 -0
  54. package/src/core/providers/index.ts +383 -0
  55. package/src/core/providers/opencode.ts +168 -0
  56. package/src/core/providers/qwen-code.ts +41 -0
  57. package/src/core/router.ts +370 -0
  58. package/src/core/state.ts +258 -0
  59. package/src/core/types.ts +206 -0
  60. package/src/core/unified/capabilities.ts +184 -0
  61. package/src/core/unified/errors.ts +141 -0
  62. package/src/core/unified/index.ts +29 -0
  63. package/src/core/unified/output.ts +189 -0
  64. package/src/core/wrap-terminalcoder.ts +245 -0
  65. package/src/mcp/__tests__/server.test.ts +295 -0
  66. package/src/mcp/server.ts +284 -0
  67. package/src/test-fixtures/mock-coder.sh +194 -0
  68. package/dist/cli/index.js +0 -16501
  69. package/dist/core/index.js +0 -7531
  70. package/dist/mcp/server.js +0 -14568
  71. package/dist/wraptc-1.0.2.tgz +0 -0
@@ -0,0 +1,437 @@
1
+ /**
2
+ * AdapterRunner - Core execution engine for adapters
3
+ *
4
+ * Handles process execution, input/output handling, streaming, and error classification.
5
+ */
6
+
7
+ import { DEFAULT_ERROR_PATTERNS } from "../define-provider";
8
+ import type { ProviderErrorKind, TokenUsage } from "../types";
9
+ import type {
10
+ AdapterDefinition,
11
+ AdapterInvokeOptions,
12
+ AdapterResult,
13
+ InputMethod,
14
+ OutputFormat,
15
+ StreamChunk,
16
+ } from "./types";
17
+
18
+ /**
19
+ * AdapterRunner executes adapter definitions against CLI tools
20
+ */
21
+ export class AdapterRunner {
22
+ private def: AdapterDefinition;
23
+
24
+ constructor(definition: AdapterDefinition) {
25
+ this.def = definition;
26
+ }
27
+
28
+ /**
29
+ * Run the adapter and return the result
30
+ */
31
+ async run(prompt: string, opts: AdapterInvokeOptions = {}): Promise<AdapterResult> {
32
+ const args = this.buildArgs(prompt, opts);
33
+ const stdin = this.getStdin(prompt);
34
+ const timeout = opts.timeoutMs ?? this.def.timeoutMs ?? 60000;
35
+
36
+ // Set up timeout handling
37
+ const controller = new AbortController();
38
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
39
+
40
+ try {
41
+ // Build command array
42
+ const command = [this.def.binary];
43
+ if (this.def.subcommand) {
44
+ command.push(this.def.subcommand);
45
+ }
46
+ command.push(...args);
47
+
48
+ // Spawn the process
49
+ const proc = Bun.spawn(command, {
50
+ cwd: opts.cwd,
51
+ env: { ...process.env, ...this.def.env, ...opts.env },
52
+ stdin: stdin !== undefined ? "pipe" : "ignore",
53
+ stdout: "pipe",
54
+ stderr: "pipe",
55
+ });
56
+
57
+ // Write stdin if provided
58
+ if (stdin !== undefined && proc.stdin) {
59
+ proc.stdin.write(stdin);
60
+ proc.stdin.end();
61
+ }
62
+
63
+ // Handle abort signals
64
+ const combinedSignal =
65
+ opts.signal && controller.signal
66
+ ? AbortSignal.any([opts.signal, controller.signal])
67
+ : (opts.signal ?? controller.signal);
68
+
69
+ combinedSignal.addEventListener("abort", () => proc.kill());
70
+
71
+ // Read stdout and stderr
72
+ const stdout = await new Response(proc.stdout).text();
73
+ const stderr = await new Response(proc.stderr).text();
74
+ const exitCode = await proc.exited;
75
+
76
+ // Check for errors
77
+ const allowed = this.def.allowedExitCodes ?? [0];
78
+ if (!allowed.includes(exitCode ?? -1)) {
79
+ const kind = this.classifyError(stderr, stdout, exitCode);
80
+ const error = new AdapterError(
81
+ `${this.def.displayName ?? this.def.id} failed with exit code ${exitCode}: ${stderr || stdout}`,
82
+ kind,
83
+ this.def.id,
84
+ { stderr, stdout, exitCode },
85
+ );
86
+ throw error;
87
+ }
88
+
89
+ return this.parseOutput(stdout);
90
+ } finally {
91
+ clearTimeout(timeoutId);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Run the adapter with streaming output
97
+ */
98
+ async *runStream(prompt: string, opts: AdapterInvokeOptions = {}): AsyncGenerator<StreamChunk> {
99
+ const requestId = crypto.randomUUID();
100
+ yield { type: "start", requestId };
101
+
102
+ const args = this.buildArgs(prompt, opts);
103
+ const stdin = this.getStdin(prompt);
104
+ const timeout = opts.timeoutMs ?? this.def.timeoutMs ?? 60000;
105
+
106
+ const controller = new AbortController();
107
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
108
+
109
+ try {
110
+ // Build command array
111
+ const command = [this.def.binary];
112
+ if (this.def.subcommand) {
113
+ command.push(this.def.subcommand);
114
+ }
115
+ command.push(...args);
116
+
117
+ const proc = Bun.spawn(command, {
118
+ cwd: opts.cwd,
119
+ env: { ...process.env, ...this.def.env, ...opts.env },
120
+ stdin: stdin !== undefined ? "pipe" : "ignore",
121
+ stdout: "pipe",
122
+ stderr: "pipe",
123
+ });
124
+
125
+ if (stdin !== undefined && proc.stdin) {
126
+ proc.stdin.write(stdin);
127
+ proc.stdin.end();
128
+ }
129
+
130
+ const combinedSignal =
131
+ opts.signal && controller.signal
132
+ ? AbortSignal.any([opts.signal, controller.signal])
133
+ : (opts.signal ?? controller.signal);
134
+
135
+ combinedSignal.addEventListener("abort", () => proc.kill());
136
+
137
+ // Stream stdout
138
+ const fullTextChunks: string[] = [];
139
+ const streamingMode = this.def.streaming ?? "none";
140
+
141
+ if (proc.stdout) {
142
+ const reader = proc.stdout.getReader();
143
+ const decoder = new TextDecoder();
144
+ let buffer = "";
145
+
146
+ try {
147
+ while (true) {
148
+ const { done, value } = await reader.read();
149
+ if (done) break;
150
+
151
+ const text = decoder.decode(value, { stream: true });
152
+ fullTextChunks.push(text);
153
+
154
+ if (streamingMode === "none") {
155
+ // No streaming - just accumulate
156
+ continue;
157
+ }
158
+
159
+ if (streamingMode === "line" || streamingMode === "jsonl") {
160
+ // Line-based streaming
161
+ buffer += text;
162
+ const lines = buffer.split("\n");
163
+ buffer = lines.pop() ?? ""; // Keep incomplete line in buffer
164
+
165
+ for (const line of lines) {
166
+ if (!line.trim()) continue;
167
+ const chunk = this.parseStreamLine(line);
168
+ if (chunk) yield chunk;
169
+ }
170
+ }
171
+ }
172
+
173
+ // Handle remaining buffer
174
+ if (buffer.trim() && streamingMode !== "none") {
175
+ const chunk = this.parseStreamLine(buffer);
176
+ if (chunk) yield chunk;
177
+ }
178
+ } finally {
179
+ reader.releaseLock();
180
+ }
181
+ }
182
+
183
+ // Collect stderr
184
+ const stderr = await new Response(proc.stderr).text();
185
+ const exitCode = await proc.exited;
186
+
187
+ // Check for errors
188
+ const allowed = this.def.allowedExitCodes ?? [0];
189
+ if (!allowed.includes(exitCode ?? -1)) {
190
+ const kind = this.classifyError(stderr, fullTextChunks.join(""), exitCode);
191
+ yield {
192
+ type: "error",
193
+ message: stderr || `Exit code ${exitCode}`,
194
+ kind,
195
+ };
196
+ return;
197
+ }
198
+
199
+ // Emit complete event
200
+ const fullText = fullTextChunks.join("");
201
+ const result = this.parseOutput(fullText);
202
+ yield { type: "complete", text: result.text, usage: result.usage };
203
+ } finally {
204
+ clearTimeout(timeoutId);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Build command line arguments
210
+ */
211
+ private buildArgs(prompt: string, opts: AdapterInvokeOptions): string[] {
212
+ // Use custom hook if provided
213
+ if (this.def.buildArgs) {
214
+ return this.def.buildArgs(prompt, opts);
215
+ }
216
+
217
+ const args: string[] = [];
218
+
219
+ // Add static args
220
+ if (this.def.args) {
221
+ args.push(...this.def.args);
222
+ }
223
+
224
+ // Add prompt based on input method
225
+ const input = this.def.input ?? "stdin";
226
+
227
+ if (input === "positional") {
228
+ args.push(prompt);
229
+ } else if (typeof input === "object" && input.flag) {
230
+ args.push(input.flag, prompt);
231
+ }
232
+ // stdin: handled separately via getStdin
233
+
234
+ return args;
235
+ }
236
+
237
+ /**
238
+ * Get stdin input for the process
239
+ */
240
+ private getStdin(prompt: string): string | undefined {
241
+ // Use custom hook if provided
242
+ if (this.def.getStdin) {
243
+ return this.def.getStdin(prompt);
244
+ }
245
+
246
+ const input = this.def.input ?? "stdin";
247
+ return input === "stdin" ? prompt : undefined;
248
+ }
249
+
250
+ /**
251
+ * Parse output from the process
252
+ */
253
+ private parseOutput(stdout: string): AdapterResult {
254
+ // Use custom hook if provided
255
+ if (this.def.parseOutput) {
256
+ return this.def.parseOutput(stdout);
257
+ }
258
+
259
+ const output = this.def.output ?? "text";
260
+
261
+ if (output === "text") {
262
+ return { text: stdout.trim() };
263
+ }
264
+
265
+ if (output === "json") {
266
+ return this.parseJsonOutput(stdout);
267
+ }
268
+
269
+ // jsonPath extraction
270
+ if (typeof output === "object" && output.jsonPath) {
271
+ try {
272
+ const parsed = JSON.parse(stdout);
273
+ const text = this.getJsonPath(parsed, output.jsonPath);
274
+ return { text: String(text ?? stdout.trim()) };
275
+ } catch {
276
+ return { text: stdout.trim() };
277
+ }
278
+ }
279
+
280
+ return { text: stdout.trim() };
281
+ }
282
+
283
+ /**
284
+ * Parse JSON output with common field extraction
285
+ */
286
+ private parseJsonOutput(stdout: string): AdapterResult {
287
+ try {
288
+ const parsed = JSON.parse(stdout);
289
+
290
+ // Try common text fields
291
+ const textFields = ["text", "response", "output", "content", "result", "message"];
292
+ let text = stdout.trim();
293
+
294
+ for (const field of textFields) {
295
+ if (typeof parsed[field] === "string") {
296
+ text = parsed[field];
297
+ break;
298
+ }
299
+ }
300
+
301
+ // Extract usage if present
302
+ const usage = parsed.usage as TokenUsage | undefined;
303
+
304
+ return { text, usage };
305
+ } catch {
306
+ return { text: stdout.trim() };
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Parse a streaming line
312
+ */
313
+ private parseStreamLine(line: string): StreamChunk | null {
314
+ // Use custom hook if provided
315
+ if (this.def.parseStreamChunk) {
316
+ return this.def.parseStreamChunk(line);
317
+ }
318
+
319
+ const streamingMode = this.def.streaming ?? "line";
320
+
321
+ if (streamingMode === "jsonl") {
322
+ try {
323
+ const parsed = JSON.parse(line);
324
+ return { type: "json", data: parsed };
325
+ } catch {
326
+ return { type: "text", content: line };
327
+ }
328
+ }
329
+
330
+ return { type: "text", content: line };
331
+ }
332
+
333
+ /**
334
+ * Classify an error based on stderr/stdout content
335
+ */
336
+ private classifyError(
337
+ stderr: string,
338
+ stdout: string,
339
+ exitCode: number | null,
340
+ ): ProviderErrorKind {
341
+ // Use custom hook if provided
342
+ if (this.def.classifyError) {
343
+ return this.def.classifyError(stderr, stdout, exitCode);
344
+ }
345
+
346
+ const combined = (stderr + stdout).toLowerCase();
347
+
348
+ // Check adapter-specific patterns first
349
+ if (this.def.errorPatterns) {
350
+ for (const [kind, patterns] of Object.entries(this.def.errorPatterns)) {
351
+ if (patterns?.some((p) => combined.includes(p.toLowerCase()))) {
352
+ return kind as ProviderErrorKind;
353
+ }
354
+ }
355
+ }
356
+
357
+ // Fall back to default patterns
358
+ for (const [kind, patterns] of Object.entries(DEFAULT_ERROR_PATTERNS)) {
359
+ if (patterns.some((p) => combined.includes(p.toLowerCase()))) {
360
+ return kind as ProviderErrorKind;
361
+ }
362
+ }
363
+
364
+ return "TRANSIENT";
365
+ }
366
+
367
+ /**
368
+ * Get a nested value from an object by path
369
+ */
370
+ private getJsonPath(obj: unknown, path: string): unknown {
371
+ return path.split(".").reduce((o, k) => {
372
+ if (o === null || o === undefined) return undefined;
373
+ return (o as Record<string, unknown>)[k];
374
+ }, obj);
375
+ }
376
+
377
+ /**
378
+ * Get adapter info
379
+ */
380
+ getInfo() {
381
+ return {
382
+ id: this.def.id,
383
+ displayName: this.def.displayName ?? this.def.id,
384
+ description: this.def.description,
385
+ version: this.def.version,
386
+ binary: this.def.binary,
387
+ capabilities: this.def.capabilities ?? ["generate"],
388
+ supportsStreaming: (this.def.streaming ?? "none") !== "none",
389
+ };
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Custom error class for adapter failures
395
+ */
396
+ export class AdapterError extends Error {
397
+ readonly kind: ProviderErrorKind;
398
+ readonly adapterId: string;
399
+ readonly context: {
400
+ stderr?: string;
401
+ stdout?: string;
402
+ exitCode?: number | null;
403
+ };
404
+
405
+ constructor(
406
+ message: string,
407
+ kind: ProviderErrorKind,
408
+ adapterId: string,
409
+ context: { stderr?: string; stdout?: string; exitCode?: number | null },
410
+ ) {
411
+ super(message);
412
+ this.name = "AdapterError";
413
+ this.kind = kind;
414
+ this.adapterId = adapterId;
415
+ this.context = context;
416
+ }
417
+
418
+ get isRetryable(): boolean {
419
+ return ["TRANSIENT", "RATE_LIMIT", "TIMEOUT"].includes(this.kind);
420
+ }
421
+
422
+ get isUserError(): boolean {
423
+ return ["BAD_REQUEST", "UNAUTHORIZED", "CONTEXT_LENGTH", "CONTENT_FILTER"].includes(this.kind);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Test helper interface that exposes private methods for testing.
429
+ * Only use this in test files.
430
+ */
431
+ export interface AdapterRunnerTestHelper extends AdapterRunner {
432
+ buildArgs(prompt: string, opts: AdapterInvokeOptions): string[];
433
+ getStdin(prompt: string): string | undefined;
434
+ parseOutput(stdout: string): AdapterResult;
435
+ parseJsonOutput(stdout: string): AdapterResult;
436
+ parseStreamLine(line: string): StreamChunk | null;
437
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Adapter Types - Lightweight configuration for terminal coding tools
3
+ *
4
+ * Designed to be simpler than defineProvider while maintaining full functionality.
5
+ * Most adapters should be < 20 lines of TypeScript.
6
+ */
7
+
8
+ import type { ProviderErrorKind, TokenUsage } from "../types";
9
+
10
+ /**
11
+ * How to pass the prompt to the CLI tool
12
+ */
13
+ export type InputMethod =
14
+ | "stdin" // Pipe prompt to stdin (default)
15
+ | "positional" // Pass as positional argument
16
+ | { flag: string }; // Pass with flag: -p "<prompt>"
17
+
18
+ /**
19
+ * How to parse CLI output
20
+ */
21
+ export type OutputFormat =
22
+ | "text" // Raw stdout as response (default)
23
+ | "json" // Parse JSON, extract text from common fields
24
+ | { jsonPath: string }; // Extract from specific JSON path
25
+
26
+ /**
27
+ * Streaming mode for real-time output
28
+ */
29
+ export type StreamingMode =
30
+ | "none" // No streaming (default)
31
+ | "line" // Emit each line as it arrives
32
+ | "jsonl"; // Parse JSON lines
33
+
34
+ /**
35
+ * Invoke options passed to adapter execution
36
+ */
37
+ export interface AdapterInvokeOptions {
38
+ signal?: AbortSignal;
39
+ cwd?: string;
40
+ env?: Record<string, string>;
41
+ timeoutMs?: number;
42
+ }
43
+
44
+ /**
45
+ * Streaming chunk types
46
+ */
47
+ export type StreamChunk =
48
+ | { type: "start"; requestId: string }
49
+ | { type: "text"; content: string }
50
+ | { type: "json"; data: unknown }
51
+ | { type: "complete"; text: string; usage?: TokenUsage }
52
+ | { type: "error"; message: string; kind: ProviderErrorKind };
53
+
54
+ /**
55
+ * Error pattern configuration
56
+ * Maps error kind to array of case-insensitive patterns to match
57
+ */
58
+ export type ErrorPatterns = Partial<Record<ProviderErrorKind, string[]>>;
59
+
60
+ /**
61
+ * Core adapter configuration
62
+ *
63
+ * Only `id` and `binary` are required - everything else has sensible defaults.
64
+ */
65
+ export interface AdapterConfig {
66
+ // Required fields
67
+ /** Unique identifier for this adapter */
68
+ id: string;
69
+ /** CLI binary name or path */
70
+ binary: string;
71
+
72
+ // Display
73
+ /** Human-readable name (defaults to id) */
74
+ displayName?: string;
75
+ /** Description of this adapter */
76
+ description?: string;
77
+ /** Version string */
78
+ version?: string;
79
+
80
+ // Input handling
81
+ /** How to pass the prompt (default: 'stdin') */
82
+ input?: InputMethod;
83
+
84
+ // Arguments
85
+ /** Static args to always include */
86
+ args?: string[];
87
+ /** Subcommand to invoke (e.g., 'exec', 'run') */
88
+ subcommand?: string;
89
+ /** Environment variables to set */
90
+ env?: Record<string, string>;
91
+
92
+ // Output handling
93
+ /** How to parse output (default: 'text') */
94
+ output?: OutputFormat;
95
+ /** Streaming mode (default: 'none') */
96
+ streaming?: StreamingMode;
97
+
98
+ // Error handling
99
+ /** Patterns to match for error classification */
100
+ errorPatterns?: ErrorPatterns;
101
+ /** Exit codes to treat as success (default: [0]) */
102
+ allowedExitCodes?: number[];
103
+
104
+ // Capabilities
105
+ /** Coding modes this adapter supports */
106
+ capabilities?: string[];
107
+
108
+ // Timeouts
109
+ /** Timeout in milliseconds (default: 60000) */
110
+ timeoutMs?: number;
111
+ }
112
+
113
+ /**
114
+ * Optional hooks for custom behavior
115
+ * Use these when declarative config isn't sufficient
116
+ */
117
+ export interface AdapterHooks {
118
+ /**
119
+ * Custom argument building logic
120
+ * @returns Array of command line arguments
121
+ */
122
+ buildArgs?: (prompt: string, options: AdapterInvokeOptions) => string[];
123
+
124
+ /**
125
+ * Custom stdin input generation
126
+ * @returns String to pipe to stdin, or undefined for no stdin
127
+ */
128
+ getStdin?: (prompt: string) => string | undefined;
129
+
130
+ /**
131
+ * Custom output parsing for non-streaming responses
132
+ */
133
+ parseOutput?: (stdout: string) => { text: string; usage?: TokenUsage };
134
+
135
+ /**
136
+ * Custom streaming chunk parsing
137
+ * @returns Parsed chunk, or null to skip the line
138
+ */
139
+ parseStreamChunk?: (line: string) => StreamChunk | null;
140
+
141
+ /**
142
+ * Custom error classification
143
+ */
144
+ classifyError?: (stderr: string, stdout: string, exitCode: number | null) => ProviderErrorKind;
145
+ }
146
+
147
+ /**
148
+ * Full adapter definition = config + optional hooks
149
+ */
150
+ export type AdapterDefinition = AdapterConfig & Partial<AdapterHooks>;
151
+
152
+ /**
153
+ * Result from running an adapter
154
+ */
155
+ export interface AdapterResult {
156
+ text: string;
157
+ usage?: TokenUsage;
158
+ }
159
+
160
+ /**
161
+ * Metadata about an adapter
162
+ */
163
+ export interface AdapterInfo {
164
+ id: string;
165
+ displayName: string;
166
+ description?: string;
167
+ version?: string;
168
+ binary: string;
169
+ capabilities: string[];
170
+ supportsStreaming: boolean;
171
+ isAvailable?: boolean;
172
+ }