mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 01:32:40 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60135cba41 |
@@ -0,0 +1,91 @@
|
||||
// Opt-in diagnostic for #3905. Run from the repository root:
|
||||
// node --import tsx packages/server/scripts/reproduce-codex-history-string-limit.mts 520
|
||||
// Requires Codex on PATH and approximately 2 GiB of available memory.
|
||||
import { spawn } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { mkdtemp, mkdir, open, rm } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
import { CodexAppServerAgentSession } from "../src/server/agent/providers/codex-app-server-agent.js";
|
||||
|
||||
const megabytes = Number(process.argv[2] ?? "520");
|
||||
if (!Number.isSafeInteger(megabytes) || megabytes < 1 || megabytes > 600) {
|
||||
throw new Error("Expected an integer size between 1 and 600 MiB");
|
||||
}
|
||||
const root = await mkdtemp(path.join(process.cwd(), ".codex-history-repro-"));
|
||||
const home = path.join(root, "codex-home");
|
||||
const now = new Date();
|
||||
const timestamp = now.toISOString();
|
||||
const threadId = randomUUID();
|
||||
const turnId = randomUUID();
|
||||
const sessions = path.join(home, "sessions", ...timestamp.slice(0, 10).split("-"));
|
||||
await mkdir(sessions, { recursive: true });
|
||||
const rollout = await open(
|
||||
path.join(sessions, `rollout-${timestamp.slice(0, 19).replaceAll(":", "-")}-${threadId}.jsonl`),
|
||||
"w",
|
||||
);
|
||||
async function write(type: string, payload: unknown) {
|
||||
await rollout.write(`${JSON.stringify({ timestamp, type, payload })}\n`);
|
||||
}
|
||||
await write("session_meta", {
|
||||
id: threadId,
|
||||
timestamp,
|
||||
cwd: root,
|
||||
originator: "codex_cli_rs",
|
||||
cli_version: "0.159.0",
|
||||
source: "cli",
|
||||
model_provider: "openai",
|
||||
history_mode: "legacy",
|
||||
});
|
||||
await write("event_msg", { type: "task_started", turn_id: turnId, model_context_window: 200000 });
|
||||
await write("event_msg", {
|
||||
type: "user_message",
|
||||
message: "Synthetic oversized history",
|
||||
images: [],
|
||||
local_images: [],
|
||||
text_elements: [],
|
||||
});
|
||||
for (let index = 0; index < megabytes; index++) {
|
||||
await write("event_msg", {
|
||||
type: "agent_message",
|
||||
message: "x".repeat(1024 * 1024),
|
||||
phase: "commentary",
|
||||
});
|
||||
}
|
||||
await write("event_msg", { type: "task_complete", turn_id: turnId, last_agent_message: "done" });
|
||||
await rollout.close();
|
||||
console.log(`Opening synthetic ${megabytes} MiB history through the real Codex app-server`);
|
||||
const session = new CodexAppServerAgentSession(
|
||||
{ provider: "codex", cwd: root },
|
||||
{ sessionId: threadId },
|
||||
pino({ level: "silent" }),
|
||||
async () =>
|
||||
spawn(
|
||||
"codex",
|
||||
["app-server", "--disable", "plugins", "--disable", "apps", "--disable", "remote_plugin"],
|
||||
{
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: { ...process.env, CODEX_HOME: home },
|
||||
},
|
||||
),
|
||||
{ codexHome: home },
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
"history",
|
||||
);
|
||||
try {
|
||||
await session.connect();
|
||||
let count = 0;
|
||||
for await (const event of session.streamHistory()) if (event.type === "timeline") count++;
|
||||
console.log(`History loaded: ${count} timeline rows`);
|
||||
} catch (error) {
|
||||
// A recoverable session error is acceptable for the crash-isolation assertion.
|
||||
console.log(
|
||||
`History rejected without crashing: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
} finally {
|
||||
await session.close();
|
||||
await rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
// Diagnostic for #3905; intentionally red on the affected baseline. Real Codex,
|
||||
// synthetic on-disk history, no model turns. Opt in because the oversized
|
||||
// case needs approximately 2 GiB of memory and over 520 MiB of temporary storage.
|
||||
const enabled = process.env.PASEO_CODEX_HISTORY_STRING_LIMIT_REPRO === "1";
|
||||
const script = fileURLToPath(
|
||||
new URL("../../../../scripts/reproduce-codex-history-string-limit.mts", import.meta.url),
|
||||
);
|
||||
|
||||
test.runIf(enabled).each([2, 520])(
|
||||
"opening %i MiB of native Codex history does not crash its hosting process",
|
||||
async (megabytes) => {
|
||||
const root = await mkdtemp(path.join(process.cwd(), ".codex-history-test-"));
|
||||
try {
|
||||
const result = await new Promise<{ code: number; stdout: string; stderr: string }>(
|
||||
(resolve) => {
|
||||
const child = execFile(
|
||||
process.execPath,
|
||||
["--import", "tsx", script, String(megabytes)],
|
||||
{
|
||||
cwd: root,
|
||||
detached: true,
|
||||
timeout: 60_000,
|
||||
maxBuffer: 1024 * 1024,
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
try {
|
||||
if (child.pid) process.kill(-child.pid, "SIGKILL");
|
||||
} catch (killError) {
|
||||
if ((killError as NodeJS.ErrnoException).code !== "ESRCH") throw killError;
|
||||
}
|
||||
resolve({ code: error ? Number(error.code ?? 1) : 0, stdout, stderr });
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
expect(result.code, result.stderr).toBe(0);
|
||||
expect(result.stdout).toMatch(/History loaded:|History rejected without crashing:/);
|
||||
if (megabytes === 2) expect(result.stdout).toContain("History loaded: 3 timeline rows");
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
||||
}
|
||||
},
|
||||
90_000,
|
||||
);
|
||||
Reference in New Issue
Block a user