fix(sessions): cover terminal transcript markers

This commit is contained in:
Fermin Quant
2026-06-05 02:40:06 -04:00
committed by Ayaan Zaidi
parent 0c9ac48d2c
commit 57bed6ae0c
14 changed files with 438 additions and 58 deletions

View File

@@ -4,6 +4,7 @@ export {
readLatestAssistantTextFromSessionTranscript,
resolveAndPersistSessionFile,
resolveSessionStoreEntry,
updateSessionStoreEntry,
} from "openclaw/plugin-sdk/session-store-runtime";
export { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
export { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";

View File

@@ -70,7 +70,11 @@ const createChannelMessageReplyPipeline = vi.hoisted(() =>
);
const wasSentByBot = vi.hoisted(() => vi.fn(() => false));
const appendSessionTranscriptMessage = vi.hoisted(() =>
vi.fn(async (_params: { message?: unknown }) => ({ messageId: "m1" })),
vi.fn(async ({ message }: { message?: unknown }) => ({
messageId: "m1",
message,
appended: true,
})),
);
const emitSessionTranscriptUpdate = vi.hoisted(() => vi.fn());
const loadSessionStore = vi.hoisted(() => vi.fn());
@@ -101,6 +105,7 @@ const resolveSessionStoreEntry = vi.hoisted(() =>
existing: store[sessionKey],
})),
);
const updateSessionStoreEntry = vi.hoisted(() => vi.fn(async () => null));
vi.mock("./draft-stream.js", () => ({
createTelegramDraftStream,
@@ -155,6 +160,7 @@ vi.mock("./bot-message-dispatch.runtime.js", () => ({
resolveMarkdownTableMode,
resolveSessionStoreEntry,
resolveStorePath,
updateSessionStoreEntry,
}));
vi.mock("./bot-message-dispatch.agent.runtime.js", () => ({
@@ -268,11 +274,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
loadSessionStore.mockReset();
resolveStorePath.mockReset();
resolveAndPersistSessionFile.mockReset();
updateSessionStoreEntry.mockReset();
generateTopicLabel.mockReset();
getAgentScopedMediaLocalRoots.mockClear();
resolveChunkMode.mockClear();
resolveMarkdownTableMode.mockClear();
resolveSessionStoreEntry.mockClear();
updateSessionStoreEntry.mockClear();
describeStickerImage.mockReset();
loadModelCatalog.mockReset();
findModelInCatalog.mockReset();
@@ -1445,6 +1453,29 @@ describe("dispatchTelegramMessage draft streaming", () => {
});
});
it("advances the session marker after mirroring preview-finalized finals", async () => {
setupDraftStreams({ answerMessageId: 2001 });
const context = createContext();
context.ctxPayload.SessionKey = "agent:default:telegram:direct:123";
loadSessionStore.mockReturnValue({
"agent:default:telegram:direct:123": { sessionId: "s1" },
});
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
await dispatcherOptions.deliver({ text: "Final answer" }, { kind: "final" });
return { queuedFinal: true };
});
await dispatchWithContext({ context });
const markerUpdateCall = expectRecordFields(mockCallArg(updateSessionStoreEntry), {
storePath: "/tmp/sessions.json",
sessionKey: "agent:default:telegram:direct:123",
});
const update = markerUpdateCall.update as (entry: { sessionId?: string }) => unknown;
expect(update({ sessionId: "s1" })).toEqual({ updatedAt: expect.any(Number) });
expect(update({ sessionId: "new-session" })).toBeNull();
});
it("does not mirror non-final tool progress into the session transcript", async () => {
const context = createContext();
context.ctxPayload.SessionKey = "agent:default:telegram:direct:123";

View File

@@ -79,6 +79,7 @@ import {
resolveMarkdownTableMode,
resolveAndPersistSessionFile,
resolveSessionStoreEntry,
updateSessionStoreEntry,
} from "./bot-message-dispatch.runtime.js";
import type { TelegramBotOptions } from "./bot.types.js";
import { deliverReplies, emitInternalMessageSentHook } from "./bot/delivery.js";
@@ -373,11 +374,26 @@ async function mirrorTelegramAssistantReplyToTranscript(params: {
stopReason: "stop" as const,
timestamp: Date.now(),
};
const { messageId, message: appendedMessage } = await appendSessionTranscriptMessage({
const {
appended,
messageId,
message: appendedMessage,
} = await appendSessionTranscriptMessage({
transcriptPath: sessionFile,
message,
config: params.cfg,
});
if (appended) {
const transcriptMarkerUpdatedAt = Date.now();
await updateSessionStoreEntry({
storePath,
sessionKey: params.sessionKey,
update: (current) =>
current.sessionId === sessionEntry.sessionId
? { updatedAt: transcriptMarkerUpdatedAt }
: null,
});
}
emitSessionTranscriptUpdate({
sessionFile,
sessionKey: params.sessionKey,