Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions apps/code/src/main/services/agent/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -709,14 +709,17 @@ When creating pull requests, add the following footer at the end of the PR descr
let configOptions: SessionConfigOption[] | undefined;
let agentSessionId: string;

// Claude-specific: hydrate session JSONL from PostHog before resuming.
// If hydration finds no conversation to restore, skip the resume and
// fall through to creating a new session. This avoids a doomed
// unstable_resumeSession that would fail with "Resource not found"
if (isReconnect && config.sessionId) {
const existingSessionId = config.sessionId;

// Claude-specific: hydrate session JSONL from PostHog before resuming
if (adapter !== "codex") {
const posthogAPI = agent.getPosthogAPI();
if (posthogAPI) {
await hydrateSessionJsonl({
const hasSession = await hydrateSessionJsonl({
sessionId: existingSessionId,
cwd: repoPath,
taskId,
Expand All @@ -725,8 +728,19 @@ When creating pull requests, add the following footer at the end of the PR descr
posthogAPI,
log,
});
if (!hasSession) {
log.info(
"No session JSONL to resume, creating new session instead",
{ taskId, taskRunId },
);
config.sessionId = undefined;
}
}
}
}

if (isReconnect && config.sessionId) {
const existingSessionId = config.sessionId;

// Both adapters implement unstable_resumeSession:
// - Claude: delegates to SDK's resumeSession with JSONL hydration
Expand Down
12 changes: 7 additions & 5 deletions packages/agent/src/adapters/claude/session/jsonl-hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ export async function hydrateSessionJsonl(params: {
permissionMode?: string;
posthogAPI: PostHogAPIClient;
log: HydrationLog;
}): Promise<void> {
}): Promise<boolean> {
const { posthogAPI, log } = params;

try {
Expand All @@ -506,21 +506,21 @@ export async function hydrateSessionJsonl(params: {
log.info("Local JSONL exists, skipping S3 hydration", {
sessionId: params.sessionId,
});
return;
return true;
} catch {
// File doesn't exist, proceed with hydration
}

const taskRun = await posthogAPI.getTaskRun(params.taskId, params.runId);
if (!taskRun.log_url) {
log.info("No log URL, skipping JSONL hydration");
return;
return false;
}

const entries = await posthogAPI.fetchTaskRunLogs(taskRun);
if (entries.length === 0) {
log.info("No S3 log entries, skipping JSONL hydration");
return;
return false;
}

const entryCounts: Record<string, number> = {};
Expand All @@ -545,7 +545,7 @@ export async function hydrateSessionJsonl(params: {
const allTurns = rebuildConversation(entries);
if (allTurns.length === 0) {
log.info("No conversation in S3 logs, skipping JSONL hydration");
return;
return false;
}

const maxTokens = supports1MContext(params.model ?? "")
Expand Down Expand Up @@ -577,10 +577,12 @@ export async function hydrateSessionJsonl(params: {
turns: conversation.length,
lines: jsonlLines.length,
});
return true;
} catch (err) {
log.warn("Failed to hydrate session JSONL, continuing", {
sessionId: params.sessionId,
error: err instanceof Error ? err.message : String(err),
});
return false;
}
}
Loading