@cleocode/adapters 2026.4.91 → 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.
- package/dist/index.js +41069 -18278
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code/install.d.ts.map +1 -1
- package/dist/providers/claude-sdk/index.d.ts +10 -4
- package/dist/providers/claude-sdk/index.d.ts.map +1 -1
- package/dist/providers/claude-sdk/spawn.d.ts +29 -28
- package/dist/providers/claude-sdk/spawn.d.ts.map +1 -1
- package/dist/providers/codex/install.d.ts.map +1 -1
- package/dist/providers/cursor/install.d.ts.map +1 -1
- package/dist/providers/openai-sdk/adapter.d.ts +18 -17
- package/dist/providers/openai-sdk/adapter.d.ts.map +1 -1
- package/dist/providers/openai-sdk/guardrails.d.ts +71 -18
- package/dist/providers/openai-sdk/guardrails.d.ts.map +1 -1
- package/dist/providers/openai-sdk/handoff.d.ts +51 -21
- package/dist/providers/openai-sdk/handoff.d.ts.map +1 -1
- package/dist/providers/openai-sdk/index.d.ts +8 -5
- package/dist/providers/openai-sdk/index.d.ts.map +1 -1
- package/dist/providers/openai-sdk/install.d.ts +1 -1
- package/dist/providers/openai-sdk/spawn.d.ts +54 -21
- package/dist/providers/openai-sdk/spawn.d.ts.map +1 -1
- package/dist/providers/openai-sdk/tracing.d.ts +87 -21
- package/dist/providers/openai-sdk/tracing.d.ts.map +1 -1
- package/dist/providers/opencode/install.d.ts.map +1 -1
- package/dist/providers/opencode/spawn.d.ts.map +1 -1
- package/dist/providers/pi/install.d.ts.map +1 -1
- package/dist/providers/shared/paths.d.ts +32 -0
- package/dist/providers/shared/paths.d.ts.map +1 -0
- package/dist/providers/shared/sdk-result-mapper.d.ts +9 -7
- package/dist/providers/shared/sdk-result-mapper.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/__tests__/claude-code-adapter.test.ts +9 -4
- package/src/__tests__/cursor-adapter.test.ts +9 -8
- package/src/__tests__/harness-interop.test.ts +451 -0
- package/src/__tests__/opencode-adapter.test.ts +9 -4
- package/src/providers/claude-code/install.ts +10 -2
- package/src/providers/claude-sdk/__tests__/spawn.test.ts +100 -265
- package/src/providers/claude-sdk/index.ts +10 -4
- package/src/providers/claude-sdk/spawn.ts +69 -106
- package/src/providers/codex/install.ts +10 -2
- package/src/providers/cursor/install.ts +10 -2
- package/src/providers/openai-sdk/__tests__/openai-sdk-spawn.test.ts +134 -103
- package/src/providers/openai-sdk/adapter.ts +19 -18
- package/src/providers/openai-sdk/guardrails.ts +106 -25
- package/src/providers/openai-sdk/handoff.ts +73 -37
- package/src/providers/openai-sdk/index.ts +28 -4
- package/src/providers/openai-sdk/install.ts +1 -1
- package/src/providers/openai-sdk/manifest.json +4 -4
- package/src/providers/openai-sdk/spawn.ts +213 -48
- package/src/providers/openai-sdk/tracing.ts +105 -22
- package/src/providers/opencode/install.ts +10 -2
- package/src/providers/opencode/spawn.ts +2 -1
- package/src/providers/pi/install.ts +10 -2
- package/src/providers/shared/paths.ts +79 -0
- package/src/providers/shared/sdk-result-mapper.ts +9 -7
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude SDK Spawn Provider
|
|
2
|
+
* Claude SDK Spawn Provider — Vercel AI SDK edition.
|
|
3
3
|
*
|
|
4
|
-
* Implements
|
|
5
|
-
*
|
|
4
|
+
* Implements {@link AdapterSpawnProvider} using the Vercel AI SDK
|
|
5
|
+
* (`ai` v6 + `@ai-sdk/anthropic`) instead of the legacy
|
|
6
|
+
* `@anthropic-ai/claude-agent-sdk`. CLEO retains its own orchestration
|
|
7
|
+
* primitives (`composeSpawnPayload`, playbook runtime, agent registry);
|
|
8
|
+
* the SDK is strictly the LLM bridge.
|
|
6
9
|
*
|
|
7
10
|
* Differences from `ClaudeCodeSpawnProvider`:
|
|
8
|
-
* - Uses
|
|
11
|
+
* - Uses `generateText()` via Vercel AI SDK instead of a detached child process
|
|
9
12
|
* - Awaits full completion before returning (synchronous output capture)
|
|
10
|
-
* - Session IDs
|
|
13
|
+
* - Session IDs are generated by CLEO and tracked in `SessionStore`
|
|
11
14
|
* - No temp files, no OS PIDs — tracking is purely in-memory session IDs
|
|
12
15
|
* - `canSpawn()` uses 3-tier key resolution (env var → stored key → Claude Code OAuth)
|
|
13
16
|
*
|
|
14
17
|
* CANT enrichment is identical to the CLI provider: `buildCantEnrichedPrompt()`
|
|
15
|
-
* is called before `
|
|
18
|
+
* is called before `generateText()` and the result is passed as the user prompt.
|
|
16
19
|
*
|
|
17
|
-
* @task T581
|
|
20
|
+
* @task T581 (original)
|
|
21
|
+
* @task T933 (SDK consolidation — Vercel AI SDK migration)
|
|
18
22
|
* @see T752 — canSpawn() OAuth fix
|
|
23
|
+
* @see ADR-052 — SDK consolidation decision
|
|
19
24
|
*/
|
|
20
25
|
|
|
21
26
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -23,7 +28,6 @@ import { homedir } from 'node:os';
|
|
|
23
28
|
import { join } from 'node:path';
|
|
24
29
|
import type { AdapterSpawnProvider, SpawnContext, SpawnResult } from '@cleocode/contracts';
|
|
25
30
|
import { getErrorMessage } from '@cleocode/contracts';
|
|
26
|
-
import { getServers } from './mcp-registry.js';
|
|
27
31
|
import { SessionStore } from './session-store.js';
|
|
28
32
|
import { resolveTools } from './tool-bridge.js';
|
|
29
33
|
|
|
@@ -86,19 +90,19 @@ function resolveAnthropicApiKey(): string | null {
|
|
|
86
90
|
const DEFAULT_MODEL = 'claude-sonnet-4-5';
|
|
87
91
|
|
|
88
92
|
/**
|
|
89
|
-
* Spawn provider that uses the
|
|
90
|
-
* subagent execution.
|
|
93
|
+
* Spawn provider that uses the Vercel AI SDK (`ai` v6 + `@ai-sdk/anthropic`)
|
|
94
|
+
* for programmatic subagent execution.
|
|
91
95
|
*
|
|
92
|
-
* Each call to `spawn()` runs a
|
|
93
|
-
* captures the output. Sessions are tracked in `SessionStore` so callers
|
|
94
|
-
*
|
|
95
|
-
* `terminate()`.
|
|
96
|
+
* Each call to `spawn()` runs a single `generateText()` call to completion and
|
|
97
|
+
* captures the output. Sessions are tracked in `SessionStore` so callers can
|
|
98
|
+
* inspect active sessions via `listRunning()` and remove them via `terminate()`.
|
|
96
99
|
*
|
|
97
100
|
* @remarks
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
101
|
+
* Unlike the legacy `@anthropic-ai/claude-agent-sdk`, the Vercel AI SDK is
|
|
102
|
+
* strictly an LLM bridge — it does not manage a Claude Code subprocess or
|
|
103
|
+
* provide built-in MCP server wiring. Tool use is available through the SDK's
|
|
104
|
+
* tool system, but CLEO orchestration (composeSpawnPayload, playbooks, the
|
|
105
|
+
* agent registry) remains the source of truth for scaffolding.
|
|
102
106
|
*/
|
|
103
107
|
export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
|
|
104
108
|
/** In-memory session registry. */
|
|
@@ -112,9 +116,6 @@ export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
|
|
|
112
116
|
* - `~/.local/share/cleo/anthropic-key` (user-stored via cleo config)
|
|
113
117
|
* - Claude Code OAuth token (zero-config for Claude Code users)
|
|
114
118
|
*
|
|
115
|
-
* No binary check is needed because the SDK manages the Claude Code
|
|
116
|
-
* subprocess internally.
|
|
117
|
-
*
|
|
118
119
|
* @returns `true` when any Anthropic credential is available
|
|
119
120
|
*/
|
|
120
121
|
async canSpawn(): Promise<boolean> {
|
|
@@ -122,24 +123,24 @@ export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
/**
|
|
125
|
-
* Spawn a subagent using the
|
|
126
|
+
* Spawn a subagent using the Vercel AI SDK.
|
|
126
127
|
*
|
|
127
|
-
* Enriches the prompt via CANT context, runs
|
|
128
|
-
*
|
|
129
|
-
* `SpawnResult` with the final output and exit code.
|
|
128
|
+
* Enriches the prompt via CANT context, runs `generateText()` to completion,
|
|
129
|
+
* and returns a `SpawnResult` with the final output and exit code.
|
|
130
130
|
*
|
|
131
131
|
* @param context - Spawn context with taskId, prompt, options
|
|
132
132
|
* @returns Resolved spawn result (status: 'completed' or 'failed')
|
|
133
133
|
*/
|
|
134
134
|
async spawn(context: SpawnContext): Promise<SpawnResult> {
|
|
135
135
|
const instanceId = `sdk-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
136
|
+
const sessionId = `cleo-${instanceId}`;
|
|
136
137
|
const startTime = new Date().toISOString();
|
|
137
138
|
|
|
138
139
|
// Register in session store immediately so listRunning() reflects it
|
|
139
|
-
// before the async
|
|
140
|
+
// before the async generation starts.
|
|
140
141
|
this.sessions.add({
|
|
141
142
|
instanceId,
|
|
142
|
-
sessionId
|
|
143
|
+
sessionId,
|
|
143
144
|
taskId: context.taskId,
|
|
144
145
|
startTime,
|
|
145
146
|
});
|
|
@@ -158,92 +159,54 @@ export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
|
|
|
158
159
|
// CANT enrichment unavailable — use raw prompt
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
// Lazy-import the SDK to avoid hard failures when
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
const {
|
|
162
|
+
// Lazy-import the SDK to avoid hard failures when credentials are absent
|
|
163
|
+
// (canSpawn() guards the normal path, but tests may import this module
|
|
164
|
+
// without a key).
|
|
165
|
+
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
166
|
+
const { generateText } = await import('ai');
|
|
165
167
|
|
|
166
|
-
|
|
168
|
+
const apiKey = resolveAnthropicApiKey();
|
|
169
|
+
if (!apiKey) {
|
|
170
|
+
this.sessions.remove(instanceId);
|
|
171
|
+
return {
|
|
172
|
+
instanceId,
|
|
173
|
+
taskId: context.taskId,
|
|
174
|
+
providerId: 'claude-sdk',
|
|
175
|
+
status: 'failed',
|
|
176
|
+
startTime,
|
|
177
|
+
endTime: new Date().toISOString(),
|
|
178
|
+
exitCode: 1,
|
|
179
|
+
error: 'No Anthropic credentials available (env, stored key, or Claude Code OAuth)',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Build the Anthropic provider with the resolved key.
|
|
184
|
+
const anthropicProvider = createAnthropic({ apiKey });
|
|
185
|
+
|
|
186
|
+
// Record the tool allowlist in metadata even though the AI SDK lacks
|
|
187
|
+
// built-in tool execution for arbitrary Claude Code tools. Downstream
|
|
188
|
+
// orchestration (composeSpawnPayload) applies allowlists before prompt
|
|
189
|
+
// composition; this array is surfaced so telemetry remains complete.
|
|
167
190
|
const toolAllowlist = context.options?.toolAllowlist as string[] | undefined;
|
|
168
191
|
const allowedTools = resolveTools(toolAllowlist);
|
|
169
192
|
|
|
170
|
-
|
|
171
|
-
const workDir = context.workingDirectory ?? process.cwd();
|
|
172
|
-
const mcpServers = getServers(workDir);
|
|
173
|
-
|
|
174
|
-
// Resume support: pass a prior session ID if provided.
|
|
175
|
-
const resumeSessionId = context.options?.resumeSessionId as string | undefined;
|
|
193
|
+
const modelId = (context.options?.model as string) ?? DEFAULT_MODEL;
|
|
176
194
|
|
|
177
|
-
const
|
|
195
|
+
const result = await generateText({
|
|
196
|
+
model: anthropicProvider(modelId),
|
|
178
197
|
prompt: enrichedPrompt,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
allowDangerouslySkipPermissions: true,
|
|
185
|
-
...(Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),
|
|
186
|
-
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
198
|
+
providerOptions: {
|
|
199
|
+
anthropic: {
|
|
200
|
+
// Preserve allowlist metadata for trace visibility.
|
|
201
|
+
cleoAllowedTools: allowedTools,
|
|
202
|
+
},
|
|
187
203
|
},
|
|
188
204
|
});
|
|
189
205
|
|
|
190
|
-
// Stream messages from the SDK, collecting text output and session ID.
|
|
191
|
-
const textParts: string[] = [];
|
|
192
|
-
let exitCode = 0;
|
|
193
|
-
let finalError: string | undefined;
|
|
194
|
-
|
|
195
|
-
for await (const message of sdkQuery) {
|
|
196
|
-
// Capture the session ID from the first message that carries it.
|
|
197
|
-
if ('session_id' in message && typeof message.session_id === 'string') {
|
|
198
|
-
this.sessions.setSessionId(instanceId, message.session_id);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (message.type === 'assistant') {
|
|
202
|
-
// Aggregate text blocks from assistant messages.
|
|
203
|
-
for (const block of message.message.content) {
|
|
204
|
-
if (block.type === 'text') {
|
|
205
|
-
textParts.push(block.text);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
} else if (message.type === 'result') {
|
|
209
|
-
if (message.subtype === 'success') {
|
|
210
|
-
// The result field on success contains the final summary text.
|
|
211
|
-
if (message.result) {
|
|
212
|
-
textParts.push(message.result);
|
|
213
|
-
}
|
|
214
|
-
exitCode = message.is_error ? 1 : 0;
|
|
215
|
-
} else {
|
|
216
|
-
// Error subtypes: error_max_turns, error_during_execution, etc.
|
|
217
|
-
exitCode = 1;
|
|
218
|
-
if ('errors' in message && Array.isArray(message.errors) && message.errors.length > 0) {
|
|
219
|
-
finalError = (message.errors as string[]).join('; ');
|
|
220
|
-
} else {
|
|
221
|
-
// Fallback: use the subtype string as the error description so
|
|
222
|
-
// `finalError` is always truthy for non-success result messages.
|
|
223
|
-
finalError = String(message.subtype);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
206
|
const endTime = new Date().toISOString();
|
|
230
207
|
this.sessions.remove(instanceId);
|
|
231
208
|
|
|
232
|
-
const output =
|
|
233
|
-
|
|
234
|
-
if (finalError) {
|
|
235
|
-
return {
|
|
236
|
-
instanceId,
|
|
237
|
-
taskId: context.taskId,
|
|
238
|
-
providerId: 'claude-sdk',
|
|
239
|
-
status: 'failed',
|
|
240
|
-
output,
|
|
241
|
-
exitCode,
|
|
242
|
-
startTime,
|
|
243
|
-
endTime,
|
|
244
|
-
error: finalError,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
209
|
+
const output = (result.text ?? '').trim();
|
|
247
210
|
|
|
248
211
|
return {
|
|
249
212
|
instanceId,
|
|
@@ -251,7 +214,7 @@ export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
|
|
|
251
214
|
providerId: 'claude-sdk',
|
|
252
215
|
status: 'completed',
|
|
253
216
|
output,
|
|
254
|
-
exitCode,
|
|
217
|
+
exitCode: 0,
|
|
255
218
|
startTime,
|
|
256
219
|
endTime,
|
|
257
220
|
};
|
|
@@ -293,10 +256,10 @@ export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
|
|
|
293
256
|
/**
|
|
294
257
|
* Remove a session from tracking.
|
|
295
258
|
*
|
|
296
|
-
* The underlying
|
|
297
|
-
* externally once the
|
|
298
|
-
* the store prevents it from appearing in `listRunning()` but
|
|
299
|
-
* interrupt the in-progress
|
|
259
|
+
* The underlying `generateText()` call runs inside `spawn()` and cannot be
|
|
260
|
+
* cancelled externally once the HTTP request is in flight. Removing the
|
|
261
|
+
* entry from the store prevents it from appearing in `listRunning()` but
|
|
262
|
+
* does not interrupt the in-progress request.
|
|
300
263
|
*
|
|
301
264
|
* @param instanceId - ID of the spawn instance to terminate
|
|
302
265
|
*/
|
|
@@ -11,9 +11,17 @@
|
|
|
11
11
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
13
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
14
|
+
import { getCleoTemplatesTildePath } from '../shared/paths.js';
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Lines that should appear in AGENTS.md to reference CLEO.
|
|
18
|
+
* The CLEO-INJECTION.md path is resolved dynamically to support non-default
|
|
19
|
+
* XDG / OS installation locations (T916).
|
|
20
|
+
*/
|
|
21
|
+
const INSTRUCTION_REFERENCES = [
|
|
22
|
+
`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`,
|
|
23
|
+
'@.cleo/memory-bridge.md',
|
|
24
|
+
];
|
|
17
25
|
|
|
18
26
|
/**
|
|
19
27
|
* Install provider for Codex CLI.
|
|
@@ -17,9 +17,17 @@
|
|
|
17
17
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
20
|
+
import { getCleoTemplatesTildePath } from '../shared/paths.js';
|
|
20
21
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Lines that should appear in instruction files to reference CLEO.
|
|
24
|
+
* The CLEO-INJECTION.md path is resolved dynamically to support non-default
|
|
25
|
+
* XDG / OS installation locations (T916).
|
|
26
|
+
*/
|
|
27
|
+
const INSTRUCTION_REFERENCES = [
|
|
28
|
+
`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`,
|
|
29
|
+
'@.cleo/memory-bridge.md',
|
|
30
|
+
];
|
|
23
31
|
|
|
24
32
|
/**
|
|
25
33
|
* Install provider for Cursor.
|