Route real provider tests through OpenRouter
This commit is contained in:
@@ -7,13 +7,19 @@ import { createOpencodeClient } from "@opencode-ai/sdk/v2/client";
|
||||
|
||||
import { AgentManager } from "./agent-manager.js";
|
||||
import { AgentStorage } from "./agent-storage.js";
|
||||
import { createAllClients, shutdownProviders } from "./provider-registry.js";
|
||||
import { shutdownProviders } from "./provider-registry.js";
|
||||
import { generateAndApplyAgentMetadata } from "./agent-metadata-generator.js";
|
||||
import { isProviderAvailable } from "../daemon-e2e/agent-configs.js";
|
||||
import { OpenCodeServerManager } from "./providers/opencode/server-manager.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
const CODEX_TEST_MODEL = "gpt-5.4-mini";
|
||||
const CODEX_TEST_THINKING_OPTION_ID = "low";
|
||||
const CODEX_TEST_MODEL = getRealProviderConfig("codex").model;
|
||||
const CODEX_TEST_THINKING_OPTION_ID = getRealProviderConfig("codex").thinkingOptionId;
|
||||
const CLAUDE_TEST_MODEL = getRealProviderConfig("claude").model;
|
||||
const OPENCODE_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
|
||||
function collectFilesRecursively(root: string, filter: (name: string) => boolean): Set<string> {
|
||||
const results = new Set<string>();
|
||||
@@ -75,9 +81,9 @@ describe("agent metadata generation (real agents)", () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
[codexAvailable, claudeAvailable, opencodeAvailable] = await Promise.all([
|
||||
isProviderAvailable("codex"),
|
||||
isProviderAvailable("claude"),
|
||||
isProviderAvailable("opencode"),
|
||||
canRunRealProvider("codex"),
|
||||
canRunRealProvider("claude"),
|
||||
canRunRealProvider("opencode"),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -86,7 +92,7 @@ describe("agent metadata generation (real agents)", () => {
|
||||
paseoHome = tmpCwd("metadata-paseo-home-");
|
||||
storage = new AgentStorage(path.join(paseoHome, "agents"), logger);
|
||||
manager = new AgentManager({
|
||||
clients: createAllClients(logger),
|
||||
clients: createRealProviderClients(["codex", "claude", "opencode"], logger),
|
||||
registry: storage,
|
||||
logger,
|
||||
});
|
||||
@@ -106,7 +112,9 @@ describe("agent metadata generation (real agents)", () => {
|
||||
{
|
||||
provider: "codex",
|
||||
model: CODEX_TEST_MODEL,
|
||||
thinkingOptionId: CODEX_TEST_THINKING_OPTION_ID,
|
||||
...(CODEX_TEST_THINKING_OPTION_ID
|
||||
? { thinkingOptionId: CODEX_TEST_THINKING_OPTION_ID }
|
||||
: {}),
|
||||
modeId: "auto",
|
||||
cwd: cwd,
|
||||
title: "Main Agent",
|
||||
@@ -144,7 +152,7 @@ describe("agent metadata generation (real agents)", () => {
|
||||
const agent = await manager.createAgent(
|
||||
{
|
||||
provider: "claude",
|
||||
model: "haiku",
|
||||
model: CLAUDE_TEST_MODEL,
|
||||
thinkingOptionId: "on",
|
||||
cwd: cwd,
|
||||
title: "Main Claude Agent",
|
||||
@@ -182,7 +190,7 @@ describe("agent metadata generation (real agents)", () => {
|
||||
const agent = await manager.createAgent(
|
||||
{
|
||||
provider: "opencode",
|
||||
model: "opencode/gpt-5-nano",
|
||||
model: OPENCODE_TEST_MODEL,
|
||||
modeId: "build",
|
||||
cwd: cwd,
|
||||
title: "Main OpenCode Agent",
|
||||
|
||||
+16
-11
@@ -12,14 +12,17 @@ import { generateStructuredAgentResponse } from "./agent-response-loop.js";
|
||||
import { AgentManager } from "./agent-manager.js";
|
||||
import { AgentStorage } from "./agent-storage.js";
|
||||
import { createAgentMcpServer } from "./mcp-server.js";
|
||||
import { createAllClients, shutdownProviders } from "./provider-registry.js";
|
||||
import { isProviderAvailable } from "../daemon-e2e/agent-configs.js";
|
||||
import { shutdownProviders } from "./provider-registry.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "../daemon-e2e/real-provider-test-config.js";
|
||||
import pino from "pino";
|
||||
|
||||
const CODEX_TEST_MODEL = "gpt-5.4-mini";
|
||||
const CODEX_TEST_THINKING_OPTION_ID = "low";
|
||||
|
||||
const hasOpenAICredentials = !!process.env.OPENAI_API_KEY;
|
||||
const CODEX_TEST_MODEL = getRealProviderConfig("codex").model;
|
||||
const CODEX_TEST_THINKING_OPTION_ID = getRealProviderConfig("codex").thinkingOptionId;
|
||||
const CLAUDE_TEST_MODEL = getRealProviderConfig("claude").model;
|
||||
|
||||
interface AgentMcpServerHandle {
|
||||
url: string;
|
||||
@@ -154,8 +157,8 @@ describe("getStructuredAgentResponse (e2e)", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
|
||||
beforeAll(async () => {
|
||||
canRunCodex = !process.env.CI && hasOpenAICredentials;
|
||||
canRunClaude = await isProviderAvailable("claude");
|
||||
canRunCodex = await canRunRealProvider("codex");
|
||||
canRunClaude = await canRunRealProvider("claude");
|
||||
if (!canRunCodex && !canRunClaude) {
|
||||
return;
|
||||
}
|
||||
@@ -169,7 +172,7 @@ describe("getStructuredAgentResponse (e2e)", () => {
|
||||
beforeEach(async () => {
|
||||
cwd = mkdtempSync(path.join(tmpdir(), "agent-response-loop-"));
|
||||
manager = new AgentManager({
|
||||
clients: createAllClients(logger),
|
||||
clients: createRealProviderClients(["codex", "claude"], logger),
|
||||
logger,
|
||||
});
|
||||
});
|
||||
@@ -193,7 +196,9 @@ describe("getStructuredAgentResponse (e2e)", () => {
|
||||
agentConfig: {
|
||||
provider: "codex",
|
||||
model: CODEX_TEST_MODEL,
|
||||
thinkingOptionId: CODEX_TEST_THINKING_OPTION_ID,
|
||||
...(CODEX_TEST_THINKING_OPTION_ID
|
||||
? { thinkingOptionId: CODEX_TEST_THINKING_OPTION_ID }
|
||||
: {}),
|
||||
cwd,
|
||||
title: "Structured Response Test",
|
||||
},
|
||||
@@ -222,7 +227,7 @@ describe("getStructuredAgentResponse (e2e)", () => {
|
||||
manager,
|
||||
agentConfig: {
|
||||
provider: "claude",
|
||||
model: "haiku",
|
||||
model: CLAUDE_TEST_MODEL,
|
||||
thinkingOptionId: "on",
|
||||
cwd,
|
||||
title: "Claude Haiku Structured Test",
|
||||
@@ -1,15 +1,17 @@
|
||||
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import pino from "pino";
|
||||
|
||||
import { isProviderAvailable } from "../../../daemon-e2e/agent-configs.js";
|
||||
import { ClaudeAgentClient } from "./agent.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
} from "../../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
// Real-Claude contract coverage: validates slash command shape from a live Claude CLI session.
|
||||
describe("claude agent commands contract (real)", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -19,9 +21,7 @@ describe("claude agent commands contract (real)", () => {
|
||||
});
|
||||
|
||||
test("lists slash commands with the expected contract", async () => {
|
||||
const client = new ClaudeAgentClient({
|
||||
logger: pino({ level: "silent" }),
|
||||
});
|
||||
const client = createRealProviderClient("claude", pino({ level: "silent" }));
|
||||
const session = await client.createSession({
|
||||
provider: "claude",
|
||||
cwd: process.cwd(),
|
||||
|
||||
+8
-6
@@ -19,15 +19,18 @@ import {
|
||||
type AgentSession,
|
||||
type AgentStreamEvent,
|
||||
} from "../../agent-sdk-types.js";
|
||||
import { isProviderAvailable } from "../../../daemon-e2e/agent-configs.js";
|
||||
import { ClaudeAgentClient } from "./agent.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
} from "../../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const logger = pino({ level: "silent" });
|
||||
const client = new ClaudeAgentClient({ logger });
|
||||
const client = createRealProviderClient("claude", logger);
|
||||
|
||||
function tmpCwd(prefix: string): string {
|
||||
return mkdtempSync(path.join(tmpdir(), prefix));
|
||||
@@ -62,11 +65,10 @@ async function createSession(params?: {
|
||||
}): Promise<{ cwd: string; session: AgentSession }> {
|
||||
const cwd = tmpCwd(params?.cwdPrefix ?? "event-stream-integration-");
|
||||
const session = await client.createSession({
|
||||
provider: "claude",
|
||||
...getRealProviderConfig("claude"),
|
||||
cwd,
|
||||
title: "event-stream integration",
|
||||
modeId: "acceptEdits",
|
||||
model: "haiku",
|
||||
});
|
||||
return { cwd, session };
|
||||
}
|
||||
@@ -202,7 +204,7 @@ function assertInvariants(events: AgentStreamEvent[], foregroundTurnIds: string[
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -2,8 +2,10 @@ import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import pino from "pino";
|
||||
|
||||
import type { AgentStreamEvent, AgentSession } from "../../agent-sdk-types.js";
|
||||
import { isProviderAvailable } from "../../../daemon-e2e/agent-configs.js";
|
||||
import { ClaudeAgentClient } from "./agent.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
} from "../../../daemon-e2e/real-provider-test-config.js";
|
||||
import { streamSession } from "../test-utils/session-stream-adapter.js";
|
||||
|
||||
function isTerminalEvent(event: AgentStreamEvent): boolean {
|
||||
@@ -29,7 +31,7 @@ describe("Claude max effort availability (real)", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -39,9 +41,7 @@ describe("Claude max effort availability (real)", () => {
|
||||
});
|
||||
|
||||
test("surfaces the Claude stderr diagnostic when bypassPermissions + max effort is unavailable", async () => {
|
||||
const client = new ClaudeAgentClient({
|
||||
logger: pino({ level: "silent" }),
|
||||
});
|
||||
const client = createRealProviderClient("claude", pino({ level: "silent" }));
|
||||
const session = await client.createSession({
|
||||
provider: "claude",
|
||||
cwd: process.cwd(),
|
||||
|
||||
@@ -10,15 +10,19 @@ import type {
|
||||
AgentStreamEvent,
|
||||
ToolCallTimelineItem,
|
||||
} from "../../agent-sdk-types.js";
|
||||
import { isProviderAvailable } from "../../../daemon-e2e/agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
getRealProviderRuntimeSettings,
|
||||
} from "../../../daemon-e2e/real-provider-test-config.js";
|
||||
import { findExecutable } from "../../../../utils/executable.js";
|
||||
import { withTimeout } from "../../../../utils/promise-timeout.js";
|
||||
import { ClaudeAgentClient } from "./agent.js";
|
||||
import { claudeQuery } from "./query.js";
|
||||
import { streamSession } from "../test-utils/session-stream-adapter.js";
|
||||
|
||||
const logger = pino({ level: "silent" });
|
||||
const client = new ClaudeAgentClient({ logger });
|
||||
const client = createRealProviderClient("claude", logger);
|
||||
|
||||
function tmpCwd(prefix: string): string {
|
||||
return mkdtempSync(path.join(tmpdir(), prefix));
|
||||
@@ -150,11 +154,10 @@ async function createSession(params?: {
|
||||
}): Promise<{ cwd: string; session: AgentSession }> {
|
||||
const cwd = tmpCwd(params?.cwdPrefix ?? "claude-agent-integration-");
|
||||
const session = await client.createSession({
|
||||
provider: "claude",
|
||||
...getRealProviderConfig("claude"),
|
||||
cwd,
|
||||
title: params?.title ?? "ClaudeAgentSession integration",
|
||||
modeId: params?.modeId ?? "acceptEdits",
|
||||
model: "haiku",
|
||||
});
|
||||
return { cwd, session };
|
||||
}
|
||||
@@ -175,7 +178,7 @@ describe("ClaudeAgentSession integration", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -233,16 +236,19 @@ describe("ClaudeAgentSession integration", () => {
|
||||
test("supportedModels returns the current abstract Claude SDK model shape", async () => {
|
||||
const claudeBinary = await findExecutable("claude");
|
||||
if (!claudeBinary) throw new Error("claude binary required for this integration test");
|
||||
const query = claudeQuery({
|
||||
prompt: createEmptyPrompt(),
|
||||
options: {
|
||||
cwd: process.cwd(),
|
||||
permissionMode: "plan",
|
||||
includePartialMessages: false,
|
||||
settingSources: ["user", "project"],
|
||||
pathToClaudeCodeExecutable: claudeBinary,
|
||||
const query = claudeQuery(
|
||||
{
|
||||
prompt: createEmptyPrompt(),
|
||||
options: {
|
||||
cwd: process.cwd(),
|
||||
permissionMode: "plan",
|
||||
includePartialMessages: false,
|
||||
settingSources: ["user", "project"],
|
||||
pathToClaudeCodeExecutable: claudeBinary,
|
||||
},
|
||||
},
|
||||
});
|
||||
{ runtimeSettings: getRealProviderRuntimeSettings("claude") },
|
||||
);
|
||||
|
||||
try {
|
||||
const models = await query.supportedModels();
|
||||
|
||||
+20
-14
@@ -6,7 +6,10 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import type { SDKMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { isProviderAvailable } from "../../../daemon-e2e/agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
getRealProviderRuntimeSettings,
|
||||
} from "../../../daemon-e2e/real-provider-test-config.js";
|
||||
import { findExecutable } from "../../../../utils/executable.js";
|
||||
import { claudeQuery } from "./query.js";
|
||||
|
||||
@@ -89,7 +92,7 @@ describe("Claude SDK direct behavior", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -104,20 +107,23 @@ describe("Claude SDK direct behavior", () => {
|
||||
const claudeBinary = await findExecutable("claude");
|
||||
|
||||
// Use same options as the Claude provider
|
||||
const q = claudeQuery({
|
||||
prompt: input,
|
||||
options: {
|
||||
cwd,
|
||||
includePartialMessages: true,
|
||||
permissionMode: "bypassPermissions",
|
||||
...(claudeBinary ? { pathToClaudeCodeExecutable: claudeBinary } : {}),
|
||||
systemPrompt: {
|
||||
type: "preset",
|
||||
preset: "claude_code",
|
||||
const q = claudeQuery(
|
||||
{
|
||||
prompt: input,
|
||||
options: {
|
||||
cwd,
|
||||
includePartialMessages: true,
|
||||
permissionMode: "bypassPermissions",
|
||||
...(claudeBinary ? { pathToClaudeCodeExecutable: claudeBinary } : {}),
|
||||
systemPrompt: {
|
||||
type: "preset",
|
||||
preset: "claude_code",
|
||||
},
|
||||
settingSources: ["user", "project"],
|
||||
},
|
||||
settingSources: ["user", "project"],
|
||||
},
|
||||
});
|
||||
{ runtimeSettings: getRealProviderRuntimeSettings("claude") },
|
||||
);
|
||||
|
||||
try {
|
||||
// Send first message
|
||||
+1
-33
@@ -8,12 +8,8 @@ import { once } from "node:events";
|
||||
|
||||
import { CodexAppServerAgentClient } from "./codex-app-server-agent.js";
|
||||
import { createTestLogger } from "../../../test-utils/test-logger.js";
|
||||
import { agentConfigs } from "../../daemon-e2e/agent-configs.js";
|
||||
import type { AgentStreamEvent } from "../agent-sdk-types.js";
|
||||
|
||||
const CODEX_TEST_MODEL = agentConfigs.codex.model;
|
||||
const CODEX_TEST_THINKING_OPTION_ID = agentConfigs.codex.thinkingOptionId;
|
||||
|
||||
function isCodexInstalled(): boolean {
|
||||
try {
|
||||
const out = execFileSync("which", ["codex"], { encoding: "utf8" }).trim();
|
||||
@@ -217,35 +213,7 @@ function waitForEvent<TEvent extends AgentStreamEvent>(params: {
|
||||
});
|
||||
}
|
||||
|
||||
describe("Codex app-server provider (e2e)", () => {
|
||||
test.runIf(isCodexInstalled())(
|
||||
"lists models and runs a simple prompt",
|
||||
async () => {
|
||||
const client = new CodexAppServerAgentClient(createTestLogger());
|
||||
const cwd = mkdtempSync(path.join(os.tmpdir(), "codex-app-server-e2e-"));
|
||||
const models = await client.listModels({ cwd, force: false });
|
||||
expect(models.some((m) => m.id.includes("gpt-5.1-codex"))).toBe(true);
|
||||
|
||||
const session = await client.createSession({
|
||||
provider: "codex",
|
||||
cwd,
|
||||
modeId: "auto",
|
||||
model: CODEX_TEST_MODEL,
|
||||
thinkingOptionId: CODEX_TEST_THINKING_OPTION_ID,
|
||||
});
|
||||
try {
|
||||
expect(session.features?.some((feature) => feature.id === "plan_mode")).toBe(true);
|
||||
|
||||
const result = await session.run("Say hello in one sentence.");
|
||||
expect(result.finalText.length).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await session.close();
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
30000,
|
||||
);
|
||||
|
||||
describe("Codex app-server provider (local e2e)", () => {
|
||||
test.runIf(isCodexInstalled())(
|
||||
"surfaces request_user_input from the app-server as question permissions and timeline tool calls",
|
||||
async () => {
|
||||
@@ -0,0 +1,47 @@
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { createTestLogger } from "../../../test-utils/test-logger.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
describe("Codex app-server provider (real)", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await canRunRealProvider("codex");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
if (!canRun) {
|
||||
context.skip();
|
||||
}
|
||||
});
|
||||
|
||||
test("lists models and runs a simple prompt", async () => {
|
||||
const client = createRealProviderClient("codex", createTestLogger());
|
||||
const cwd = mkdtempSync(path.join(os.tmpdir(), "codex-app-server-e2e-"));
|
||||
const models = await client.listModels({ cwd, force: false });
|
||||
expect(models.length).toBeGreaterThan(0);
|
||||
|
||||
const session = await client.createSession({
|
||||
...getRealProviderConfig("codex"),
|
||||
cwd,
|
||||
modeId: "auto",
|
||||
});
|
||||
try {
|
||||
expect(session.features?.some((feature) => feature.id === "plan_mode")).toBe(true);
|
||||
|
||||
const result = await session.run("Say hello in one sentence.");
|
||||
expect(result.finalText.length).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await session.close();
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
}
|
||||
}, 30_000);
|
||||
});
|
||||
@@ -3,9 +3,12 @@ import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { CodexAppServerAgentClient } from "./codex-app-server-agent.js";
|
||||
import { createTestLogger } from "../../../test-utils/test-logger.js";
|
||||
import { isProviderAvailable } from "../../daemon-e2e/agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "codex-plan-mode-real-"));
|
||||
@@ -15,7 +18,7 @@ describe("Codex app-server provider (real) plan mode", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("codex");
|
||||
canRun = await canRunRealProvider("codex");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -26,14 +29,13 @@ describe("Codex app-server provider (real) plan mode", () => {
|
||||
|
||||
test("maps gpt-5.4 markdown plans to a plan tool call instead of todo items", async () => {
|
||||
const cwd = tmpCwd();
|
||||
const client = new CodexAppServerAgentClient(createTestLogger());
|
||||
const client = createRealProviderClient("codex", createTestLogger());
|
||||
|
||||
try {
|
||||
const session = await client.createSession({
|
||||
provider: "codex",
|
||||
...getRealProviderConfig("codex"),
|
||||
cwd,
|
||||
modeId: "auto",
|
||||
model: "gpt-5.4",
|
||||
thinkingOptionId: "medium",
|
||||
});
|
||||
|
||||
|
||||
+11
-12
@@ -1,18 +1,21 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import pino from "pino";
|
||||
|
||||
import { isCommandAvailable } from "../../../utils/executable.js";
|
||||
import { OpenCodeAgentClient } from "./opencode-agent.js";
|
||||
import { OpenCodeServerManager } from "./opencode/server-manager.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
const BIG_PICKLE_MODEL = "opencode/big-pickle";
|
||||
const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
const logger = pino({ level: "silent" });
|
||||
|
||||
describe("opencode agent commands contract (real)", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isCommandAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -26,13 +29,11 @@ describe("opencode agent commands contract (real)", () => {
|
||||
});
|
||||
|
||||
test("lists slash commands with the expected contract", async () => {
|
||||
expect(await isCommandAvailable("opencode")).toBe(true);
|
||||
|
||||
const client = new OpenCodeAgentClient(logger);
|
||||
const client = createRealProviderClient("opencode", logger);
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd: process.cwd(),
|
||||
model: BIG_PICKLE_MODEL,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "plan",
|
||||
});
|
||||
|
||||
@@ -57,13 +58,11 @@ describe("opencode agent commands contract (real)", () => {
|
||||
}, 60_000);
|
||||
|
||||
test("executes a slash command without arguments", async () => {
|
||||
expect(await isCommandAvailable("opencode")).toBe(true);
|
||||
|
||||
const client = new OpenCodeAgentClient(logger);
|
||||
const client = createRealProviderClient("opencode", logger);
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd: process.cwd(),
|
||||
model: BIG_PICKLE_MODEL,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "plan",
|
||||
});
|
||||
|
||||
|
||||
+10
-9
@@ -6,21 +6,24 @@ import path from "node:path";
|
||||
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import pino from "pino";
|
||||
|
||||
import { isCommandAvailable } from "../../../utils/executable.js";
|
||||
import { createDaemonTestContext, type DaemonTestContext } from "../../test-utils/index.js";
|
||||
import { OpenCodeAgentClient } from "./opencode-agent.js";
|
||||
import { OpenCodeServerManager } from "./opencode/server-manager.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
const COMMAND_NAME = "paseo-issue-903-big-pickle";
|
||||
const COMMAND_FILE_NAME = `${COMMAND_NAME}.md`;
|
||||
const BIG_PICKLE_MODEL = "opencode/big-pickle";
|
||||
const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
const EXPECTED_RESPONSE = "PASEO_ISSUE_903_BIG_PICKLE_OK";
|
||||
|
||||
describe("opencode custom command Big Pickle E2E (real)", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isCommandAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -59,21 +62,19 @@ describe("opencode custom command Big Pickle E2E (real)", () => {
|
||||
|
||||
ctx = await createDaemonTestContext({
|
||||
logger,
|
||||
agentClients: {
|
||||
opencode: new OpenCodeAgentClient(logger),
|
||||
},
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
});
|
||||
|
||||
const agent = await ctx.client.createAgent({
|
||||
provider: "opencode",
|
||||
cwd: projectDir,
|
||||
model: BIG_PICKLE_MODEL,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "plan",
|
||||
title: "OpenCode issue 903 Big Pickle custom command",
|
||||
});
|
||||
|
||||
expect(agent.provider).toBe("opencode");
|
||||
expect(agent.model).toBe(BIG_PICKLE_MODEL);
|
||||
expect(agent.model).toBe(OPENCODE_REAL_TEST_MODEL);
|
||||
expect(agent.status).toBe("idle");
|
||||
|
||||
const commands = await ctx.client.listCommands(agent.id);
|
||||
|
||||
+9
-7
@@ -2,8 +2,10 @@ import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import pino from "pino";
|
||||
|
||||
import type { AgentStreamEvent } from "../agent-sdk-types.js";
|
||||
import { isCommandAvailable } from "../../../utils/executable.js";
|
||||
import { OpenCodeAgentClient } from "./opencode-agent.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
import { streamSession } from "./test-utils/session-stream-adapter.js";
|
||||
|
||||
function isTerminalEvent(event: AgentStreamEvent): boolean {
|
||||
@@ -24,7 +26,7 @@ describe("opencode agent error handling (real)", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isCommandAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -34,7 +36,7 @@ describe("opencode agent error handling (real)", () => {
|
||||
});
|
||||
|
||||
test("surfaces error for the exact opencode/<bad-model> path used by paseo run", async () => {
|
||||
const client = new OpenCodeAgentClient(pino({ level: "silent" }));
|
||||
const client = createRealProviderClient("opencode", pino({ level: "silent" }));
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd: process.cwd(),
|
||||
@@ -59,7 +61,7 @@ describe("opencode agent error handling (real)", () => {
|
||||
}, 45_000);
|
||||
|
||||
test("surfaces error for unknown provider model (fast path)", async () => {
|
||||
const client = new OpenCodeAgentClient(pino({ level: "silent" }));
|
||||
const client = createRealProviderClient("opencode", pino({ level: "silent" }));
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd: process.cwd(),
|
||||
@@ -84,7 +86,7 @@ describe("opencode agent error handling (real)", () => {
|
||||
}, 30_000);
|
||||
|
||||
test("sequential sessions: second session works after first errors", async () => {
|
||||
const client = new OpenCodeAgentClient(pino({ level: "silent" }));
|
||||
const client = createRealProviderClient("opencode", pino({ level: "silent" }));
|
||||
|
||||
// Session 1: bogus model, will error quickly
|
||||
const s1 = await client.createSession({
|
||||
@@ -121,7 +123,7 @@ describe("opencode agent error handling (real)", () => {
|
||||
test("surfaces error for known provider with nonexistent model (retry path)", async () => {
|
||||
// When the provider is recognized (anthropic) but the model doesn't exist,
|
||||
// OpenCode retries before surfacing the error. This must not hang.
|
||||
const client = new OpenCodeAgentClient(pino({ level: "silent" }));
|
||||
const client = createRealProviderClient("opencode", pino({ level: "silent" }));
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd: process.cwd(),
|
||||
|
||||
+12
-9
@@ -4,19 +4,22 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "./opencode-agent.js";
|
||||
import { OpenCodeServerManager } from "./opencode/server-manager.js";
|
||||
import { isProviderAvailable } from "../../daemon-e2e/agent-configs.js";
|
||||
import type { AgentStreamEvent } from "../agent-sdk-types.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
const BIG_PICKLE_MODEL = "opencode/big-pickle";
|
||||
const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
|
||||
describe("OpenCode assistant message", () => {
|
||||
let canRun = false;
|
||||
const logger = pino({ level: "silent" });
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -29,15 +32,15 @@ describe("OpenCode assistant message", () => {
|
||||
await OpenCodeServerManager.getInstance(logger).shutdown();
|
||||
});
|
||||
|
||||
test("assistant_message appears in live stream with opencode/big-pickle", async () => {
|
||||
test("assistant_message appears in live stream", async () => {
|
||||
const cwd = mkdtempSync(path.join(tmpdir(), "opencode-msg-"));
|
||||
const client = new OpenCodeAgentClient(logger);
|
||||
const client = createRealProviderClient("opencode", logger);
|
||||
|
||||
try {
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd,
|
||||
model: BIG_PICKLE_MODEL,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "build",
|
||||
});
|
||||
|
||||
@@ -53,13 +56,13 @@ describe("OpenCode assistant message", () => {
|
||||
|
||||
test("streamHistory returns assistant_message after a completed turn", async () => {
|
||||
const cwd = mkdtempSync(path.join(tmpdir(), "opencode-history-"));
|
||||
const client = new OpenCodeAgentClient(logger);
|
||||
const client = createRealProviderClient("opencode", logger);
|
||||
|
||||
try {
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd,
|
||||
model: BIG_PICKLE_MODEL,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "build",
|
||||
});
|
||||
|
||||
|
||||
+9
-6
@@ -4,19 +4,22 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "./opencode-agent.js";
|
||||
import { OpenCodeServerManager } from "./opencode/server-manager.js";
|
||||
import { isProviderAvailable } from "../../daemon-e2e/agent-configs.js";
|
||||
import type { AgentStreamEvent } from "../agent-sdk-types.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
} from "../../daemon-e2e/real-provider-test-config.js";
|
||||
|
||||
const BIG_PICKLE_MODEL = "opencode/big-pickle";
|
||||
const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
|
||||
describe("OpenCode reasoning dedup", () => {
|
||||
let canRun = false;
|
||||
const logger = pino({ level: "silent" });
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -31,13 +34,13 @@ describe("OpenCode reasoning dedup", () => {
|
||||
|
||||
test("reasoning content is not duplicated as assistant_message", async () => {
|
||||
const cwd = mkdtempSync(path.join(tmpdir(), "opencode-reasoning-dedup-"));
|
||||
const client = new OpenCodeAgentClient(logger);
|
||||
const client = createRealProviderClient("opencode", logger);
|
||||
|
||||
try {
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd,
|
||||
model: BIG_PICKLE_MODEL,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "build",
|
||||
});
|
||||
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
* Shared agent configurations for e2e tests.
|
||||
* Enables running the same tests against Claude, Codex, and OpenCode providers.
|
||||
*/
|
||||
import { existsSync } from "node:fs";
|
||||
import { join, dirname, resolve } from "node:path";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { homedir } from "node:os";
|
||||
import dotenv from "dotenv";
|
||||
import { isCommandAvailable } from "../../utils/executable.js";
|
||||
|
||||
const serverRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
||||
dotenv.config({ path: resolve(serverRoot, ".env.test"), override: true });
|
||||
@@ -63,7 +60,6 @@ export const agentConfigs = {
|
||||
} as const satisfies Record<string, AgentTestConfig>;
|
||||
|
||||
export type AgentProvider = keyof typeof agentConfigs;
|
||||
const providerAvailabilityCache = new Map<AgentProvider, Promise<boolean>>();
|
||||
|
||||
/**
|
||||
* Get test config for creating an agent with full permissions (no prompts).
|
||||
@@ -93,56 +89,7 @@ export function getAskModeConfig(provider: AgentProvider) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a real provider can run in this environment.
|
||||
* Checks binary availability AND credentials (env vars, OAuth tokens, auth files).
|
||||
*
|
||||
* Credentials are typically loaded from .env.test via the vitest setup file.
|
||||
* This MUST be a function (not a const) so process.env is read at call time,
|
||||
* after dotenv has injected the test credentials.
|
||||
*/
|
||||
export function isProviderAvailable(provider: AgentProvider): Promise<boolean> {
|
||||
const cached = providerAvailabilityCache.get(provider);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const availability = (async (): Promise<boolean> => {
|
||||
switch (provider) {
|
||||
case "claude":
|
||||
const hasClaudeEnvCredentials =
|
||||
Boolean(process.env.CLAUDE_CODE_OAUTH_TOKEN) || Boolean(process.env.ANTHROPIC_API_KEY);
|
||||
return (await isCommandAvailable("claude")) && (!process.env.CI || hasClaudeEnvCredentials);
|
||||
case "codex":
|
||||
return (
|
||||
(await isCommandAvailable("codex")) &&
|
||||
(existsSync(join(homedir(), ".codex", "auth.json")) ||
|
||||
Boolean(process.env.OPENAI_API_KEY))
|
||||
);
|
||||
case "copilot":
|
||||
return await isCommandAvailable("copilot");
|
||||
case "opencode":
|
||||
return await isCommandAvailable("opencode");
|
||||
case "pi":
|
||||
return (
|
||||
(await isCommandAvailable(
|
||||
process.env.PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND ?? "pi",
|
||||
)) &&
|
||||
(Boolean(process.env.OPENAI_API_KEY) ||
|
||||
Boolean(process.env.ANTHROPIC_API_KEY) ||
|
||||
Boolean(process.env.OPENROUTER_API_KEY) ||
|
||||
existsSync(join(homedir(), ".pi", "agent", "auth.json")))
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
providerAvailabilityCache.set(provider, availability);
|
||||
return availability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to run a test for each provider.
|
||||
*/
|
||||
export const allProviders: AgentProvider[] = ["claude", "codex", "copilot", "opencode", "pi"];
|
||||
export const allProviders: AgentProvider[] = ["claude", "codex", "opencode", "pi"];
|
||||
|
||||
+8
-5
@@ -4,10 +4,13 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-claude-autonomous-simple-"));
|
||||
@@ -21,7 +24,7 @@ describe("daemon E2E (real claude) - autonomous wake simple", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -34,7 +37,7 @@ describe("daemon E2E (real claude) - autonomous wake simple", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -48,7 +51,7 @@ describe("daemon E2E (real claude) - autonomous wake simple", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-simple-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
const autonomousWakeToken = `AUTONOMOUS_SIMPLE_${Date.now().toString(36)}`;
|
||||
|
||||
@@ -4,10 +4,13 @@ import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import { WebSocket } from "ws";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-claude-autonomous-wake-"));
|
||||
@@ -427,7 +430,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -440,7 +443,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -454,7 +457,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-abc-a",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
const autonomousWakeToken = `AUTONOMOUS_WAKE_${Date.now().toString(36)}`;
|
||||
@@ -513,7 +516,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -527,7 +530,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-abc-b",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(agent.id, BACKGROUND_TASK_SLEEP_5_PROMPT);
|
||||
@@ -586,7 +589,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -600,7 +603,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-abc-c",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(agent.id, BACKGROUND_TASK_SLEEP_5_PROMPT);
|
||||
@@ -652,7 +655,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -666,7 +669,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-wake-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(
|
||||
@@ -710,7 +713,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -724,7 +727,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-followup-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(
|
||||
@@ -755,7 +758,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const wsUrl = `ws://127.0.0.1:${daemon.port}/ws`;
|
||||
@@ -770,7 +773,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-hang-repro-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
for (let cycle = 0; cycle < 20; cycle += 1) {
|
||||
@@ -858,7 +861,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -872,7 +875,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-background-repro-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(agent.id, BACKGROUND_TASK_SLEEP_5_PROMPT);
|
||||
@@ -930,7 +933,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -944,7 +947,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-autonomous-race-stress-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
for (let cycle = 0; cycle < 20; cycle += 1) {
|
||||
@@ -1016,7 +1019,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -1030,7 +1033,7 @@ describe("daemon E2E (real claude) - autonomous wake from background task", () =
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-transcript-parity-race",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(agent.id, BACKGROUND_TASK_SLEEP_5_PROMPT);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { readdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon, type TestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import {
|
||||
closeRewindSession,
|
||||
fetchTimelineItems,
|
||||
@@ -114,7 +117,7 @@ async function launchClaudeRewindSession(
|
||||
const agent = await harness.client.createAgent({
|
||||
cwd,
|
||||
title,
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
return { agentId: agent.id, cwd };
|
||||
@@ -212,12 +215,17 @@ function assertTimeline(items: AgentTimelineItem[], expectedKept: ClaudeTurnSpec
|
||||
}
|
||||
|
||||
describe("daemon E2E (real claude) - rewind", () => {
|
||||
let canRun = false;
|
||||
let harness: ClaudeRewindHarness;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await canRunRealProvider("claude");
|
||||
if (!canRun) {
|
||||
return;
|
||||
}
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({
|
||||
@@ -235,9 +243,16 @@ describe("daemon E2E (real claude) - rewind", () => {
|
||||
await harness?.daemon.close().catch(() => undefined);
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
if (!canRun) {
|
||||
context.skip();
|
||||
}
|
||||
});
|
||||
|
||||
test.each(MATRIX)(
|
||||
"$name",
|
||||
async (scenario) => {
|
||||
if (!harness) throw new Error("Claude rewind harness was not initialized");
|
||||
const session = await launchClaudeRewindSession(
|
||||
harness,
|
||||
`claude-rewind-${scenario.mode}-${scenario.turnCount}-${scenario.rewindTurn}`,
|
||||
|
||||
@@ -5,10 +5,13 @@ import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-claude-thinking-memory-"));
|
||||
@@ -48,7 +51,7 @@ describe("daemon E2E (real claude) - thinking effort memory", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -61,7 +64,7 @@ describe("daemon E2E (real claude) - thinking effort memory", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -81,7 +84,7 @@ describe("daemon E2E (real claude) - thinking effort memory", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "claude-thinking-memory-real",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
model: model.id,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,11 +4,14 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import { resolveBinaryVersion } from "../agent/providers/diagnostic-utils.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-codex-goal-"));
|
||||
@@ -89,7 +92,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
if (!(await isProviderAvailable("codex"))) {
|
||||
if (!(await canRunRealProvider("codex"))) {
|
||||
return;
|
||||
}
|
||||
const versionOutput = await resolveBinaryVersion("codex");
|
||||
@@ -106,7 +109,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -118,7 +121,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "codex-goal-set",
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
const sentinel = "TX9Q4Z";
|
||||
@@ -160,7 +163,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -172,7 +175,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "codex-goal-mid-turn",
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
// Set an initial goal so pause has something to act on.
|
||||
@@ -237,7 +240,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -249,7 +252,7 @@ describe("daemon E2E (real codex) - /goal command", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "codex-goal-clear",
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
await client.sendMessage(agent.id, "/goal something to be cleared");
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { readdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon, type TestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import {
|
||||
closeRewindSession,
|
||||
fetchTimelineItems,
|
||||
@@ -84,7 +87,7 @@ async function launchCodexRewindSession(
|
||||
const agent = await harness.client.createAgent({
|
||||
cwd,
|
||||
title,
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
return { agentId: agent.id, cwd, scratchPath };
|
||||
@@ -142,12 +145,17 @@ async function askCodexToEditFile(
|
||||
}
|
||||
|
||||
describe("daemon E2E (real codex) - rewind", () => {
|
||||
let canRun = false;
|
||||
let harness: CodexRewindHarness;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await canRunRealProvider("codex");
|
||||
if (!canRun) {
|
||||
return;
|
||||
}
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({
|
||||
@@ -165,7 +173,14 @@ describe("daemon E2E (real codex) - rewind", () => {
|
||||
await harness?.daemon.close().catch(() => undefined);
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
if (!canRun) {
|
||||
context.skip();
|
||||
}
|
||||
});
|
||||
|
||||
test("rewinds a real Codex conversation after a single edit turn while leaving the file edit on disk", async () => {
|
||||
if (!harness) throw new Error("Codex rewind harness was not initialized");
|
||||
const session = await launchCodexRewindSession(harness, "codex-rewind-single-edit-real");
|
||||
|
||||
try {
|
||||
|
||||
@@ -6,8 +6,11 @@ import pino from "pino";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-claude-model-init-"));
|
||||
@@ -17,7 +20,7 @@ describe("daemon E2E (real claude) - model resolution on init", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -30,7 +33,7 @@ describe("daemon E2E (real claude) - model resolution on init", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -45,9 +48,8 @@ describe("daemon E2E (real claude) - model resolution on init", () => {
|
||||
const catalogModelIds = new Set(modelsResult.models.map((m) => m.id));
|
||||
|
||||
const agent = await client.createAgent({
|
||||
provider: "claude",
|
||||
...getRealProviderConfig("claude"),
|
||||
cwd,
|
||||
model: "haiku",
|
||||
title: "model-init-test",
|
||||
});
|
||||
|
||||
|
||||
+3
-4
@@ -6,8 +6,7 @@ import pino from "pino";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-claude-runtime-model-reconcile-"));
|
||||
@@ -17,7 +16,7 @@ describe("daemon E2E (real claude) - runtime model reconciliation", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -30,7 +29,7 @@ describe("daemon E2E (real claude) - runtime model reconciliation", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
|
||||
@@ -4,10 +4,9 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-opencode-custom-"));
|
||||
@@ -19,7 +18,7 @@ async function createHarness(): Promise<{
|
||||
}> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -32,7 +31,7 @@ describe("daemon E2E (real opencode) - custom agent discovery", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -5,12 +5,11 @@ import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import type { PersistedAgentDescriptor } from "../agent/agent-sdk-types.js";
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import type { AgentClient, PersistedAgentDescriptor } from "../agent/agent-sdk-types.js";
|
||||
import { OpenCodeServerManager } from "../agent/providers/opencode/server-manager.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClient } from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
const dir = mkdtempSync(path.join(tmpdir(), "daemon-real-opencode-draft-features-"));
|
||||
@@ -18,7 +17,7 @@ function tmpCwd(): string {
|
||||
}
|
||||
|
||||
async function withConnectedOpenCodeDaemon(
|
||||
provider: OpenCodeAgentClient,
|
||||
provider: AgentClient,
|
||||
run: (context: { client: DaemonClient }) => Promise<void>,
|
||||
): Promise<void> {
|
||||
const logger = pino({ level: "silent" });
|
||||
@@ -41,7 +40,7 @@ async function withConnectedOpenCodeDaemon(
|
||||
}
|
||||
|
||||
async function deletePersistedSessions(
|
||||
provider: OpenCodeAgentClient,
|
||||
provider: AgentClient,
|
||||
sessions: ReadonlyArray<PersistedAgentDescriptor>,
|
||||
): Promise<void> {
|
||||
await Promise.all(
|
||||
@@ -56,7 +55,7 @@ describe("daemon E2E (real opencode) - draft feature discovery", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -67,12 +66,12 @@ describe("daemon E2E (real opencode) - draft feature discovery", () => {
|
||||
|
||||
test("listing draft features does not leave an OpenCode provider session behind", async () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const provider = new OpenCodeAgentClient(logger);
|
||||
const provider = createRealProviderClient("opencode", logger);
|
||||
const cwd = tmpCwd();
|
||||
let after: PersistedAgentDescriptor[] = [];
|
||||
|
||||
try {
|
||||
expect(await provider.listPersistedAgents({ cwd })).toEqual([]);
|
||||
expect(await provider.listPersistedAgents?.({ cwd })).toEqual([]);
|
||||
|
||||
await withConnectedOpenCodeDaemon(provider, async ({ client }) => {
|
||||
const response = await client.listProviderFeatures({
|
||||
@@ -85,7 +84,7 @@ describe("daemon E2E (real opencode) - draft feature discovery", () => {
|
||||
expect(response.features ?? []).toEqual([]);
|
||||
});
|
||||
|
||||
after = await provider.listPersistedAgents({ cwd });
|
||||
after = (await provider.listPersistedAgents?.({ cwd })) ?? [];
|
||||
} finally {
|
||||
await deletePersistedSessions(provider, after);
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
@@ -97,12 +96,12 @@ describe("daemon E2E (real opencode) - draft feature discovery", () => {
|
||||
|
||||
test("listing draft commands does not leave an OpenCode provider session behind", async () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const provider = new OpenCodeAgentClient(logger);
|
||||
const provider = createRealProviderClient("opencode", logger);
|
||||
const cwd = tmpCwd();
|
||||
let after: PersistedAgentDescriptor[] = [];
|
||||
|
||||
try {
|
||||
expect(await provider.listPersistedAgents({ cwd })).toEqual([]);
|
||||
expect(await provider.listPersistedAgents?.({ cwd })).toEqual([]);
|
||||
|
||||
await withConnectedOpenCodeDaemon(provider, async ({ client }) => {
|
||||
const response = await client.listCommands("draft-opencode-agent", {
|
||||
@@ -116,7 +115,7 @@ describe("daemon E2E (real opencode) - draft feature discovery", () => {
|
||||
expect(response.commands.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
after = await provider.listPersistedAgents({ cwd });
|
||||
after = (await provider.listPersistedAgents?.({ cwd })) ?? [];
|
||||
} finally {
|
||||
await deletePersistedSessions(provider, after);
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
|
||||
@@ -6,13 +6,16 @@ import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import type { AgentPersistenceHandle, AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import type { FetchRecentProviderSessionEntry } from "../../client/daemon-client.js";
|
||||
|
||||
const OPENCODE_REAL_TEST_MODEL = "opencode/big-pickle";
|
||||
const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
const OPENCODE_REAL_TEST_TIMEOUT_MS = 180_000;
|
||||
|
||||
function tmpCwd(): string {
|
||||
@@ -25,7 +28,7 @@ async function withConnectedOpenCodeDaemon(
|
||||
): Promise<void> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -112,7 +115,7 @@ describe("daemon E2E (real opencode) - persisted import resume", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -4,12 +4,15 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
const OPENCODE_REAL_TEST_MODEL = "opencode/big-pickle";
|
||||
const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model;
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-opencode-init-prompt-"));
|
||||
@@ -21,7 +24,7 @@ async function createHarness(): Promise<{
|
||||
}> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -34,7 +37,7 @@ describe("daemon E2E (real opencode) - initial prompt wait", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -4,10 +4,9 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-opencode-invalid-mode-"));
|
||||
@@ -19,7 +18,7 @@ async function createHarness(): Promise<{
|
||||
}> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -32,7 +31,7 @@ describe("daemon E2E (real opencode) - invalid mode handling", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -4,10 +4,9 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-opencode-invalid-model-"));
|
||||
@@ -19,7 +18,7 @@ async function createHarness(): Promise<{
|
||||
}> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -32,7 +31,7 @@ describe("daemon E2E (real opencode) - invalid model handling", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -4,10 +4,9 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-opencode-"));
|
||||
@@ -19,7 +18,7 @@ async function createHarness(): Promise<{
|
||||
}> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -32,7 +31,7 @@ describe("daemon E2E (real opencode) - plan mode and clarifying questions", () =
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -2,14 +2,17 @@ import { execFileSync } from "node:child_process";
|
||||
import { readdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import type { AgentLifecycleStatus } from "@getpaseo/protocol/agent-lifecycle";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon, type TestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import {
|
||||
closeRewindSession,
|
||||
fetchTimelineItems,
|
||||
@@ -56,7 +59,7 @@ async function launchOpenCodeRewindSession(
|
||||
const agent = await harness.client.createAgent({
|
||||
cwd,
|
||||
title,
|
||||
...getFullAccessConfig("opencode"),
|
||||
...getRealProviderConfig("opencode"),
|
||||
});
|
||||
|
||||
return { agentId: agent.id, cwd, scratchPath };
|
||||
@@ -212,12 +215,17 @@ async function rewindOpenCode(
|
||||
}
|
||||
|
||||
describe("daemon E2E (real opencode) - rewind", () => {
|
||||
let canRun = false;
|
||||
let harness: OpenCodeRewindHarness;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
if (!canRun) {
|
||||
return;
|
||||
}
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({
|
||||
@@ -235,7 +243,14 @@ describe("daemon E2E (real opencode) - rewind", () => {
|
||||
await harness?.daemon.close().catch(() => undefined);
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
if (!canRun) {
|
||||
context.skip();
|
||||
}
|
||||
});
|
||||
|
||||
test("rewinds conversation and files after a single real OpenCode edit turn", async () => {
|
||||
if (!harness) throw new Error("OpenCode rewind harness was not initialized");
|
||||
const session = await launchOpenCodeRewindSession(harness, "opencode-rewind-single-edit-real");
|
||||
|
||||
try {
|
||||
|
||||
@@ -4,11 +4,10 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient, type WaitForFinishResult } from "../test-utils/daemon-client.js";
|
||||
import { createMessageCollector } from "../test-utils/message-collector.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
import type { AgentPermissionRequest } from "../agent/agent-sdk-types.js";
|
||||
import type { SessionOutboundMessage } from "../messages.js";
|
||||
|
||||
@@ -252,7 +251,7 @@ async function createHarness(): Promise<{
|
||||
}> {
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { opencode: new OpenCodeAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["opencode"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -265,7 +264,7 @@ describe("daemon E2E (real opencode) - send while working and interrupt", () =>
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("opencode");
|
||||
canRun = await canRunRealProvider("opencode");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { readdir } from "node:fs/promises";
|
||||
import pino from "pino";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { PiRpcAgentClient } from "../agent/providers/pi/agent.js";
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon, type TestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { canRunRealProvider, createRealProviderClients } from "./real-provider-test-config.js";
|
||||
import {
|
||||
closeRewindSession,
|
||||
fetchTimelineItems,
|
||||
@@ -103,12 +103,17 @@ async function askPi(
|
||||
}
|
||||
|
||||
describe("daemon E2E (real pi) - rewind", () => {
|
||||
let canRun = false;
|
||||
let harness: PiRewindHarness;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await canRunRealProvider("pi");
|
||||
if (!canRun) {
|
||||
return;
|
||||
}
|
||||
const logger = pino({ level: "silent" });
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { pi: new PiRpcAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["pi"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({
|
||||
@@ -126,7 +131,14 @@ describe("daemon E2E (real pi) - rewind", () => {
|
||||
await harness?.daemon.close().catch(() => undefined);
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
if (!canRun) {
|
||||
context.skip();
|
||||
}
|
||||
});
|
||||
|
||||
test("rewinds a real Pi conversation after a single turn and keeps the session usable", async () => {
|
||||
if (!harness) throw new Error("Pi rewind harness was not initialized");
|
||||
const session = await launchPiRewindSession(harness, "pi-rewind-single-conversation-real");
|
||||
|
||||
try {
|
||||
|
||||
@@ -6,19 +6,24 @@ import { beforeAll, beforeEach, expect, test } from "vitest";
|
||||
import pino from "pino";
|
||||
|
||||
import type {
|
||||
AgentClient,
|
||||
AgentPersistenceHandle,
|
||||
AgentStreamEvent,
|
||||
AgentTimelineItem,
|
||||
} from "../agent/agent-sdk-types.js";
|
||||
import { PiRpcAgentClient } from "../agent/providers/pi/agent.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
process.env.PASEO_SUPERVISED = "0";
|
||||
|
||||
const PI_TEST_TIMEOUT_MS = 240_000;
|
||||
const PI_REAL_TEST_MODEL = "openrouter/google/gemini-2.5-flash-lite";
|
||||
const PI_REAL_TEST_MODEL = getRealProviderConfig("pi").model;
|
||||
|
||||
type ToolCallItem = Extract<AgentTimelineItem, { type: "tool_call" }>;
|
||||
|
||||
@@ -26,14 +31,14 @@ function tmpCwd(prefix = "daemon-real-pi-"): string {
|
||||
return mkdtempSync(path.join(tmpdir(), prefix));
|
||||
}
|
||||
|
||||
function createPiClient(): PiRpcAgentClient {
|
||||
return new PiRpcAgentClient({ logger: pino({ level: "silent" }) });
|
||||
function createPiClient(): AgentClient {
|
||||
return createRealProviderClient("pi", pino({ level: "silent" }));
|
||||
}
|
||||
|
||||
function createPiToolDaemon() {
|
||||
const logger = pino({ level: "silent" });
|
||||
return createTestPaseoDaemon({
|
||||
agentClients: { pi: new PiRpcAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["pi"], logger),
|
||||
logger,
|
||||
});
|
||||
}
|
||||
@@ -87,7 +92,7 @@ async function withConnectedPiDaemon(
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("pi");
|
||||
canRun = await canRunRealProvider("pi");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import type { Logger } from "pino";
|
||||
|
||||
import type { AgentClient, AgentProvider, AgentSessionConfig } from "../agent/agent-sdk-types.js";
|
||||
import type { ProviderRuntimeSettings } from "../agent/provider-launch-config.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { PiRpcAgentClient } from "../agent/providers/pi/agent.js";
|
||||
import { isCommandAvailable } from "../../utils/executable.js";
|
||||
|
||||
export const realProviders = ["claude", "codex", "opencode", "pi"] as const;
|
||||
export type RealProvider = (typeof realProviders)[number];
|
||||
export type RealProviderConfig = Pick<
|
||||
AgentSessionConfig,
|
||||
"provider" | "model" | "modeId" | "thinkingOptionId"
|
||||
>;
|
||||
|
||||
const OPENROUTER_BASE_URL = "https://openrouter.ai/api";
|
||||
const OPENROUTER_OPENAI_BASE_URL = "https://openrouter.ai/api";
|
||||
const CLAUDE_REAL_TEST_MODEL = "haiku";
|
||||
const CODEX_REAL_TEST_MODEL = "~openai/gpt-latest";
|
||||
const OPENCODE_REAL_TEST_MODEL = "openrouter/google/gemini-2.5-flash-lite";
|
||||
const PI_REAL_TEST_MODEL = "openrouter/google/gemini-2.5-flash-lite";
|
||||
|
||||
const availabilityCache = new Map<RealProvider, Promise<boolean>>();
|
||||
|
||||
export function getRealProviderConfig(provider: RealProvider): RealProviderConfig {
|
||||
switch (provider) {
|
||||
case "claude":
|
||||
return {
|
||||
provider,
|
||||
model: CLAUDE_REAL_TEST_MODEL,
|
||||
modeId: "bypassPermissions",
|
||||
};
|
||||
case "codex":
|
||||
return {
|
||||
provider,
|
||||
model: CODEX_REAL_TEST_MODEL,
|
||||
thinkingOptionId: "low",
|
||||
modeId: "full-access",
|
||||
};
|
||||
case "opencode":
|
||||
return {
|
||||
provider,
|
||||
model: OPENCODE_REAL_TEST_MODEL,
|
||||
modeId: "build",
|
||||
};
|
||||
case "pi":
|
||||
return {
|
||||
provider,
|
||||
model: PI_REAL_TEST_MODEL,
|
||||
thinkingOptionId: "medium",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getRealProviderRuntimeSettings(provider: RealProvider): ProviderRuntimeSettings {
|
||||
const apiKey = getOpenRouterApiKey();
|
||||
switch (provider) {
|
||||
case "claude":
|
||||
return {
|
||||
env: {
|
||||
OPENROUTER_API_KEY: apiKey,
|
||||
ANTHROPIC_BASE_URL: OPENROUTER_BASE_URL,
|
||||
ANTHROPIC_AUTH_TOKEN: apiKey,
|
||||
ANTHROPIC_API_KEY: "",
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: "~anthropic/claude-haiku-latest",
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: "~anthropic/claude-sonnet-latest",
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: "~anthropic/claude-opus-latest",
|
||||
CLAUDE_CODE_SUBAGENT_MODEL: "~anthropic/claude-haiku-latest",
|
||||
},
|
||||
};
|
||||
case "codex":
|
||||
return {
|
||||
env: {
|
||||
OPENROUTER_API_KEY: apiKey,
|
||||
OPENAI_API_KEY: apiKey,
|
||||
OPENAI_BASE_URL: OPENROUTER_OPENAI_BASE_URL,
|
||||
},
|
||||
};
|
||||
case "opencode": {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "paseo-real-opencode-"));
|
||||
return {
|
||||
env: {
|
||||
OPENROUTER_API_KEY: apiKey,
|
||||
OPENCODE_DISABLE_AUTO_UPDATE: "1",
|
||||
XDG_CONFIG_HOME: path.join(root, "config"),
|
||||
XDG_DATA_HOME: path.join(root, "data"),
|
||||
XDG_CACHE_HOME: path.join(root, "cache"),
|
||||
},
|
||||
};
|
||||
}
|
||||
case "pi":
|
||||
return {
|
||||
env: {
|
||||
OPENROUTER_API_KEY: apiKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createRealProviderClient(provider: RealProvider, logger: Logger): AgentClient {
|
||||
const runtimeSettings = getRealProviderRuntimeSettings(provider);
|
||||
switch (provider) {
|
||||
case "claude":
|
||||
return new ClaudeAgentClient({ logger, runtimeSettings });
|
||||
case "codex":
|
||||
return new CodexAppServerAgentClient(logger, runtimeSettings, {
|
||||
customProvider: {
|
||||
id: "codex-openrouter",
|
||||
label: "Codex OpenRouter",
|
||||
extends: "codex",
|
||||
},
|
||||
});
|
||||
case "opencode":
|
||||
return new OpenCodeAgentClient(logger, runtimeSettings);
|
||||
case "pi":
|
||||
return new PiRpcAgentClient({ logger, runtimeSettings });
|
||||
}
|
||||
}
|
||||
|
||||
export function createRealProviderClients(
|
||||
providers: readonly RealProvider[],
|
||||
logger: Logger,
|
||||
): Partial<Record<AgentProvider, AgentClient>> {
|
||||
return Object.fromEntries(
|
||||
providers.map((provider) => [provider, createRealProviderClient(provider, logger)]),
|
||||
);
|
||||
}
|
||||
|
||||
export function canRunRealProvider(provider: RealProvider): Promise<boolean> {
|
||||
const cached = availabilityCache.get(provider);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const availability = (async () => {
|
||||
if (!getOpenRouterApiKeyOrNull()) {
|
||||
return false;
|
||||
}
|
||||
return await isCommandAvailable(getProviderBinary(provider));
|
||||
})();
|
||||
|
||||
availabilityCache.set(provider, availability);
|
||||
return availability;
|
||||
}
|
||||
|
||||
function getOpenRouterApiKey(): string {
|
||||
const apiKey = getOpenRouterApiKeyOrNull();
|
||||
if (!apiKey) {
|
||||
throw new Error("OPENROUTER_API_KEY is required for real provider tests");
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
function getOpenRouterApiKeyOrNull(): string | null {
|
||||
const value = process.env.OPENROUTER_API_KEY?.trim();
|
||||
return value && value.length > 0 ? value : null;
|
||||
}
|
||||
|
||||
function getProviderBinary(provider: RealProvider): string {
|
||||
if (provider === "pi") {
|
||||
return process.env.PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND ?? "pi";
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
+8
-5
@@ -6,8 +6,11 @@ import pino from "pino";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-rewind-dedupe-real-claude-"));
|
||||
@@ -17,7 +20,7 @@ describe("daemon E2E (real claude) - rewind user message dedupe", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -30,7 +33,7 @@ describe("daemon E2E (real claude) - rewind user message dedupe", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -44,7 +47,7 @@ describe("daemon E2E (real claude) - rewind user message dedupe", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "rewind-user-message-dedupe-real-claude",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await client.sendMessage(agent.id, "Reply with exactly: READY");
|
||||
|
||||
@@ -6,8 +6,11 @@ import pino from "pino";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import { createMessageCollector } from "../test-utils/message-collector.js";
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import type { SessionOutboundMessage } from "../messages.js";
|
||||
@@ -171,7 +174,7 @@ describe("daemon E2E (real claude) - send message during tool call", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -184,7 +187,7 @@ describe("daemon E2E (real claude) - send message during tool call", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -197,7 +200,7 @@ describe("daemon E2E (real claude) - send message during tool call", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "tool-interrupt-repro",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
const collector = createMessageCollector(client);
|
||||
|
||||
@@ -4,13 +4,16 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import type { AgentTimelineItem } from "../agent/agent-sdk-types.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createMessageCollector } from "../test-utils/message-collector.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import type { SessionOutboundMessage } from "../messages.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
return mkdtempSync(path.join(tmpdir(), "daemon-real-codex-tool-interrupt-"));
|
||||
@@ -143,7 +146,7 @@ describe("daemon E2E (real codex) - send message during tool call", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("codex");
|
||||
canRun = await canRunRealProvider("codex");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -156,7 +159,7 @@ describe("daemon E2E (real codex) - send message during tool call", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -169,7 +172,7 @@ describe("daemon E2E (real codex) - send message during tool call", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "codex-tool-interrupt-repro",
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
collector = createMessageCollector(client);
|
||||
@@ -232,7 +235,7 @@ describe("daemon E2E (real codex) - send message during tool call", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -245,7 +248,7 @@ describe("daemon E2E (real codex) - send message during tool call", () => {
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: "codex-quick-follow-up-repro",
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
collector = createMessageCollector(client);
|
||||
|
||||
+8
-5
@@ -6,8 +6,11 @@ import pino from "pino";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import { applyAgentInputProcessingTransition } from "./send-while-running-stuck-test-utils.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
@@ -18,7 +21,7 @@ describe("daemon E2E (real claude) - send while running recovery", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("claude");
|
||||
canRun = await canRunRealProvider("claude");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -31,7 +34,7 @@ describe("daemon E2E (real claude) - send while running recovery", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { claude: new ClaudeAgentClient({ logger }) },
|
||||
agentClients: createRealProviderClients(["claude"], logger),
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -47,7 +50,7 @@ describe("daemon E2E (real claude) - send while running recovery", () => {
|
||||
const agent = await primary.createAgent({
|
||||
cwd,
|
||||
title: "stuck-repro-real-claude",
|
||||
...getFullAccessConfig("claude"),
|
||||
...getRealProviderConfig("claude"),
|
||||
});
|
||||
|
||||
await primary.sendMessage(
|
||||
|
||||
@@ -6,8 +6,11 @@ import pino from "pino";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
} from "./real-provider-test-config.js";
|
||||
import { applyAgentInputProcessingTransition } from "./send-while-running-stuck-test-utils.js";
|
||||
|
||||
function tmpCwd(): string {
|
||||
@@ -18,7 +21,7 @@ describe("daemon E2E (real codex) - send while running recovery", () => {
|
||||
let canRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable("codex");
|
||||
canRun = await canRunRealProvider("codex");
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -31,7 +34,7 @@ describe("daemon E2E (real codex) - send while running recovery", () => {
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: { codex: new CodexAppServerAgentClient(logger) },
|
||||
agentClients: createRealProviderClients(["codex"], logger),
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -47,7 +50,7 @@ describe("daemon E2E (real codex) - send while running recovery", () => {
|
||||
const agent = await primary.createAgent({
|
||||
cwd,
|
||||
title: "stuck-repro-real-codex",
|
||||
...getFullAccessConfig("codex"),
|
||||
...getRealProviderConfig("codex"),
|
||||
});
|
||||
|
||||
await primary.sendMessage(agent.id, "Run: sleep 5");
|
||||
|
||||
@@ -4,18 +4,15 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import type { AgentClient } from "../agent/agent-sdk-types.js";
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import {
|
||||
allProviders,
|
||||
getFullAccessConfig,
|
||||
isProviderAvailable,
|
||||
type AgentProvider,
|
||||
} from "./agent-configs.js";
|
||||
canRunRealProvider,
|
||||
createRealProviderClients,
|
||||
getRealProviderConfig,
|
||||
realProviders,
|
||||
type RealProvider,
|
||||
} from "./real-provider-test-config.js";
|
||||
|
||||
type UiAction =
|
||||
| {
|
||||
@@ -109,7 +106,7 @@ function longRunningPrompt(sleepSeconds: number, token: string): string {
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function buildNormalUsageScenario(provider: AgentProvider, seed: number): UiScenario {
|
||||
function buildNormalUsageScenario(provider: RealProvider, seed: number): UiScenario {
|
||||
const token1 = `UI_${provider}_${seed}_A`;
|
||||
const token2 = `UI_${provider}_${seed}_B`;
|
||||
return {
|
||||
@@ -146,7 +143,7 @@ function buildNormalUsageScenario(provider: AgentProvider, seed: number): UiScen
|
||||
}
|
||||
|
||||
function buildOverlapScenario(
|
||||
provider: AgentProvider,
|
||||
provider: RealProvider,
|
||||
seed: number,
|
||||
firstPick: "first" | "last",
|
||||
): UiScenario {
|
||||
@@ -444,21 +441,11 @@ async function runUiScenario(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function createRealAgentClient(provider: AgentProvider, logger: pino.Logger): AgentClient {
|
||||
if (provider === "claude") {
|
||||
return new ClaudeAgentClient({ logger });
|
||||
}
|
||||
if (provider === "codex") {
|
||||
return new CodexAppServerAgentClient(logger);
|
||||
}
|
||||
return new OpenCodeAgentClient(logger);
|
||||
}
|
||||
|
||||
describe.each(allProviders)("daemon E2E (real %s) - UI action stress", (provider) => {
|
||||
describe.each(realProviders)("daemon E2E (real %s) - UI action stress", (provider) => {
|
||||
let shouldRun = false;
|
||||
|
||||
beforeAll(async () => {
|
||||
shouldRun = await isProviderAvailable(provider);
|
||||
shouldRun = await canRunRealProvider(provider);
|
||||
});
|
||||
|
||||
beforeEach((context) => {
|
||||
@@ -471,9 +458,7 @@ describe.each(allProviders)("daemon E2E (real %s) - UI action stress", (provider
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: {
|
||||
[provider]: createRealAgentClient(provider, logger),
|
||||
} as Partial<Record<AgentProvider, AgentClient>>,
|
||||
agentClients: createRealProviderClients([provider], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -486,7 +471,7 @@ describe.each(allProviders)("daemon E2E (real %s) - UI action stress", (provider
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: `uist-n-${provider}`,
|
||||
...getFullAccessConfig(provider),
|
||||
...getRealProviderConfig(provider),
|
||||
});
|
||||
|
||||
await runUiScenario({
|
||||
@@ -507,9 +492,7 @@ describe.each(allProviders)("daemon E2E (real %s) - UI action stress", (provider
|
||||
const logger = pino({ level: "silent" });
|
||||
const cwd = tmpCwd();
|
||||
const daemon = await createTestPaseoDaemon({
|
||||
agentClients: {
|
||||
[provider]: createRealAgentClient(provider, logger),
|
||||
} as Partial<Record<AgentProvider, AgentClient>>,
|
||||
agentClients: createRealProviderClients([provider], logger),
|
||||
logger,
|
||||
});
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
@@ -528,7 +511,7 @@ describe.each(allProviders)("daemon E2E (real %s) - UI action stress", (provider
|
||||
const agent = await client.createAgent({
|
||||
cwd,
|
||||
title: `uist-o-${provider}`,
|
||||
...getFullAccessConfig(provider),
|
||||
...getRealProviderConfig(provider),
|
||||
});
|
||||
|
||||
await runUiScenario({
|
||||
|
||||
@@ -4,65 +4,54 @@ import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { ClaudeAgentClient } from "../agent/providers/claude/agent.js";
|
||||
import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js";
|
||||
import { OpenCodeAgentClient } from "../agent/providers/opencode-agent.js";
|
||||
import { PiRpcAgentClient } from "../agent/providers/pi/agent.js";
|
||||
import type {
|
||||
AgentClient,
|
||||
AgentProvider,
|
||||
AgentSessionConfig,
|
||||
AgentStreamEvent,
|
||||
} from "../agent/agent-sdk-types.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
import { createTestPaseoDaemon, type TestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { getFullAccessConfig, isProviderAvailable } from "./agent-configs.js";
|
||||
import {
|
||||
canRunRealProvider,
|
||||
createRealProviderClient,
|
||||
getRealProviderConfig,
|
||||
type RealProvider,
|
||||
} from "./real-provider-test-config.js";
|
||||
import { fetchTimelineItems } from "./test-utils/rewind-helpers.js";
|
||||
|
||||
type ContractProvider = Extract<AgentProvider, "claude" | "codex" | "opencode" | "pi">;
|
||||
type ContractProvider = Extract<AgentProvider, RealProvider>;
|
||||
|
||||
interface ProviderContractCase {
|
||||
provider: ContractProvider;
|
||||
title: string;
|
||||
timeoutMs: number;
|
||||
createClient: (logger: pino.Logger) => AgentClient;
|
||||
createConfig: () => AgentSessionConfig;
|
||||
}
|
||||
|
||||
const PI_REAL_TEST_MODEL = "openrouter/google/gemini-2.5-flash-lite";
|
||||
|
||||
const CONTRACT_CASES: ProviderContractCase[] = [
|
||||
{
|
||||
provider: "claude",
|
||||
title: "Claude",
|
||||
timeoutMs: 180_000,
|
||||
createClient: (logger) => new ClaudeAgentClient({ logger }),
|
||||
createConfig: () => getFullAccessConfig("claude"),
|
||||
createConfig: () => getRealProviderConfig("claude"),
|
||||
},
|
||||
{
|
||||
provider: "codex",
|
||||
title: "Codex",
|
||||
timeoutMs: 180_000,
|
||||
createClient: (logger) => new CodexAppServerAgentClient(logger),
|
||||
createConfig: () => getFullAccessConfig("codex"),
|
||||
createConfig: () => getRealProviderConfig("codex"),
|
||||
},
|
||||
{
|
||||
provider: "opencode",
|
||||
title: "OpenCode",
|
||||
timeoutMs: 180_000,
|
||||
createClient: (logger) => new OpenCodeAgentClient(logger),
|
||||
createConfig: () => getFullAccessConfig("opencode"),
|
||||
createConfig: () => getRealProviderConfig("opencode"),
|
||||
},
|
||||
{
|
||||
provider: "pi",
|
||||
title: "Pi",
|
||||
timeoutMs: 240_000,
|
||||
createClient: (logger) => new PiRpcAgentClient({ logger }),
|
||||
createConfig: () => ({
|
||||
provider: "pi",
|
||||
model: PI_REAL_TEST_MODEL,
|
||||
thinkingOptionId: "medium",
|
||||
}),
|
||||
createConfig: () => getRealProviderConfig("pi"),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -94,7 +83,7 @@ describe.each(CONTRACT_CASES)("daemon E2E (real $provider) - user_message contra
|
||||
let cwd: string | null = null;
|
||||
|
||||
beforeAll(async () => {
|
||||
canRun = await isProviderAvailable(entry.provider);
|
||||
canRun = await canRunRealProvider(entry.provider);
|
||||
});
|
||||
|
||||
beforeEach(async (context) => {
|
||||
@@ -106,7 +95,7 @@ describe.each(CONTRACT_CASES)("daemon E2E (real $provider) - user_message contra
|
||||
cwd = tmpCwd(entry.provider);
|
||||
const logger = pino({ level: "silent" });
|
||||
daemon = await createTestPaseoDaemon({
|
||||
agentClients: { [entry.provider]: entry.createClient(logger) },
|
||||
agentClients: { [entry.provider]: createRealProviderClient(entry.provider, logger) },
|
||||
logger,
|
||||
});
|
||||
client = new DaemonClient({
|
||||
|
||||
Reference in New Issue
Block a user