fix: delete transcripts on role ordering reset

This commit is contained in:
Peter Steinberger
2026-01-16 09:20:11 +00:00
parent 949fa1051f
commit 6fa437613b
2 changed files with 31 additions and 3 deletions

View File

@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import type { SessionEntry } from "../../config/sessions.js";
import * as sessions from "../../config/sessions.js";
import type { TypingMode } from "../../config/types.js";
import type { TemplateContext } from "../templating.js";
import type { GetReplyOptions } from "../types.js";
@@ -127,11 +128,14 @@ describe("runReplyAgent typing (heartbeat)", () => {
try {
const sessionId = "session";
const storePath = path.join(stateDir, "sessions", "sessions.json");
const sessionEntry = { sessionId, updatedAt: Date.now() };
const transcriptPath = sessions.resolveSessionTranscriptPath(sessionId);
const sessionEntry = { sessionId, updatedAt: Date.now(), sessionFile: transcriptPath };
const sessionStore = { main: sessionEntry };
await fs.mkdir(path.dirname(storePath), { recursive: true });
await fs.writeFile(storePath, JSON.stringify(sessionStore), "utf-8");
await fs.mkdir(path.dirname(transcriptPath), { recursive: true });
await fs.writeFile(transcriptPath, "ok", "utf-8");
runEmbeddedPiAgentMock
.mockImplementationOnce(async () => {
@@ -175,11 +179,14 @@ describe("runReplyAgent typing (heartbeat)", () => {
try {
const sessionId = "session";
const storePath = path.join(stateDir, "sessions", "sessions.json");
const sessionEntry = { sessionId, updatedAt: Date.now() };
const transcriptPath = sessions.resolveSessionTranscriptPath(sessionId);
const sessionEntry = { sessionId, updatedAt: Date.now(), sessionFile: transcriptPath };
const sessionStore = { main: sessionEntry };
await fs.mkdir(path.dirname(storePath), { recursive: true });
await fs.writeFile(storePath, JSON.stringify(sessionStore), "utf-8");
await fs.mkdir(path.dirname(transcriptPath), { recursive: true });
await fs.writeFile(transcriptPath, "ok", "utf-8");
runEmbeddedPiAgentMock
.mockImplementationOnce(async () => ({
@@ -229,11 +236,14 @@ describe("runReplyAgent typing (heartbeat)", () => {
try {
const sessionId = "session";
const storePath = path.join(stateDir, "sessions", "sessions.json");
const sessionEntry = { sessionId, updatedAt: Date.now() };
const transcriptPath = sessions.resolveSessionTranscriptPath(sessionId);
const sessionEntry = { sessionId, updatedAt: Date.now(), sessionFile: transcriptPath };
const sessionStore = { main: sessionEntry };
await fs.mkdir(path.dirname(storePath), { recursive: true });
await fs.writeFile(storePath, JSON.stringify(sessionStore), "utf-8");
await fs.mkdir(path.dirname(transcriptPath), { recursive: true });
await fs.writeFile(transcriptPath, "ok", "utf-8");
runEmbeddedPiAgentMock.mockImplementationOnce(async () => ({
payloads: [{ text: "Message ordering conflict - please try again.", isError: true }],
@@ -260,6 +270,7 @@ describe("runReplyAgent typing (heartbeat)", () => {
});
expect(payload.text?.toLowerCase()).toContain("reset");
expect(sessionStore.main.sessionId).not.toBe(sessionId);
await expect(fs.access(transcriptPath)).rejects.toBeDefined();
const persisted = JSON.parse(await fs.readFile(storePath, "utf-8"));
expect(persisted.main.sessionId).toBe(sessionStore.main.sessionId);

View File

@@ -1,4 +1,5 @@
import crypto from "node:crypto";
import fs from "node:fs";
import { setCliSessionId } from "../../agents/cli-session.js";
import { lookupContextTokens } from "../../agents/context.js";
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
@@ -8,6 +9,7 @@ import { queueEmbeddedPiMessage } from "../../agents/pi-embedded.js";
import { hasNonzeroUsage } from "../../agents/usage.js";
import {
resolveAgentIdFromSessionKey,
resolveSessionFilePath,
resolveSessionTranscriptPath,
type SessionEntry,
updateSessionStore,
@@ -247,6 +249,8 @@ export async function runReplyAgent(params: {
};
const resetSessionAfterRoleOrderingConflict = async (reason: string): Promise<boolean> => {
if (!sessionKey || !activeSessionStore || !storePath) return false;
const prevEntry = activeSessionStore[sessionKey] ?? activeSessionEntry;
const prevSessionId = prevEntry?.sessionId;
const nextSessionId = crypto.randomUUID();
const nextEntry: SessionEntry = {
...(activeSessionStore[sessionKey] ?? activeSessionEntry),
@@ -279,6 +283,19 @@ export async function runReplyAgent(params: {
defaultRuntime.error(
`Role ordering conflict (${reason}). Restarting session ${sessionKey} -> ${nextSessionId}.`,
);
if (prevSessionId) {
const transcriptCandidates = new Set<string>();
const resolved = resolveSessionFilePath(prevSessionId, prevEntry, { agentId });
if (resolved) transcriptCandidates.add(resolved);
transcriptCandidates.add(resolveSessionTranscriptPath(prevSessionId, agentId));
for (const candidate of transcriptCandidates) {
try {
fs.unlinkSync(candidate);
} catch {
// Best-effort cleanup.
}
}
}
return true;
};
try {