Add adapter session codecs with cwd-aware resume and unknown-session retry
Introduce AdapterSessionCodec interface for structured session serialization, deserialization, and display ID extraction. Implement codecs for claude_local and codex_local adapters with cwd validation — sessions saved for a different working directory are not resumed. Both adapters now return sessionParams and sessionDisplayId alongside legacy sessionId. Add isCodexUnknownSessionError detection and automatic retry with fresh session for codex_local (matching existing claude_local behavior). Inject approval context env vars (PAPERCLIP_APPROVAL_ID, PAPERCLIP_APPROVAL_STATUS, PAPERCLIP_LINKED_ISSUE_IDS) into adapter environments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
64
server/src/__tests__/adapter-session-codecs.test.ts
Normal file
64
server/src/__tests__/adapter-session-codecs.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sessionCodec as claudeSessionCodec } from "@paperclip/adapter-claude-local/server";
|
||||
import { sessionCodec as codexSessionCodec, isCodexUnknownSessionError } from "@paperclip/adapter-codex-local/server";
|
||||
|
||||
describe("adapter session codecs", () => {
|
||||
it("normalizes claude session params with cwd", () => {
|
||||
const parsed = claudeSessionCodec.deserialize({
|
||||
session_id: "claude-session-1",
|
||||
folder: "/tmp/workspace",
|
||||
});
|
||||
expect(parsed).toEqual({
|
||||
sessionId: "claude-session-1",
|
||||
cwd: "/tmp/workspace",
|
||||
});
|
||||
|
||||
const serialized = claudeSessionCodec.serialize(parsed);
|
||||
expect(serialized).toEqual({
|
||||
sessionId: "claude-session-1",
|
||||
cwd: "/tmp/workspace",
|
||||
});
|
||||
expect(claudeSessionCodec.getDisplayId?.(serialized ?? null)).toBe("claude-session-1");
|
||||
});
|
||||
|
||||
it("normalizes codex session params with cwd", () => {
|
||||
const parsed = codexSessionCodec.deserialize({
|
||||
sessionId: "codex-session-1",
|
||||
cwd: "/tmp/codex",
|
||||
});
|
||||
expect(parsed).toEqual({
|
||||
sessionId: "codex-session-1",
|
||||
cwd: "/tmp/codex",
|
||||
});
|
||||
|
||||
const serialized = codexSessionCodec.serialize(parsed);
|
||||
expect(serialized).toEqual({
|
||||
sessionId: "codex-session-1",
|
||||
cwd: "/tmp/codex",
|
||||
});
|
||||
expect(codexSessionCodec.getDisplayId?.(serialized ?? null)).toBe("codex-session-1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("codex resume recovery detection", () => {
|
||||
it("detects unknown session errors from codex output", () => {
|
||||
expect(
|
||||
isCodexUnknownSessionError(
|
||||
'{"type":"error","message":"Unknown session id abc"}',
|
||||
"",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isCodexUnknownSessionError(
|
||||
"",
|
||||
"thread 123 not found",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isCodexUnknownSessionError(
|
||||
'{"type":"result","ok":true}',
|
||||
"",
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user