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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/gh-github-pull-request-opened.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
- run: poe-code github-workflows github-pull-request-opened --yes
- run: poe-code github-workflows github-pull-request-opened --yes --model "${POE_CODE_REVIEW_MODEL:-gpt-5.4}"
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
Expand All @@ -129,3 +129,4 @@ jobs:
PR_TITLE: ${{ inputs.PR_TITLE }}
PR_AUTHOR: ${{ inputs.PR_AUTHOR }}
GITHUB_REPOSITORY: ${{ inputs.GITHUB_REPOSITORY }}
POE_CODE_REVIEW_MODEL: ${{ vars.POE_CODE_REVIEW_MODEL }}
3 changes: 2 additions & 1 deletion .github/workflows/gh-github-pull-request-synchronized.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ jobs:
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
- run: poe-code github-workflows github-pull-request-synchronized --yes
- run: poe-code github-workflows github-pull-request-synchronized --yes --model "${POE_CODE_REVIEW_MODEL:-gpt-5.4}"
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ inputs.PR_NUMBER }}
GITHUB_REPOSITORY: ${{ inputs.GITHUB_REPOSITORY }}
POE_CODE_REVIEW_MODEL: ${{ vars.POE_CODE_REVIEW_MODEL }}
5 changes: 3 additions & 2 deletions .github/workflows/poe-code-github-pull-request-opened.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: 'GitHub: Pull Request Opened'
on:
pull_request:
types: [opened, ready_for_review]
types: [ opened, ready_for_review ]

jobs:
guard:
Expand Down Expand Up @@ -103,7 +103,7 @@ jobs:
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
- run: poe-code github-workflows github-pull-request-opened --yes
- run: poe-code github-workflows github-pull-request-opened --yes --model "${POE_CODE_REVIEW_MODEL:-gpt-5.4}"
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
Expand All @@ -112,3 +112,4 @@ jobs:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
GITHUB_REPOSITORY: ${{ github.repository }}
POE_CODE_REVIEW_MODEL: ${{ vars.POE_CODE_REVIEW_MODEL }}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: 'GitHub: Pull Request Synchronized'
on:
pull_request:
types: [synchronize]
types: [ synchronize ]

jobs:
guard:
Expand Down Expand Up @@ -103,10 +103,11 @@ jobs:
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
- run: poe-code github-workflows github-pull-request-synchronized --yes
- run: poe-code github-workflows github-pull-request-synchronized --yes --model "${POE_CODE_REVIEW_MODEL:-gpt-5.4}"
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_REPOSITORY: ${{ github.repository }}
POE_CODE_REVIEW_MODEL: ${{ vars.POE_CODE_REVIEW_MODEL }}
7 changes: 4 additions & 3 deletions e2e/claude-code.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { useContainer } from '@poe-code/e2e-test-runner';
import { useContainer, shellQuote } from '@poe-code/e2e-test-runner';
import { resolveE2eModel } from './runtime-models.js';

describe('claude-code', () => {
const container = useContainer({ testName: 'claude-code' });
Expand All @@ -19,12 +20,12 @@ describe('claude-code', () => {
expect(config).not.toHaveProperty('env.ANTHROPIC_CUSTOM_HEADERS');
expect(config).toHaveProperty('env.ANTHROPIC_BASE_URL');

const testResult = await container.exec('poe-code test claude-code');
const testResult = await container.exec(`poe-code test claude-code --model ${shellQuote(resolveE2eModel('claude-code'))}`);
expect(testResult).toSucceedWith('Tested Claude Code.');
});

it('test --isolated', async () => {
const result = await container.exec('poe-code test claude-code --isolated');
const result = await container.exec(`poe-code test claude-code --isolated --model ${shellQuote(resolveE2eModel('claude-code'))}`);
expect(result).toSucceedWith('Tested Claude Code.');
});
});
14 changes: 14 additions & 0 deletions e2e/mcp-fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fileURLToPath } from 'node:url';
import type { Backend } from '@poe-code/e2e-test-runner';

export function resolveMcpFixtureCommand(backend: Backend): { command: string; args: string[] } {
if (backend === 'podman') {
return { command: 'tiny-stdio-mcp-test-server', args: ['serve', 'word-of-the-day'] };
}
// npm ci runs before build, so the workspace bin link may not exist.
// Host backends can invoke the built fixture without a link or executable bit.
return {
command: process.execPath,
args: [fileURLToPath(new URL('../packages/tiny-stdio-mcp-test-server/dist/cli.js', import.meta.url)), 'serve', 'word-of-the-day'],
};
}
22 changes: 15 additions & 7 deletions e2e/mcp-tool.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { useContainer, shellQuote } from '@poe-code/e2e-test-runner';
import { useContainer, shellQuote, resolveBackend } from '@poe-code/e2e-test-runner';
import { resolveMcpFixtureCommand } from './mcp-fixture.js';
import { resolveE2eModel, resolveE2eModelEnvironment, registerKimiFixtureModel } from './runtime-models.js';

interface AgentMcpSpawnTest {
name: string;
name: Parameters<typeof resolveE2eModel>[0];
expectSpawnSuccess: boolean;
spawnArgs?: string[];
}
Expand Down Expand Up @@ -33,10 +35,7 @@ const agents: AgentMcpSpawnTest[] = [
];

const mcpConfig = shellQuote(JSON.stringify({
'tiny-stdio-mcp-test-server': {
command: 'tiny-stdio-mcp-test-server',
args: ['serve', 'word-of-the-day'],
},
'tiny-stdio-mcp-test-server': resolveMcpFixtureCommand(resolveBackend()),
}));

describe.each(agents)('spawn --mcp-config: $name', ({ name, expectSpawnSuccess, spawnArgs }) => {
Expand All @@ -51,11 +50,20 @@ describe.each(agents)('spawn --mcp-config: $name', ({ name, expectSpawnSuccess,
const configResult = await container.exec(`poe-code configure ${name} --yes`);
expect(configResult).toHaveExitCode(0);

const model = resolveE2eModel(name);
if (name === 'kimi') {
const configPath = `${container.home}/.kimi/config.toml`;
const contextSize = Number(process.env.POE_CODE_E2E_KIMI_CONTEXT_SIZE ?? 131072);
await container.writeFile(configPath, registerKimiFixtureModel(await container.readFile(configPath), model, contextSize));
}

const prompt = 'Call the word_of_the_day tool and return only the exact tool output.';
const extraArgs = spawnArgs
? ` -- ${spawnArgs.map((arg) => shellQuote(arg)).join(' ')}`
: '';
const command = `poe-code spawn --mode yolo --mcp-config ${mcpConfig} ${name} ${shellQuote(prompt)}${extraArgs}`;
const modelEnv = Object.entries(resolveE2eModelEnvironment(name, model))
.map(([key, value]) => `${key}=${shellQuote(value)}`);
const command = [...modelEnv, `poe-code spawn --mode yolo --model ${shellQuote(model)} --mcp-config ${mcpConfig} ${name} ${shellQuote(prompt)}${extraArgs}`].join(' ');
const spawnResult = await container.exec(command);

if (!expectSpawnSuccess) {
Expand Down
34 changes: 34 additions & 0 deletions e2e/runtime-models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { parse, stringify, type TomlTable } from "smol-toml";

const models = {
"claude-code": { env: "POE_CODE_E2E_CLAUDE_CODE_MODEL", model: "claude-sonnet-4.6" },
codex: { env: "POE_CODE_E2E_CODEX_MODEL", model: "gpt-5.4" },
opencode: { env: "POE_CODE_E2E_OPENCODE_MODEL", model: "gpt-5.4" },
kimi: { env: "POE_CODE_E2E_KIMI_MODEL", model: "gpt-5.4" },
goose: { env: "POE_CODE_E2E_GOOSE_MODEL", model: "gpt-5.4" },
} as const;

export function resolveE2eModel(agent: keyof typeof models, env: NodeJS.ProcessEnv = process.env): string {
const selected = models[agent];
return env[selected.env]?.trim() || selected.model;
}

export function registerKimiFixtureModel(source: string, model: string, maxContextSize = 131072): string {
const config = parse(source);
const providers = config.providers as TomlTable | undefined;
if (!providers?.poe) throw new Error("Configure the poe provider before registering the Kimi fixture model");
const configured = (config.models ?? {}) as TomlTable;
if (Object.hasOwn(configured, model)) return source;
if (!Number.isSafeInteger(maxContextSize) || maxContextSize <= 0) throw new Error("Kimi fixture context size must be a positive integer");
return stringify({ ...config, models: { ...configured,
// Keep this fixture below the selected GPT-5.4 context limit; native user
// aliases above retain their own context configuration unchanged.
[model]: { provider: "poe", model, max_context_size: maxContextSize },
} });
}

export function resolveE2eModelEnvironment(agent: keyof typeof models, model: string): Record<string, string> {
// Goose v1.50.0 ACP reads the model from config/environment before session/new;
// its ACP command has no --model flag. Keep this scoped to the live fixture.
return agent === "goose" ? { GOOSE_MODEL: model } : {};
Comment thread
kamilio marked this conversation as resolved.
}
4 changes: 2 additions & 2 deletions e2e/tiny-mcp-server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ describe("tiny MCP server", () => {
});

it("serves prompts and resources over a real HTTP listener", async () => {
const server = createHttpServer({ name: "e2e-http", version: "1.0.0" })
.prompt({ name: "hello" }, () => ({ messages: [{ role: "user", content: { type: "text", text: "hello" } }] }))
const server = createHttpServer({ name: "e2e-http", version: "1.0.0" });
server.prompt({ name: "hello" }, () => ({ messages: [{ role: "user", content: { type: "text", text: "hello" } }] }))
.resource({ uri: "memory://hello", name: "hello" }, () => ({ contents: [{ uri: "memory://hello", text: "hello" }] }));
const handle = await server.listenHttp({ port: 0 });
const client = new Client({ name: "e2e-http-client", version: "1.0.0" });
Expand Down
7 changes: 7 additions & 0 deletions packages/agent-spawn/src/agent-spawn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ describe("stripModelNamespace", () => {
// === build-spawn-args.test.ts ===

describe("buildSpawnArgs", () => {
it("forwards an explicit Kimi model alias without choosing a default", () => {
const selected = buildSpawnArgs("kimi", { prompt: "test", model: "custom/model-alias", mode: "yolo" });
expect(selected.args).toContain("--model");
expect(selected.args[selected.args.indexOf("--model") + 1]).toBe("custom/model-alias");
expect(buildSpawnArgs("kimi", { prompt: "test", mode: "yolo" }).args).not.toContain("--model");
});

it("throws error if agent ID cannot be resolved", () => {
expect(() => buildSpawnArgs("unknown", { prompt: "test" })).toThrow(/Unknown agent/);
});
Expand Down
4 changes: 3 additions & 1 deletion packages/agent-spawn/src/configs/kimi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export const kimiSpawnConfig: CliSpawnConfig = {
// (no `{ event, ... }` field), so it needs the Kimi adapter (not "native").
adapter: "kimi",
promptFlag: "-p",
modelStripProviderPrefix: true,
modelFlag: "--model",
// Kimi selects a local models-table alias, which may itself contain slashes.
modelStripProviderPrefix: false,
defaultArgs: ["--print", "--output-format", "stream-json"],
mcpArgs: serializeJsonMcpArgs,
modes: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: 'GitHub: Pull Request Opened'
on:
pull_request:
types: [opened, ready_for_review]
types: [ opened, ready_for_review ]

jobs:
guard:
Expand Down Expand Up @@ -104,7 +104,7 @@ jobs:
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
- run: poe-code github-workflows github-pull-request-opened --yes
- run: poe-code github-workflows github-pull-request-opened --yes --model "${POE_CODE_REVIEW_MODEL:-gpt-5.4}"
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
Expand All @@ -113,3 +113,4 @@ jobs:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
GITHUB_REPOSITORY: ${{ github.repository }}
POE_CODE_REVIEW_MODEL: ${{ vars.POE_CODE_REVIEW_MODEL }}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: 'GitHub: Pull Request Synchronized'
on:
pull_request:
types: [synchronize]
types: [ synchronize ]

jobs:
guard:
Expand Down Expand Up @@ -104,10 +104,11 @@ jobs:
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
- run: poe-code github-workflows github-pull-request-synchronized --yes
- run: poe-code github-workflows github-pull-request-synchronized --yes --model "${POE_CODE_REVIEW_MODEL:-gpt-5.4}"
env:
POE_CODE_STDERR_LOGS: "1"
POE_API_KEY: ${{ secrets.POE_API_KEY }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_REPOSITORY: ${{ github.repository }}
POE_CODE_REVIEW_MODEL: ${{ vars.POE_CODE_REVIEW_MODEL }}
2 changes: 2 additions & 0 deletions packages/safe-bash-playground/src/engine/kernel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe("real safe-bash browser kernel", () => {
const activeWorkers = new Set<{ terminate(): void }>();

beforeAll(async () => {
vi.stubGlobal("navigator", { language: "en-US" });
vi.stubGlobal(
"Worker",
class extends EventTarget {
Expand All @@ -28,6 +29,7 @@ describe("real safe-bash browser kernel", () => {
const worker = new NodeWorker(
`
const { parentPort } = require('node:worker_threads');
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: { language: 'en-US' } });
globalThis.addEventListener = (event, handler) => parentPort.on(event, data => handler({ data }));
globalThis.postMessage = (value, transfer) => parentPort.postMessage(value, transfer);
${code}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { describe, expect, it, vi } from "vitest";
import { afterAll, describe, expect, it, vi } from "vitest";
import { createMemoryFileSystem, FsError } from "./engine/index.js";
import type { FileSystem } from "./engine/index.js";
import { decodeError, encodeError, hostFileSystem, remoteFileSystem } from "./execution-filesystem.js";

vi.hoisted(() => vi.stubGlobal("navigator", { language: "en-US" }));
afterAll(() => vi.unstubAllGlobals());

vi.mock("./engine/index.js", async () => {
const { buildBrowserEngine } = await import("./engine/build-plugin.mjs");
const built = await buildBrowserEngine();
Expand Down
2 changes: 2 additions & 0 deletions packages/safe-bash-playground/src/execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { ExecutionMessage } from "./execution-protocol.js";
import { browserWorkerFixture } from "../test/browser-worker.js";
import { setTimeout as delay } from "node:timers/promises";

vi.hoisted(() => vi.stubGlobal("navigator", { language: "en-US" }));

vi.mock("virtual:safe-bash-worker-sources", async () => {
const { buildBrowserEngine } = await import("./engine/build-plugin.mjs");
return { sources: (await buildBrowserEngine({ workersOnly: true })).workerSources };
Expand Down
2 changes: 2 additions & 0 deletions packages/safe-bash-playground/src/session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { createSession, SESSION_LIMITS } from "./session.js";
import { sampleFiles } from "./samples.js";
import { browserWorkerFixture } from "../test/browser-worker.js";

vi.hoisted(() => vi.stubGlobal("navigator", { language: "en-US" }));

vi.mock("virtual:safe-bash-worker-sources", async () => {
const { buildBrowserEngine } = await import("./engine/build-plugin.mjs");
return { sources: (await buildBrowserEngine({ workersOnly: true })).workerSources };
Expand Down
1 change: 1 addition & 0 deletions packages/safe-bash-playground/test/browser-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function browserWorkerFixture(executionSource: string) {
this.worker = source.then((code) => {
const worker = new NodeWorker(`
const { parentPort } = require("node:worker_threads");
Object.defineProperty(globalThis, "navigator", { configurable: true, value: { language: "en-US" } });
globalThis.addEventListener = (event, handler) => parentPort.on(event, data => handler({ data }));
globalThis.postMessage = value => parentPort.postMessage(value);
(() => { ${code} })();
Expand Down
Loading
Loading