@cleocode/adapters 2026.4.92 → 2026.4.94

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 (36) hide show
  1. package/dist/index.js +40795 -18064
  2. package/dist/index.js.map +4 -4
  3. package/dist/providers/claude-sdk/index.d.ts +10 -4
  4. package/dist/providers/claude-sdk/index.d.ts.map +1 -1
  5. package/dist/providers/claude-sdk/spawn.d.ts +29 -28
  6. package/dist/providers/claude-sdk/spawn.d.ts.map +1 -1
  7. package/dist/providers/openai-sdk/adapter.d.ts +18 -17
  8. package/dist/providers/openai-sdk/adapter.d.ts.map +1 -1
  9. package/dist/providers/openai-sdk/guardrails.d.ts +71 -18
  10. package/dist/providers/openai-sdk/guardrails.d.ts.map +1 -1
  11. package/dist/providers/openai-sdk/handoff.d.ts +51 -21
  12. package/dist/providers/openai-sdk/handoff.d.ts.map +1 -1
  13. package/dist/providers/openai-sdk/index.d.ts +8 -5
  14. package/dist/providers/openai-sdk/index.d.ts.map +1 -1
  15. package/dist/providers/openai-sdk/install.d.ts +1 -1
  16. package/dist/providers/openai-sdk/spawn.d.ts +54 -21
  17. package/dist/providers/openai-sdk/spawn.d.ts.map +1 -1
  18. package/dist/providers/openai-sdk/tracing.d.ts +87 -21
  19. package/dist/providers/openai-sdk/tracing.d.ts.map +1 -1
  20. package/dist/providers/shared/sdk-result-mapper.d.ts +9 -7
  21. package/dist/providers/shared/sdk-result-mapper.d.ts.map +1 -1
  22. package/package.json +6 -5
  23. package/src/__tests__/harness-interop.test.ts +451 -0
  24. package/src/providers/claude-sdk/__tests__/spawn.test.ts +100 -265
  25. package/src/providers/claude-sdk/index.ts +10 -4
  26. package/src/providers/claude-sdk/spawn.ts +69 -106
  27. package/src/providers/openai-sdk/__tests__/openai-sdk-spawn.test.ts +134 -103
  28. package/src/providers/openai-sdk/adapter.ts +19 -18
  29. package/src/providers/openai-sdk/guardrails.ts +106 -25
  30. package/src/providers/openai-sdk/handoff.ts +73 -37
  31. package/src/providers/openai-sdk/index.ts +28 -4
  32. package/src/providers/openai-sdk/install.ts +1 -1
  33. package/src/providers/openai-sdk/manifest.json +4 -4
  34. package/src/providers/openai-sdk/spawn.ts +213 -48
  35. package/src/providers/openai-sdk/tracing.ts +105 -22
  36. package/src/providers/shared/sdk-result-mapper.ts +9 -7
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Tests for the OpenAI Agents SDK spawn provider.
2
+ * Tests for the OpenAI SDK spawn provider — Vercel AI SDK edition.
3
3
  *
4
- * All OpenAI SDK calls are mocked — no real API keys or network calls
5
- * are required to run this suite.
4
+ * All LLM calls are mocked — no real API keys or network calls are required
5
+ * to run this suite.
6
6
  *
7
7
  * Test coverage:
8
8
  * - GuardrailTests: path ACL logic and guardrail builders
@@ -11,9 +11,10 @@
11
11
  * - AdapterTests: identity, capabilities, initialize, dispose, healthCheck
12
12
  * - InstallProviderTests: isInstalled, install, uninstall
13
13
  * - TraceProcessorTests: onSpanEnd event capture
14
- * - HandoffIntegrationTest: lead + worker topology with mocked runner
14
+ * - HandoffIntegrationTest: lead + worker topology with mocked generateText
15
15
  *
16
- * @task T582
16
+ * @task T582 (original)
17
+ * @task T933 (SDK consolidation — Vercel AI SDK migration)
17
18
  */
18
19
 
19
20
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@ -22,61 +23,33 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
23
  // Hoist shared state so vi.mock factories can reference it
23
24
  // ---------------------------------------------------------------------------
24
25
 
25
- const { createdAgents, mockRunState, mockFsState } = vi.hoisted(() => {
26
+ const { generateTextCalls, mockRunState, mockFsState } = vi.hoisted(() => {
26
27
  return {
27
- createdAgents: [] as Array<{ name?: string; handoffs?: unknown[]; model?: string }>,
28
- mockRunState: { result: { finalOutput: 'mock output' }, shouldThrow: false },
28
+ generateTextCalls: [] as Array<{ model: unknown; system?: string; prompt: string }>,
29
+ mockRunState: { output: 'mock output', shouldThrow: false },
29
30
  mockFsState: { exists: false, content: '' },
30
31
  };
31
32
  });
32
33
 
33
34
  // ---------------------------------------------------------------------------
34
- // Mock @openai/agents
35
+ // Mock Vercel AI SDK surface — @ai-sdk/openai + ai/generateText
35
36
  // ---------------------------------------------------------------------------
36
37
 
37
- vi.mock('@openai/agents', () => {
38
- class MockAgent {
39
- name: string;
40
- handoffs: unknown[];
41
- model: string;
42
- inputGuardrails: unknown[];
43
- instructions: string;
44
-
45
- constructor(opts: {
46
- name?: string;
47
- instructions?: string;
48
- model?: string;
49
- handoffs?: unknown[];
50
- inputGuardrails?: unknown[];
51
- }) {
52
- this.name = opts.name ?? 'agent';
53
- this.instructions = opts.instructions ?? '';
54
- this.model = opts.model ?? 'gpt-4.1';
55
- this.handoffs = opts.handoffs ?? [];
56
- this.inputGuardrails = opts.inputGuardrails ?? [];
57
- createdAgents.push({ name: this.name, handoffs: this.handoffs, model: this.model });
58
- }
59
- }
60
-
61
- const mockRunFn = async (agent: { name: string }) => {
62
- if (mockRunState.shouldThrow) throw new Error('mock SDK error');
63
- return { ...mockRunState.result, _agent: agent };
64
- };
65
-
66
- class MockRunner {
67
- run = vi.fn(mockRunFn);
68
- }
69
-
70
- class MockOpenAIProvider {}
38
+ vi.mock('@ai-sdk/openai', () => ({
39
+ createOpenAI: vi.fn((_config: { apiKey: string }) => {
40
+ return (modelId: string) => ({ __cleoMockModel: true, modelId });
41
+ }),
42
+ }));
71
43
 
72
- return {
73
- Agent: MockAgent,
74
- Runner: MockRunner,
75
- OpenAIProvider: MockOpenAIProvider,
76
- addTraceProcessor: vi.fn(),
77
- setTracingDisabled: vi.fn(),
78
- };
79
- });
44
+ vi.mock('ai', () => ({
45
+ generateText: vi.fn(
46
+ async ({ model, system, prompt }: { model: unknown; system?: string; prompt: string }) => {
47
+ generateTextCalls.push({ model, system, prompt });
48
+ if (mockRunState.shouldThrow) throw new Error('mock SDK error');
49
+ return { text: mockRunState.output };
50
+ },
51
+ ),
52
+ }));
80
53
 
81
54
  // ---------------------------------------------------------------------------
82
55
  // Mock node:fs for install provider tests
@@ -124,6 +97,7 @@ import {
124
97
  buildDefaultGuardrails,
125
98
  buildPathGuardrail,
126
99
  buildToolAllowlistGuardrail,
100
+ evaluateGuardrails,
127
101
  isPathAllowed,
128
102
  } from '../guardrails.js';
129
103
  import {
@@ -131,17 +105,18 @@ import {
131
105
  buildLeadAgent,
132
106
  buildStandaloneAgent,
133
107
  buildWorkerAgent,
108
+ type CleoAgent,
134
109
  WORKER_ARCHETYPES,
135
110
  } from '../handoff.js';
136
111
  import { OpenAiSdkInstallProvider } from '../install.js';
137
112
  import { OpenAiSdkSpawnProvider } from '../spawn.js';
138
- import { CleoConduitTraceProcessor } from '../tracing.js';
113
+ import { CleoConduitTraceProcessor, type CleoSpan } from '../tracing.js';
139
114
 
140
115
  // ---------------------------------------------------------------------------
141
116
  // Helpers
142
117
  // ---------------------------------------------------------------------------
143
118
 
144
- function makeSpanLike(overrides: Record<string, unknown> = {}): unknown {
119
+ function makeSpanLike(overrides: Partial<CleoSpan> = {}): CleoSpan {
145
120
  return {
146
121
  spanId: 'span-001',
147
122
  startedAt: new Date().toISOString(),
@@ -186,9 +161,9 @@ describe('buildPathGuardrail', () => {
186
161
  it('passes when no path fields in input', async () => {
187
162
  const guard = buildPathGuardrail(['/allowed/**']);
188
163
  const result = await guard.execute({
189
- agent: {} as never,
164
+ agent: {},
190
165
  input: 'Tell me about the project',
191
- context: {} as never,
166
+ context: {},
192
167
  });
193
168
  expect(result.tripwireTriggered).toBe(false);
194
169
  });
@@ -196,9 +171,9 @@ describe('buildPathGuardrail', () => {
196
171
  it('trips when path field violates ACL', async () => {
197
172
  const guard = buildPathGuardrail(['/allowed/**']);
198
173
  const result = await guard.execute({
199
- agent: {} as never,
174
+ agent: {},
200
175
  input: JSON.stringify({ path: '/etc/shadow' }),
201
- context: {} as never,
176
+ context: {},
202
177
  });
203
178
  expect(result.tripwireTriggered).toBe(true);
204
179
  expect((result.outputInfo as Record<string, unknown>).deniedPath).toBe('/etc/shadow');
@@ -207,9 +182,9 @@ describe('buildPathGuardrail', () => {
207
182
  it('passes when path field is within ACL', async () => {
208
183
  const guard = buildPathGuardrail(['/allowed/**']);
209
184
  const result = await guard.execute({
210
- agent: {} as never,
185
+ agent: {},
211
186
  input: JSON.stringify({ path: '/allowed/file.ts' }),
212
- context: {} as never,
187
+ context: {},
213
188
  });
214
189
  expect(result.tripwireTriggered).toBe(false);
215
190
  });
@@ -224,9 +199,9 @@ describe('buildToolAllowlistGuardrail', () => {
224
199
  it('always passes (structural enforcement)', async () => {
225
200
  const guard = buildToolAllowlistGuardrail(['read', 'bash']);
226
201
  const result = await guard.execute({
227
- agent: {} as never,
202
+ agent: {},
228
203
  input: 'run this',
229
- context: {} as never,
204
+ context: {},
230
205
  });
231
206
  expect(result.tripwireTriggered).toBe(false);
232
207
  expect((result.outputInfo as Record<string, unknown>).checked).toBe(true);
@@ -257,6 +232,19 @@ describe('buildDefaultGuardrails', () => {
257
232
  });
258
233
  });
259
234
 
235
+ describe('evaluateGuardrails', () => {
236
+ it('passes when no guardrails are provided', async () => {
237
+ const result = await evaluateGuardrails([], 'anything');
238
+ expect(result.tripwireTriggered).toBe(false);
239
+ });
240
+
241
+ it('returns the first tripwire result', async () => {
242
+ const guard = buildPathGuardrail(['/allowed/**']);
243
+ const result = await evaluateGuardrails([guard], JSON.stringify({ path: '/blocked/file.ts' }));
244
+ expect(result.tripwireTriggered).toBe(true);
245
+ });
246
+ });
247
+
260
248
  // ---------------------------------------------------------------------------
261
249
  // Handoff topology
262
250
  // ---------------------------------------------------------------------------
@@ -278,29 +266,33 @@ describe('buildWorkerAgent', () => {
278
266
  expect(buildWorkerAgent('unknown-archetype', [])).toBeNull();
279
267
  });
280
268
 
281
- it('returns an Agent for a known archetype', () => {
269
+ it('returns an agent for a known archetype', () => {
282
270
  const agent = buildWorkerAgent('worker-read', []);
283
271
  expect(agent).not.toBeNull();
272
+ expect(agent?.name).toBe('worker-read');
284
273
  });
285
274
  });
286
275
 
287
276
  describe('buildLeadAgent', () => {
288
277
  it('creates a lead agent with handoff workers', () => {
289
- const worker = buildWorkerAgent('worker-read', [])!;
278
+ const worker = buildWorkerAgent('worker-read', []);
279
+ if (!worker) throw new Error('worker-read archetype missing');
290
280
  const lead = buildLeadAgent('You are a lead.', 'gpt-4.1', [worker], []);
291
- expect(lead).not.toBeNull();
281
+ expect(lead.name).toBe('cleo-lead');
282
+ expect(lead.handoffs).toHaveLength(1);
292
283
  });
293
284
  });
294
285
 
295
286
  describe('buildStandaloneAgent', () => {
296
- it('creates an agent instance', () => {
287
+ it('creates an agent descriptor', () => {
297
288
  const agent = buildStandaloneAgent('Instructions', 'gpt-4.1-mini', []);
298
- expect(agent).not.toBeNull();
289
+ expect(agent.name).toBe('cleo-worker');
290
+ expect(agent.model).toBe('gpt-4.1-mini');
299
291
  });
300
292
  });
301
293
 
302
294
  describe('buildAgentTopology', () => {
303
- it('returns an agent when tier is worker', () => {
295
+ it('returns a standalone agent when tier is worker', () => {
304
296
  const agent = buildAgentTopology({
305
297
  instructions: 'Do work',
306
298
  model: 'gpt-4.1-mini',
@@ -308,10 +300,11 @@ describe('buildAgentTopology', () => {
308
300
  handoffNames: ['worker-read'],
309
301
  guardrails: [],
310
302
  });
311
- expect(agent).not.toBeNull();
303
+ expect(agent.name).toBe('cleo-worker');
304
+ expect(agent.handoffs).toBeUndefined();
312
305
  });
313
306
 
314
- it('returns an agent when tier is lead', () => {
307
+ it('returns a lead agent with handoffs when tier is lead', () => {
315
308
  const agent = buildAgentTopology({
316
309
  instructions: 'Lead the team',
317
310
  model: 'gpt-4.1',
@@ -319,7 +312,8 @@ describe('buildAgentTopology', () => {
319
312
  handoffNames: ['worker-read', 'worker-write'],
320
313
  guardrails: [],
321
314
  });
322
- expect(agent).not.toBeNull();
315
+ expect(agent.name).toBe('cleo-lead');
316
+ expect(agent.handoffs).toHaveLength(2);
323
317
  });
324
318
 
325
319
  it('handles unknown archetype names gracefully', () => {
@@ -330,10 +324,11 @@ describe('buildAgentTopology', () => {
330
324
  handoffNames: ['worker-read', 'nonexistent-worker'],
331
325
  guardrails: [],
332
326
  });
333
- expect(agent).not.toBeNull();
327
+ expect(agent.name).toBe('cleo-lead');
328
+ expect(agent.handoffs).toHaveLength(1);
334
329
  });
335
330
 
336
- it('returns agent when tier is lead but no valid handoffs', () => {
331
+ it('falls back to standalone when tier is lead but no valid handoffs', () => {
337
332
  const agent = buildAgentTopology({
338
333
  instructions: 'Lead',
339
334
  model: 'gpt-4.1',
@@ -341,7 +336,7 @@ describe('buildAgentTopology', () => {
341
336
  handoffNames: [],
342
337
  guardrails: [],
343
338
  });
344
- expect(agent).not.toBeNull();
339
+ expect(agent.name).toBe('cleo-worker');
345
340
  });
346
341
  });
347
342
 
@@ -355,12 +350,14 @@ describe('OpenAiSdkSpawnProvider', () => {
355
350
  beforeEach(() => {
356
351
  provider = new OpenAiSdkSpawnProvider();
357
352
  mockRunState.shouldThrow = false;
358
- mockRunState.result = { finalOutput: 'completed output' };
359
- createdAgents.length = 0;
353
+ mockRunState.output = 'completed output';
354
+ generateTextCalls.length = 0;
355
+ process.env.OPENAI_API_KEY = 'sk-test-key';
360
356
  });
361
357
 
362
358
  afterEach(() => {
363
359
  vi.clearAllMocks();
360
+ delete process.env.OPENAI_API_KEY;
364
361
  });
365
362
 
366
363
  describe('canSpawn', () => {
@@ -376,7 +373,6 @@ describe('OpenAiSdkSpawnProvider', () => {
376
373
  process.env.OPENAI_API_KEY = 'sk-test-key';
377
374
  const result = await provider.canSpawn();
378
375
  expect(result).toBe(true);
379
- delete process.env.OPENAI_API_KEY;
380
376
  });
381
377
  });
382
378
 
@@ -439,6 +435,27 @@ describe('OpenAiSdkSpawnProvider', () => {
439
435
  options: { model: 'gpt-4o', tier: 'worker', tracingDisabled: true },
440
436
  });
441
437
  expect(result.status).toBe('completed');
438
+ const model = generateTextCalls[0]?.model as
439
+ | { __cleoMockModel: true; modelId: string }
440
+ | undefined;
441
+ expect(model?.modelId).toBe('gpt-4o');
442
+ });
443
+ });
444
+
445
+ describe('spawn — guardrail tripwire', () => {
446
+ it('aborts with failed status when a guardrail trips', async () => {
447
+ const result = await provider.spawn({
448
+ taskId: 'T582-guard',
449
+ prompt: JSON.stringify({ path: '/etc/shadow' }),
450
+ options: {
451
+ allowedGlobs: ['/mnt/projects/**'],
452
+ tier: 'worker',
453
+ tracingDisabled: true,
454
+ },
455
+ });
456
+ expect(result.status).toBe('failed');
457
+ expect(result.error).toContain('guardrail tripped');
458
+ expect(generateTextCalls.length).toBe(0);
442
459
  });
443
460
  });
444
461
  });
@@ -461,8 +478,9 @@ describe('OpenAiSdkAdapter', () => {
461
478
 
462
479
  describe('identity', () => {
463
480
  it('has id openai-sdk', () => expect(adapter.id).toBe('openai-sdk'));
464
- it('has name OpenAI Agents SDK', () => expect(adapter.name).toBe('OpenAI Agents SDK'));
465
- it('has version 1.0.0', () => expect(adapter.version).toBe('1.0.0'));
481
+ it('has display name "OpenAI SDK (Vercel AI SDK)"', () =>
482
+ expect(adapter.name).toBe('OpenAI SDK (Vercel AI SDK)'));
483
+ it('has version 2.0.0', () => expect(adapter.version).toBe('2.0.0'));
466
484
  });
467
485
 
468
486
  describe('capabilities', () => {
@@ -602,30 +620,30 @@ describe('CleoConduitTraceProcessor', () => {
602
620
 
603
621
  describe('onTraceStart', () => {
604
622
  it('resolves without error', async () => {
605
- await expect(processor.onTraceStart({} as never)).resolves.toBeUndefined();
623
+ await expect(processor.onTraceStart({})).resolves.toBeUndefined();
606
624
  });
607
625
  });
608
626
 
609
627
  describe('onTraceEnd', () => {
610
628
  it('resolves without error', async () => {
611
- await expect(processor.onTraceEnd({} as never)).resolves.toBeUndefined();
629
+ await expect(processor.onTraceEnd({})).resolves.toBeUndefined();
612
630
  });
613
631
  });
614
632
 
615
633
  describe('onSpanStart', () => {
616
634
  it('resolves without error', async () => {
617
- await expect(processor.onSpanStart({} as never)).resolves.toBeUndefined();
635
+ await expect(processor.onSpanStart(makeSpanLike())).resolves.toBeUndefined();
618
636
  });
619
637
  });
620
638
 
621
639
  describe('onSpanEnd', () => {
622
640
  it('does not throw for a well-formed span', async () => {
623
641
  const span = makeSpanLike();
624
- await expect(processor.onSpanEnd(span as never)).resolves.toBeUndefined();
642
+ await expect(processor.onSpanEnd(span)).resolves.toBeUndefined();
625
643
  });
626
644
 
627
645
  it('does not throw for a span with missing fields', async () => {
628
- await expect(processor.onSpanEnd({} as never)).resolves.toBeUndefined();
646
+ await expect(processor.onSpanEnd({ spanId: 'x' })).resolves.toBeUndefined();
629
647
  });
630
648
  });
631
649
 
@@ -643,24 +661,26 @@ describe('CleoConduitTraceProcessor', () => {
643
661
  });
644
662
 
645
663
  // ---------------------------------------------------------------------------
646
- // Handoff integration: lead + workers with mocked runner
664
+ // Handoff integration: lead + workers with mocked generateText
647
665
  // ---------------------------------------------------------------------------
648
666
 
649
- describe('Handoff integration — lead routes to workers via SDK', () => {
667
+ describe('Handoff integration — lead routes to workers via Vercel AI SDK', () => {
650
668
  let provider: OpenAiSdkSpawnProvider;
651
669
 
652
670
  beforeEach(() => {
653
671
  provider = new OpenAiSdkSpawnProvider();
654
672
  mockRunState.shouldThrow = false;
655
- mockRunState.result = { finalOutput: 'handoff result' };
656
- createdAgents.length = 0;
673
+ mockRunState.output = 'handoff result';
674
+ generateTextCalls.length = 0;
675
+ process.env.OPENAI_API_KEY = 'sk-test-key';
657
676
  });
658
677
 
659
678
  afterEach(() => {
660
679
  vi.clearAllMocks();
680
+ delete process.env.OPENAI_API_KEY;
661
681
  });
662
682
 
663
- it('creates a lead agent when tier is lead with handoffs', async () => {
683
+ it('executes the lead then each handoff worker sequentially', async () => {
664
684
  const result = await provider.spawn({
665
685
  taskId: 'T582-handoff',
666
686
  prompt: 'Research and implement feature',
@@ -672,12 +692,8 @@ describe('Handoff integration — lead routes to workers via SDK', () => {
672
692
  });
673
693
 
674
694
  expect(result.status).toBe('completed');
675
- expect(result.output).toBe('handoff result');
676
-
677
- // A cleo-lead agent should have been created with 2 handoff workers
678
- const leadAgent = createdAgents.find((a) => a.name === 'cleo-lead');
679
- expect(leadAgent).toBeDefined();
680
- expect(leadAgent?.handoffs).toHaveLength(2);
695
+ // 1 lead + 2 workers = 3 generateText calls
696
+ expect(generateTextCalls.length).toBe(3);
681
697
  });
682
698
 
683
699
  it('result reflects correct providerId and taskId', async () => {
@@ -693,7 +709,6 @@ describe('Handoff integration — lead routes to workers via SDK', () => {
693
709
  });
694
710
 
695
711
  it('handoff workers use worker archetype model (gpt-4.1-mini)', async () => {
696
- createdAgents.length = 0;
697
712
  await provider.spawn({
698
713
  taskId: 'T582-model',
699
714
  prompt: 'Work',
@@ -704,13 +719,29 @@ describe('Handoff integration — lead routes to workers via SDK', () => {
704
719
  },
705
720
  });
706
721
 
707
- // Worker archetypes should use gpt-4.1-mini
708
- const workerAgents = createdAgents.filter(
709
- (a) => a.name === 'worker-read' || a.name === 'worker-bash',
710
- );
711
- expect(workerAgents.length).toBe(2);
712
- for (const worker of workerAgents) {
713
- expect(worker.model).toBe('gpt-4.1-mini');
714
- }
722
+ // Workers receive gpt-4.1-mini — calls[1] and calls[2] correspond to the two workers.
723
+ const workerCallA = generateTextCalls[1]?.model as
724
+ | { __cleoMockModel: true; modelId: string }
725
+ | undefined;
726
+ const workerCallB = generateTextCalls[2]?.model as
727
+ | { __cleoMockModel: true; modelId: string }
728
+ | undefined;
729
+ expect(workerCallA?.modelId).toBe('gpt-4.1-mini');
730
+ expect(workerCallB?.modelId).toBe('gpt-4.1-mini');
731
+ });
732
+
733
+ it('aggregates lead + worker outputs in the final SpawnResult', async () => {
734
+ mockRunState.output = 'step complete';
735
+ const result = await provider.spawn({
736
+ taskId: 'T582-output',
737
+ prompt: 'Do',
738
+ options: { tier: 'lead', handoffs: ['worker-read'], tracingDisabled: true },
739
+ });
740
+
741
+ expect(result.output).toContain('step complete');
742
+ expect(result.output).toContain('[worker-read]');
715
743
  });
716
744
  });
745
+
746
+ // Dummy reference so type-only import doesn't get tree-shaken before test runs.
747
+ void ({} as CleoAgent);
@@ -1,11 +1,14 @@
1
1
  /**
2
- * OpenAI Agents SDK Adapter.
2
+ * OpenAI SDK Adapter — Vercel AI SDK edition.
3
3
  *
4
- * Main `CLEOProviderAdapter` implementation for the OpenAI Agents SDK.
5
- * Provides spawn and install capabilities. Hooks are not supported (the
6
- * SDK does not expose a CLI hook system equivalent to Claude Code's).
4
+ * Main `CLEOProviderAdapter` implementation for the OpenAI provider, backed
5
+ * by the Vercel AI SDK (`ai` v6 + `@ai-sdk/openai`). Provides spawn and
6
+ * install capabilities. Hooks are not supported the Vercel AI SDK does not
7
+ * expose a CLI hook system equivalent to Claude Code's.
7
8
  *
8
- * @task T582
9
+ * @task T582 (original)
10
+ * @task T933 (SDK consolidation — Vercel AI SDK migration)
11
+ * @see ADR-052 — SDK consolidation decision
9
12
  */
10
13
 
11
14
  import type {
@@ -17,29 +20,27 @@ import { OpenAiSdkInstallProvider } from './install.js';
17
20
  import { OpenAiSdkSpawnProvider } from './spawn.js';
18
21
 
19
22
  /**
20
- * CLEO provider adapter for the OpenAI Agents SDK.
23
+ * CLEO provider adapter for the OpenAI provider.
21
24
  *
22
- * Bridges CLEO's adapter system with the `@openai/agents` SDK:
23
- * - Spawn: Launches agents via the SDK runner with handoff topology
25
+ * Bridges CLEO's adapter system with the Vercel AI SDK:
26
+ * - Spawn: Launches agents via the SDK with CLEO-native handoff topology
24
27
  * - Install: Manages AGENTS.md @-references and .openai/ config directory
25
28
  * - Tracing: Default-on conduit span persistence via `CleoConduitTraceProcessor`
26
29
  *
27
30
  * @remarks
28
- * This adapter is the only CLEO provider with first-class handoff support.
29
- * Team Lead Worker topology is declared in `SpawnContext.options.handoffs`
30
- * and wired directly to SDK `Agent.handoffs`. The SDK handles routing
31
- * internally without any CLEO glue code.
32
- *
33
- * This is also the only provider supporting 100+ LLMs via the Vercel AI SDK
34
- * bridge (capability flag: `supportsMultiModel`).
31
+ * Handoff topology is CLEO-owned (see `handoff.ts`): lead agents delegate to
32
+ * worker archetypes in sequence, and the concatenated output is returned.
33
+ * The Vercel AI SDK surface (`generateText` / `streamText`) works uniformly
34
+ * across Anthropic, OpenAI, and compatible providers, so the provider keeps
35
+ * the `supportsMultiModel` capability flag.
35
36
  */
36
37
  export class OpenAiSdkAdapter implements CLEOProviderAdapter {
37
38
  /** Unique provider identifier. */
38
39
  readonly id = 'openai-sdk';
39
40
  /** Human-readable provider name. */
40
- readonly name = 'OpenAI Agents SDK';
41
+ readonly name = 'OpenAI SDK (Vercel AI SDK)';
41
42
  /** Adapter version string. */
42
- readonly version = '1.0.0';
43
+ readonly version = '2.0.0';
43
44
 
44
45
  /** Declared capabilities for this provider. */
45
46
  capabilities: AdapterCapabilities = {
@@ -117,7 +118,7 @@ export class OpenAiSdkAdapter implements CLEOProviderAdapter {
117
118
  details: {
118
119
  apiKeyPresent,
119
120
  projectDir: this.projectDir,
120
- sdkVersion: '0.8.3',
121
+ sdkVersion: 'ai@6 + @ai-sdk/openai',
121
122
  },
122
123
  };
123
124
  }