diff --git a/server/src/__tests__/heartbeat-workspace-session.test.ts b/server/src/__tests__/heartbeat-workspace-session.test.ts index 22aba591..f36698c4 100644 --- a/server/src/__tests__/heartbeat-workspace-session.test.ts +++ b/server/src/__tests__/heartbeat-workspace-session.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from "vitest"; import { resolveDefaultAgentWorkspaceDir } from "../home-paths.js"; -import { resolveRuntimeSessionParamsForWorkspace, type ResolvedWorkspaceForRun } from "../services/heartbeat.ts"; +import { + resolveRuntimeSessionParamsForWorkspace, + shouldResetTaskSessionForWake, + type ResolvedWorkspaceForRun, +} from "../services/heartbeat.ts"; function buildResolvedWorkspace(overrides: Partial = {}): ResolvedWorkspaceForRun { return { @@ -83,3 +87,17 @@ describe("resolveRuntimeSessionParamsForWorkspace", () => { expect(result.warning).toBeNull(); }); }); + +describe("shouldResetTaskSessionForWake", () => { + it("resets session context on assignment wake", () => { + expect(shouldResetTaskSessionForWake({ wakeReason: "issue_assigned" })).toBe(true); + }); + + it("does not reset for comment wakes", () => { + expect(shouldResetTaskSessionForWake({ wakeReason: "issue_comment_mentioned" })).toBe(false); + }); + + it("does not reset when wake reason is missing", () => { + expect(shouldResetTaskSessionForWake({})).toBe(false); + }); +}); diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index b7396d7f..88155bbc 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -195,6 +195,13 @@ function deriveTaskKey( ); } +export function shouldResetTaskSessionForWake( + contextSnapshot: Record | null | undefined, +) { + const wakeReason = readNonEmptyString(contextSnapshot?.wakeReason); + return wakeReason === "issue_assigned"; +} + function deriveCommentId( contextSnapshot: Record | null | undefined, payload: Record | null | undefined, @@ -1058,8 +1065,10 @@ export function heartbeatService(db: Db) { const taskSession = taskKey ? await getTaskSession(agent.companyId, agent.id, agent.adapterType, taskKey) : null; + const resetTaskSession = shouldResetTaskSessionForWake(context); + const taskSessionForRun = resetTaskSession ? null : taskSession; const previousSessionParams = normalizeSessionParams( - sessionCodec.deserialize(taskSession?.sessionParamsJson ?? null), + sessionCodec.deserialize(taskSessionForRun?.sessionParamsJson ?? null), ); const resolvedWorkspace = await resolveWorkspaceForRun( agent, @@ -1076,6 +1085,11 @@ export function heartbeatService(db: Db) { const runtimeWorkspaceWarnings = [ ...resolvedWorkspace.warnings, ...(runtimeSessionResolution.warning ? [runtimeSessionResolution.warning] : []), + ...(resetTaskSession && taskKey + ? [ + `Skipping saved session resume for task "${taskKey}" because wake reason is issue_assigned.`, + ] + : []), ]; context.paperclipWorkspace = { cwd: resolvedWorkspace.cwd, @@ -1091,7 +1105,7 @@ export function heartbeatService(db: Db) { } const runtimeSessionFallback = taskKey ? null : runtime.sessionId; const previousSessionDisplayId = truncateDisplayId( - taskSession?.sessionDisplayId ?? + taskSessionForRun?.sessionDisplayId ?? (sessionCodec.getDisplayId ? sessionCodec.getDisplayId(runtimeSessionParams) : null) ?? readNonEmptyString(runtimeSessionParams?.sessionId) ?? runtimeSessionFallback,