feat: archive Codex sessions as private cognition evidence

This commit is contained in:
冰朔 2026-08-05 12:42:16 +08:00
commit 7d85d79e61
8 changed files with 798 additions and 0 deletions

View file

@ -0,0 +1,70 @@
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import {
archiveSessions,
redactVisibleText,
restoreManifest,
verifyManifest,
} from "./archive.mjs";
test("visible text is redacted without erasing ordinary corrections", () => {
const namedSecret = `${["tok", "en"].join("")}=abc123456789`;
const result = redactVisibleText(`不是这个方向。${namedSecret} user@example.com 10.0.0.8 /Users/demo/work`);
assert.match(result.text, /不是这个方向/);
assert.doesNotMatch(result.text, /abc123456789|user@example\.com|10\.0\.0\.8/);
assert.equal(result.counts.named_secret, 1);
assert.equal(result.counts.email, 1);
assert.equal(result.counts.ip_address, 1);
});
test("archive is content addressed, excludes reasoning and restores exact bytes", async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "codex-session-archive-"));
const source = path.join(root, "source");
const destination = path.join(root, "archive");
const restoreRoot = path.join(root, "restore");
const session = path.join(source, "2026", "08", "05", "rollout-test.jsonl");
await fs.promises.mkdir(path.dirname(session), { recursive: true });
await fs.promises.mkdir(destination, { recursive: true });
await fs.promises.writeFile(path.join(destination, "._generated-sidecar"), "regenerable");
const rows = [
{ timestamp: "2026-08-05T00:00:00Z", type: "session_meta", payload: { id: "thread-1", cwd: "/private/work" } },
{ timestamp: "2026-08-05T00:00:01Z", type: "event_msg", payload: { type: "user_message", message: `不是这个方向,${["pass", "word"].join("")}=fixture-value` } },
{ timestamp: "2026-08-05T00:00:02Z", type: "response_item", payload: { type: "reasoning", summary: [{ text: "hidden chain" }], encrypted_content: "ciphertext" } },
{ timestamp: "2026-08-05T00:00:03Z", type: "event_msg", payload: { type: "agent_message", message: "已按边界修正。" } },
{ timestamp: "2026-08-05T00:00:04Z", type: "response_item", payload: { type: "function_call", name: "exec_command", arguments: "{\"secret\":\"do-not-index\"}" } },
];
await fs.promises.writeFile(session, `${rows.map((row) => JSON.stringify(row)).join("\n")}\n`);
const archived = await archiveSessions({ source, destination, stableSeconds: 0, now: new Date("2026-08-05T01:00:00Z") });
assert.equal(archived.manifest.totals.archived_verified, 1);
assert.equal(archived.manifest.totals.pending_live, 0);
assert.equal(archived.appleDoubleRemoved, 1);
assert.equal(fs.existsSync(path.join(destination, "._generated-sidecar")), false);
const entry = archived.manifest.entries[0];
const indexText = await fs.promises.readFile(path.join(destination, entry.private_index), "utf8");
assert.match(indexText, /不是这个方向/);
assert.match(indexText, /REDACTED:NAMED_SECRET/);
assert.match(indexText, /exec_command/);
assert.doesNotMatch(indexText, /hidden chain|ciphertext|do-not-index|fixture-value/);
const verified = await verifyManifest({ destination, manifest: archived.manifestPath });
assert.equal(verified.ok, true);
const dryRun = await restoreManifest({ destination, manifest: archived.manifestPath, restoreRoot, execute: false });
assert.equal(dryRun.planned_files, 1);
assert.equal(fs.existsSync(restoreRoot), false);
await restoreManifest({ destination, manifest: archived.manifestPath, restoreRoot, execute: true });
assert.equal(await fs.promises.readFile(path.join(restoreRoot, entry.relative_path), "utf8"), await fs.promises.readFile(session, "utf8"));
});
test("recent live sessions are recorded as pending instead of misreported archived", async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "codex-session-live-"));
const source = path.join(root, "source");
const destination = path.join(root, "archive");
await fs.promises.mkdir(source);
await fs.promises.writeFile(path.join(source, "live.jsonl"), "{}\n");
const archived = await archiveSessions({ source, destination, stableSeconds: 3600 });
assert.equal(archived.manifest.totals.archived_verified, 0);
assert.equal(archived.manifest.totals.pending_live, 1);
assert.equal(archived.manifest.entries[0].state, "pending_live");
});