mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
fix(runtime): omit Codex refresh handoffs from backups (#20614)
## Thinking Path > - Managed ACPX Codex retains its final refresh after provider exit so Core can save it. > - Shutdown saves a checkpoint before Core removes the live credential handoff. > - Checkpoint profiles must exclude the handoff while retaining resumable session history. ## Linked Issues or Issue Description Follow-up to #20580 and its credential-backup review finding. Refs #20457. ## What Changed - Exclude `auth-refresh.json` and `auth-refresh.json.tmp` from the shared Codex checkpoint profile, covering native and ACPX homes. - Extend real checkpoint-copy coverage to ACPX Codex, both handoff filenames, and native Codex. Keep the live files and session history intact. ## Verification - A real ACPX checkpoint-copy test failed before the exclusion fix; Grok passed. - Five targeted real-copy/profile tests pass, including after applying the change to current master. - `git diff --check` and local secret/PII diff review pass. - Full regression coverage runs in GitHub CI at the maintainer's request. No live provider credentials were used. ## Risks - Two entries added to an existing exclusion list; no runtime lifecycle, schema, persisted identity, or session format changes. Saved sessions remain readable. Existing backup restoration uses the same exclusion profile. - Provider credential cleanup still follows final writeback; excluding a backup entry does not delete the live handoff. ## Model Used - OpenAI GPT-6 through Codex, with tool-assisted implementation and verification. The runtime does not expose a more specific model ID or context-window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
86cc3e3a11
commit
19e938415a
2 files changed
+15
-6
No files matched your search
@@ -3163,6 +3163,8 @@ describe("native harness persistence profiles", () => {
|
||||
".tmp",
|
||||
"auth.json",
|
||||
"config.toml",
|
||||
"auth-refresh.json",
|
||||
"auth-refresh.json.tmp",
|
||||
".remote-plugin-install-staging",
|
||||
"cache/remote_plugin_catalog",
|
||||
"plugins/.remote-plugin-install-staging",
|
||||
@@ -3209,6 +3211,8 @@ describe("native harness persistence profiles", () => {
|
||||
`acpx/${sessionDirectory}/codex-home/.tmp`,
|
||||
`acpx/${sessionDirectory}/codex-home/auth.json`,
|
||||
`acpx/${sessionDirectory}/codex-home/config.toml`,
|
||||
`acpx/${sessionDirectory}/codex-home/auth-refresh.json`,
|
||||
`acpx/${sessionDirectory}/codex-home/auth-refresh.json.tmp`,
|
||||
`acpx/${sessionDirectory}/codex-home/.remote-plugin-install-staging`,
|
||||
`acpx/${sessionDirectory}/codex-home/cache/remote_plugin_catalog`,
|
||||
`acpx/${sessionDirectory}/codex-home/plugins/.remote-plugin-install-staging`,
|
||||
@@ -3920,22 +3924,23 @@ describe("split durable provider checkpoint identity", () => {
|
||||
});
|
||||
|
||||
describe("remote provider checkpoint snapshots", () => {
|
||||
it("excludes Grok credentials and diagnostic logs from real checkpoint copies while retaining sessions", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "grok-checkpoint-"));
|
||||
it.each(["codex", "grok"] as const)("excludes %s credentials and handoffs from real checkpoint copies while retaining sessions", async provider => {
|
||||
const root = await mkdtemp(join(tmpdir(), `${provider}-checkpoint-`));
|
||||
try {
|
||||
const source = join(root, "source");
|
||||
const target = join(root, "backup");
|
||||
const sessionDirectory = acpxRuntimeSessionDirectoryName("session");
|
||||
const relativeHome = join("acpx", sessionDirectory, "grok-home");
|
||||
const relativeHome = join("acpx", sessionDirectory, `${provider}-home`);
|
||||
const home = join(source, relativeHome);
|
||||
await mkdir(join(home, "logs"), { recursive: true });
|
||||
await mkdir(join(home, "sessions"));
|
||||
await writeFile(join(home, "logs", "unified.jsonl"), "AUTH_SENTINEL");
|
||||
await writeFile(join(home, "auth.json"), "AUTH_SENTINEL");
|
||||
await writeFile(join(home, "auth-refresh.json"), "AUTH_SENTINEL");
|
||||
await writeFile(join(home, "auth-refresh.json.tmp"), "AUTH_SENTINEL");
|
||||
await writeFile(join(home, "sessions", "session.json"), "resume-state");
|
||||
const profile = resolveNativeHarnessPersistenceProfile({
|
||||
provider: { kind: "acpx", agent: "grok" },
|
||||
provider: { kind: "acpx", agent: provider },
|
||||
session: { normalizedSessionId: "session", driverKind: "acpx_runtime" },
|
||||
} as unknown as NativeExecutionInputV1);
|
||||
const execute = async ({ command, args }: { command: string; args: string[] }) => ({
|
||||
@@ -3948,7 +3953,7 @@ describe("remote provider checkpoint snapshots", () => {
|
||||
await syncRemoteRunnerDirectoryOut({ runner: { execute, syncOut } as never,
|
||||
sourcePath: source, targetPath: target, mode: 0o700,
|
||||
excludeEntries: profile.directories.find(directory => directory.name === "acpx")!.excludeEntries });
|
||||
for (const entry of ["auth.json", "auth-refresh.json", "logs"])
|
||||
for (const entry of ["auth.json", "auth-refresh.json", "auth-refresh.json.tmp", ...(provider === "grok" ? ["logs"] : [])])
|
||||
await expect(access(join(target, relativeHome, entry))).rejects.toMatchObject({ code: "ENOENT" });
|
||||
expect(await readFile(join(target, relativeHome, "sessions", "session.json"), "utf8")).toBe("resume-state");
|
||||
expect(await readFile(join(home, "logs", "unified.jsonl"), "utf8")).toBe("AUTH_SENTINEL");
|
||||
@@ -4054,6 +4059,8 @@ describe("remote provider checkpoint snapshots", () => {
|
||||
const source = join(root, "codex-home");
|
||||
const target = join(root, "backup");
|
||||
const files: Record<string, string> = {
|
||||
"auth-refresh.json": "AUTH_SENTINEL",
|
||||
"auth-refresh.json.tmp": "AUTH_SENTINEL",
|
||||
"sessions/2026/10/20/rollout.jsonl": "resume-state",
|
||||
"state_5.sqlite": "state",
|
||||
"cache/codex_apps_tools/tools.json": "apps",
|
||||
@@ -4086,7 +4093,7 @@ describe("remote provider checkpoint snapshots", () => {
|
||||
for (const kept of ["sessions/2026/10/20/rollout.jsonl", "state_5.sqlite", "cache/codex_apps_tools/tools.json",
|
||||
"plugins/data/sites/data.json", "plugins/cache/openai-bundled/browser/1.0.0/plugin.json"])
|
||||
expect(await readFile(join(target, kept), "utf8")).toBe(files[kept]);
|
||||
for (const omitted of ["cache/remote_plugin_catalog", "plugins/cache/openai-curated-remote",
|
||||
for (const omitted of ["auth-refresh.json", "auth-refresh.json.tmp", "cache/remote_plugin_catalog", "plugins/cache/openai-curated-remote",
|
||||
"plugins/cache/workspace-shared-with-me", "plugins/.remote-plugin-install-staging", ".remote-plugin-install-staging"])
|
||||
await expect(access(join(target, omitted))).rejects.toMatchObject({ code: "ENOENT" });
|
||||
expect(await readFile(join(source, "plugins/cache/openai-curated-remote/sites/1.0.2/plugin.json"), "utf8")).toBe("remote");
|
||||
|
||||
@@ -558,6 +558,8 @@ const CODEX_HOME_REDOWNLOADABLE_ENTRIES = [
|
||||
] as const;
|
||||
const CODEX_HOME_CHECKPOINT_EXCLUDED_ENTRIES = [
|
||||
...CODEX_HOME_NON_PERSISTENT_ENTRIES,
|
||||
"auth-refresh.json",
|
||||
"auth-refresh.json.tmp",
|
||||
...CODEX_HOME_REDOWNLOADABLE_ENTRIES,
|
||||
] as const;
|
||||
const OPENCODE_RUNTIME_CHECKPOINT_EXCLUDED_ENTRIES = [
|
||||
|
||||
Reference in new issue
Block a user