Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"yaml": "^2.8.3"
},
"peerDependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.88",
"@anthropic-ai/claude-agent-sdk": "^0.2.89",
"@github/copilot-sdk": "^1.0.3",
"@openai/codex-sdk": "^0.136.0",
"@earendil-works/pi-coding-agent": "^0.74.0"
Expand All @@ -60,6 +60,12 @@
"optional": true
}
},
"optionalDependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.89",
"@github/copilot-sdk": "^1.0.3",
"@openai/codex-sdk": "^0.136.0",
"@earendil-works/pi-coding-agent": "^0.74.0"
},
"devDependencies": {
"@agentv/core": "workspace:*",
"@agentv/sdk": "workspace:*",
Expand Down
23 changes: 23 additions & 0 deletions apps/web/src/content/docs/docs/next/targets/coding-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,29 @@ Run bundles preserve target id, provider kind, runtime mode, command argv, cwd,
stdout/stderr, transcripts or logs, final output when available, timing,
timeouts, exit codes, signals, and partial artifacts on failure.

## SDK beta optional dependencies

The SDK providers are beta surfaces and run only inside the AgentV SDK child
runner. AgentV declares their packages as optional dependencies so a normal
`bun install`, `npm install`, or package install can hydrate them, while the
main CLI still does not import the SDK packages during startup or non-SDK
runs.

If your package manager omitted optional dependencies, install the SDK package
for the provider you use:

```bash
bun add --optional @openai/codex-sdk
bun add --optional @earendil-works/pi-coding-agent
bun add --optional @anthropic-ai/claude-agent-sdk
bun add --optional @github/copilot-sdk
```

Missing SDK packages are reported as target execution failures from the child
runner. Prefer the process/protocol providers for default local and CI runs,
and choose an SDK provider only when SDK-native events or lifecycle control are
the behavior under test.

## Codex

Use `codex-app-server` when you want rich protocol control:
Expand Down
23 changes: 23 additions & 0 deletions apps/web/src/content/docs/docs/v4.42.4/targets/coding-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,29 @@ Run bundles preserve target id, provider kind, runtime mode, command argv, cwd,
stdout/stderr, transcripts or logs, final output when available, timing,
timeouts, exit codes, signals, and partial artifacts on failure.

## SDK beta optional dependencies

The SDK providers are beta surfaces and run only inside the AgentV SDK child
runner. AgentV declares their packages as optional dependencies so a normal
`bun install`, `npm install`, or package install can hydrate them, while the
main CLI still does not import the SDK packages during startup or non-SDK
runs.

If your package manager omitted optional dependencies, install the SDK package
for the provider you use:

```bash
bun add --optional @openai/codex-sdk
bun add --optional @earendil-works/pi-coding-agent
bun add --optional @anthropic-ai/claude-agent-sdk
bun add --optional @github/copilot-sdk
```

Missing SDK packages are reported as target execution failures from the child
runner. Prefer the process/protocol providers for default local and CI runs,
and choose an SDK provider only when SDK-native events or lifecycle control are
the behavior under test.

## Codex

Use `codex-app-server` when you want rich protocol control:
Expand Down
316 changes: 307 additions & 9 deletions bun.lock

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"zod": "^3.23.8"
},
"peerDependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.88",
"@anthropic-ai/claude-agent-sdk": "^0.2.89",
"@github/copilot-sdk": "^1.0.3",
"@openai/codex-sdk": "^0.136.0",
"@earendil-works/pi-coding-agent": "^0.74.0"
Expand All @@ -69,6 +69,12 @@
"optional": true
}
},
"optionalDependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.89",
"@github/copilot-sdk": "^1.0.3",
"@openai/codex-sdk": "^0.136.0",
"@earendil-works/pi-coding-agent": "^0.74.0"
},
"devDependencies": {
"@types/micromatch": "^4.0.10",
"@types/nunjucks": "^3.2.6",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/evaluation/providers/claude-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async function loadClaudeSdk(): Promise<typeof import('@anthropic-ai/claude-agen
claudeSdkModule = await import('@anthropic-ai/claude-agent-sdk');
} catch (error) {
throw new Error(
`Failed to load @anthropic-ai/claude-agent-sdk. Please install it:\n npm install @anthropic-ai/claude-agent-sdk\n\nOriginal error: ${error instanceof Error ? error.message : String(error)}`,
`Failed to load @anthropic-ai/claude-agent-sdk. AgentV declares SDK beta packages as optional dependencies; run bun install to hydrate optional dependencies, or install this SDK explicitly:\n bun add --optional @anthropic-ai/claude-agent-sdk\n npm install @anthropic-ai/claude-agent-sdk\n\nOriginal error: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
Expand All @@ -42,7 +42,7 @@ async function loadClaudeSdk(): Promise<typeof import('@anthropic-ai/claude-agen
* session lifecycle. Use `claude-cli` for subprocess-based invocation.
*
* Note: The SDK is loaded lazily on first use to avoid bundling issues.
* Users must install @anthropic-ai/claude-agent-sdk separately.
* If optional dependencies were omitted, install @anthropic-ai/claude-agent-sdk explicitly.
*/
export class ClaudeSdkProvider implements Provider {
readonly id: string;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/evaluation/providers/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async function loadCodexSdk(): Promise<any> {
codexSdkModule = await import('@openai/codex-sdk');
} catch (error) {
throw new Error(
`Failed to load @openai/codex-sdk. Please install it:\n npm install @openai/codex-sdk\n\nOriginal error: ${error instanceof Error ? error.message : String(error)}`,
`Failed to load @openai/codex-sdk. AgentV declares SDK beta packages as optional dependencies; run bun install to hydrate optional dependencies, or install this SDK explicitly:\n bun add --optional @openai/codex-sdk\n npm install @openai/codex-sdk\n\nOriginal error: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
Expand All @@ -41,7 +41,7 @@ async function loadCodexSdk(): Promise<any> {
* This provides typed event access for structured tool calls, token usage, and clean thread lifecycle.
*
* Note: The SDK is loaded lazily on first use to avoid bundling issues.
* Users must install @openai/codex-sdk separately.
* If optional dependencies were omitted, install @openai/codex-sdk explicitly.
*/
export class CodexProvider implements Provider {
readonly id: string;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/evaluation/providers/copilot-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async function loadCopilotSdk(): Promise<any> {
);
}
throw new Error(
`Failed to load @github/copilot-sdk. Please install it:\n npm install @github/copilot-sdk\n\nOriginal error: ${message}`,
`Failed to load @github/copilot-sdk. AgentV declares SDK beta packages as optional dependencies; run bun install to hydrate optional dependencies, or install this SDK explicitly:\n bun add --optional @github/copilot-sdk\n npm install @github/copilot-sdk\n\nOriginal error: ${message}`,
);
}
}
Expand Down Expand Up @@ -99,7 +99,7 @@ interface ToolCallInProgress {
* This provides typed event access for structured tool calls, token usage, and clean session lifecycle.
*
* Note: The SDK is loaded lazily on first use to avoid bundling issues.
* Users must install @github/copilot-sdk separately.
* If optional dependencies were omitted, install @github/copilot-sdk explicitly.
*/
export class CopilotSdkProvider implements Provider {
readonly id: string;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/evaluation/providers/pi-coding-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Events are consumed via `session.subscribe()` to extract messages, tool calls, and token usage.
*
* Dependencies are lazy-loaded on first use to avoid bundling issues.
* The package `@earendil-works/pi-coding-agent` must be installed.
* If optional dependencies were omitted, install @earendil-works/pi-coding-agent explicitly.
*/

import { execSync } from 'node:child_process';
Expand Down Expand Up @@ -225,7 +225,7 @@ async function doLoadSdkModules(): Promise<void> {
}

throw new Error(
'pi-coding-agent SDK is not installed. Install it with:\n npm install @earendil-works/pi-coding-agent',
'pi-coding-agent SDK is not installed. AgentV declares SDK beta packages as optional dependencies; run bun install to hydrate optional dependencies, or install this SDK explicitly:\n bun add --optional @earendil-works/pi-coding-agent\n npm install @earendil-works/pi-coding-agent',
);
}

Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/evaluation/providers/sdk-child-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,17 @@ export class SdkChildProvider implements Provider {
}

function resolveDefaultRunnerArgv(): readonly string[] {
const jsRunnerPath = fileURLToPath(new URL('./sdk-child-runner.js', import.meta.url));
if (existsSync(jsRunnerPath)) {
return [process.execPath, jsRunnerPath];
for (const relativePath of [
'./sdk-child-runner.js',
'./evaluation/providers/sdk-child-runner.js',
'./sdk-child-runner.ts',
]) {
const runnerPath = fileURLToPath(new URL(relativePath, import.meta.url));
if (existsSync(runnerPath)) {
return [process.execPath, runnerPath];
}
}

const tsRunnerPath = fileURLToPath(new URL('./sdk-child-runner.ts', import.meta.url));
return [process.execPath, tsRunnerPath];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,49 @@ describe('SdkChildProvider', () => {
expect(raw.child_runner?.stderr).toContain('stderr log');
});

it('passes the provider kind, target config, and request through the child protocol', async () => {
const provider = fakeProvider('request-plumbing', {
model: 'gpt-5-codex',
cwd: '/tmp/from-config',
});

const response = await provider.invoke({
question: 'inspect this request',
cwd: '/tmp/from-request',
metadata: { run_id: 'run-123' },
maxOutputTokens: 128,
});

expect(extractLastAssistantContent(response.output)).toBe('request received');
expect(response.raw).toMatchObject({
received_provider_kind: 'codex-sdk',
received_target_name: 'fake-target',
received_model: 'gpt-5-codex',
received_question: 'inspect this request',
received_cwd: '/tmp/from-request',
received_metadata: { run_id: 'run-123' },
received_max_output_tokens: 128,
});
});

it('keeps missing SDK dependency errors scoped to the child runner', async () => {
const provider = fakeProvider('dependency-error');

try {
await provider.invoke({ question: 'hello' });
throw new Error('expected invoke to fail');
} catch (error) {
expect(error).toBeInstanceOf(SdkChildRunnerError);
const runnerError = error as SdkChildRunnerError;
expect(runnerError.reason).toBe('child_error');
expect(runnerError.message).toContain('@openai/codex-sdk');
expect(runnerError.message).toContain('bun install');
expect(runnerError.details).toMatchObject({
code: 'MissingSdkDependency',
});
}
});

it('maps fatal child exit before a result to a provider-scoped error', async () => {
const provider = fakeProvider('fatal');

Expand Down Expand Up @@ -82,13 +125,17 @@ async function writeFakeRunner(filePath: string): Promise<void> {
filePath,
`
const mode = process.argv[2];
let requestEnvelope;
await new Promise((resolve) => {
let body = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => {
body += chunk;
});
process.stdin.on('end', resolve);
process.stdin.on('end', () => {
requestEnvelope = JSON.parse(body.trim());
resolve();
});
});

function write(message) {
Expand All @@ -109,6 +156,36 @@ if (mode === 'success') {
process.exit(0);
}

if (mode === 'request-plumbing') {
write({
type: 'result',
response: {
raw: {
received_provider_kind: requestEnvelope.provider_kind,
received_target_name: requestEnvelope.target_name,
received_model: requestEnvelope.config.model,
received_question: requestEnvelope.request.question,
received_cwd: requestEnvelope.request.cwd,
received_metadata: requestEnvelope.request.metadata,
received_max_output_tokens: requestEnvelope.request.max_output_tokens,
},
output: [{ role: 'assistant', content: 'request received' }],
},
});
process.exit(0);
}

if (mode === 'dependency-error') {
write({
type: 'error',
error: {
code: 'MissingSdkDependency',
message: 'Failed to load @openai/codex-sdk. SDK beta optional dependencies are installed by default with bun install; if optional dependencies were omitted, run bun install or bun add --optional @openai/codex-sdk.',
},
});
process.exit(1);
}

if (mode === 'fatal') {
process.stderr.write('fatal before result');
process.exit(7);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { describe, expect, it } from 'bun:test';
import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

import { createBuiltinProviderRegistry } from '../../../src/evaluation/providers/index.js';
import { SdkChildProvider } from '../../../src/evaluation/providers/sdk-child-provider.js';

describe('SDK provider registry isolation', () => {
const testDir = dirname(fileURLToPath(import.meta.url));
const corePackageRoot = resolve(testDir, '../../..');
const repoRoot = resolve(corePackageRoot, '../..');

const sdkOptionalDependencies = {
'@anthropic-ai/claude-agent-sdk': '^0.2.89',
'@earendil-works/pi-coding-agent': '^0.74.0',
'@github/copilot-sdk': '^1.0.3',
'@openai/codex-sdk': '^0.136.0',
};

it('registers explicit SDK providers through the child-runner wrapper', () => {
const registry = createBuiltinProviderRegistry();

Expand All @@ -31,4 +43,23 @@ describe('SDK provider registry isolation', () => {
expect(source).not.toContain("from './copilot-sdk.js'");
expect(source).not.toContain("from './pi-coding-agent.js'");
});

it('declares SDK provider packages as optional package dependencies', () => {
for (const packageJsonPath of [
resolve(corePackageRoot, 'package.json'),
resolve(repoRoot, 'apps/cli/package.json'),
]) {
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
optionalDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
};

expect(packageJson.optionalDependencies).toMatchObject(sdkOptionalDependencies);
expect(packageJson.peerDependencies).toMatchObject(sdkOptionalDependencies);
for (const packageName of Object.keys(sdkOptionalDependencies)) {
expect(packageJson.peerDependenciesMeta?.[packageName]?.optional).toBe(true);
}
}
});
});
Loading