mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 02:20:38 +09:00
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:
parent
4e3da49116
commit
d56618e9fe
11 changed files with 300 additions and 65 deletions
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);
|
||||
});
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@ export type {
|
|||
AdapterExecutionContext,
|
||||
AdapterExecutionResult,
|
||||
AdapterInvocationMeta,
|
||||
AdapterSessionCodec,
|
||||
UsageSummary,
|
||||
AdapterAgent,
|
||||
AdapterRuntime,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ServerAdapterModule } from "./types.js";
|
||||
import { execute as claudeExecute } from "@paperclip/adapter-claude-local/server";
|
||||
import { execute as claudeExecute, sessionCodec as claudeSessionCodec } from "@paperclip/adapter-claude-local/server";
|
||||
import { agentConfigurationDoc as claudeAgentConfigurationDoc, models as claudeModels } from "@paperclip/adapter-claude-local";
|
||||
import { execute as codexExecute } from "@paperclip/adapter-codex-local/server";
|
||||
import { execute as codexExecute, sessionCodec as codexSessionCodec } from "@paperclip/adapter-codex-local/server";
|
||||
import { agentConfigurationDoc as codexAgentConfigurationDoc, models as codexModels } from "@paperclip/adapter-codex-local";
|
||||
import { processAdapter } from "./process/index.js";
|
||||
import { httpAdapter } from "./http/index.js";
|
||||
|
|
@ -9,6 +9,7 @@ import { httpAdapter } from "./http/index.js";
|
|||
const claudeLocalAdapter: ServerAdapterModule = {
|
||||
type: "claude_local",
|
||||
execute: claudeExecute,
|
||||
sessionCodec: claudeSessionCodec,
|
||||
models: claudeModels,
|
||||
supportsLocalAgentJwt: true,
|
||||
agentConfigurationDoc: claudeAgentConfigurationDoc,
|
||||
|
|
@ -17,6 +18,7 @@ const claudeLocalAdapter: ServerAdapterModule = {
|
|||
const codexLocalAdapter: ServerAdapterModule = {
|
||||
type: "codex_local",
|
||||
execute: codexExecute,
|
||||
sessionCodec: codexSessionCodec,
|
||||
models: codexModels,
|
||||
supportsLocalAgentJwt: true,
|
||||
agentConfigurationDoc: codexAgentConfigurationDoc,
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ export type {
|
|||
AdapterExecutionResult,
|
||||
AdapterInvocationMeta,
|
||||
AdapterExecutionContext,
|
||||
AdapterSessionCodec,
|
||||
ServerAdapterModule,
|
||||
} from "@paperclip/adapter-utils";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue