From 8bbd4baa9a41263fac1edcfa8a091f797606f472 Mon Sep 17 00:00:00 2001 From: Shakker Date: Tue, 26 May 2026 15:24:50 +0100 Subject: [PATCH] refactor: trim user turn transcript API --- src/sessions/user-turn-transcript.test.ts | 75 ++--------------------- src/sessions/user-turn-transcript.ts | 52 +++++----------- 2 files changed, 20 insertions(+), 107 deletions(-) diff --git a/src/sessions/user-turn-transcript.test.ts b/src/sessions/user-turn-transcript.test.ts index fe4d7e5e3b6..53e27cb61bb 100644 --- a/src/sessions/user-turn-transcript.test.ts +++ b/src/sessions/user-turn-transcript.test.ts @@ -9,10 +9,8 @@ import { createMockPluginRegistry } from "openclaw/plugin-sdk/plugin-test-runtim import { castAgentMessage } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it } from "vitest"; import { - appendInlineUserTurnTranscriptMessage, appendUserTurnTranscriptMessage, buildPersistedUserTurnMediaInputsFromFields, - buildPersistedUserTurnMediaFields, buildPersistedUserTurnMessage, createUserTurnTranscriptRecorder, mergePreparedUserTurnMessageForRuntime, @@ -137,71 +135,6 @@ describe("user turn transcript persistence", () => { }); }); - describe("buildPersistedUserTurnMediaFields", () => { - it("omits media fields when there is no structured media", () => { - expect(buildPersistedUserTurnMediaFields(undefined)).toEqual({}); - expect(buildPersistedUserTurnMediaFields([])).toEqual({}); - expect(buildPersistedUserTurnMediaFields([{ path: " ", contentType: "image/png" }])).toEqual( - {}, - ); - }); - - it("builds aligned transcript media fields from structured media facts", () => { - expect( - buildPersistedUserTurnMediaFields([ - { path: "/tmp/a.png", contentType: "image/png" }, - { path: "/tmp/b.jpg", contentType: "image/jpeg" }, - ]), - ).toEqual({ - MediaPath: "/tmp/a.png", - MediaPaths: ["/tmp/a.png", "/tmp/b.jpg"], - MediaType: "image/png", - MediaTypes: ["image/png", "image/jpeg"], - }); - }); - - it("uses url-backed media when no local path is available", () => { - expect( - buildPersistedUserTurnMediaFields([ - { url: "media://inbound/photo.png", contentType: "image/png" }, - ]), - ).toEqual({ - MediaPath: "media://inbound/photo.png", - MediaPaths: ["media://inbound/photo.png"], - MediaType: "image/png", - MediaTypes: ["image/png"], - }); - }); - - it("falls back to kind and then octet-stream for media types", () => { - expect( - buildPersistedUserTurnMediaFields([ - { path: "/tmp/doc", kind: "document" }, - { path: "/tmp/blob" }, - ]), - ).toEqual({ - MediaPath: "/tmp/doc", - MediaPaths: ["/tmp/doc", "/tmp/blob"], - MediaType: "document", - MediaTypes: ["document", "application/octet-stream"], - }); - }); - - it("keeps media paths and types aligned when incomplete entries are skipped", () => { - expect( - buildPersistedUserTurnMediaFields([ - { contentType: "image/png" }, - { path: "/tmp/b.jpg", contentType: "image/jpeg" }, - ]), - ).toEqual({ - MediaPath: "/tmp/b.jpg", - MediaPaths: ["/tmp/b.jpg"], - MediaType: "image/jpeg", - MediaTypes: ["image/jpeg"], - }); - }); - }); - describe("buildPersistedUserTurnMessage", () => { it("builds a plain user transcript message for text-only turns", () => { expect( @@ -419,11 +352,11 @@ describe("user turn transcript persistence", () => { ]); }); - it("uses inline update mode through the convenience wrapper", async () => { + it("uses inline update mode by default", async () => { const dir = createTempDir("openclaw-user-turn-append-inline-"); const transcriptPath = path.join(dir, "session.jsonl"); - const appended = await appendInlineUserTurnTranscriptMessage({ + const appended = await appendUserTurnTranscriptMessage({ transcriptPath, sessionId: "session-1", sessionKey: "main", @@ -509,14 +442,14 @@ describe("user turn transcript persistence", () => { const dir = createTempDir("openclaw-user-turn-redacted-idempotent-"); const transcriptPath = path.join(dir, "session.jsonl"); - await appendInlineUserTurnTranscriptMessage({ + await appendUserTurnTranscriptMessage({ transcriptPath, input: { text: "secret prompt", idempotencyKey: "chat-run-1:user", }, }); - await appendInlineUserTurnTranscriptMessage({ + await appendUserTurnTranscriptMessage({ transcriptPath, input: { text: "secret prompt", diff --git a/src/sessions/user-turn-transcript.ts b/src/sessions/user-turn-transcript.ts index 2744279f0ae..eee36c1a37d 100644 --- a/src/sessions/user-turn-transcript.ts +++ b/src/sessions/user-turn-transcript.ts @@ -9,14 +9,14 @@ import { logVerbose } from "../globals.js"; import { mimeTypeFromFilePath } from "../media/mime.js"; import { emitSessionTranscriptUpdate } from "./transcript-events.js"; -export type PersistedUserTurnMediaInput = { +type PersistedUserTurnMediaInput = { path?: string | null; url?: string | null; contentType?: string | null; kind?: string | null; }; -export type PersistedUserTurnMediaFields = { +type PersistedUserTurnMediaFields = { MediaPath?: string; MediaPaths?: string[]; MediaType?: string; @@ -33,11 +33,9 @@ export type UserTurnInput = { mediaOnlyText?: string; }; -export type BuildPersistedUserTurnMessageParams = UserTurnInput; +type UserTurnTranscriptUpdateMode = "inline" | "file-only" | "none"; -export type UserTurnTranscriptUpdateMode = "inline" | "file-only" | "none"; - -export type AppendUserTurnTranscriptMessageParams = { +type AppendUserTurnTranscriptMessageParams = { transcriptPath: string; input?: UserTurnInput; message?: PersistedUserTurnMessage; @@ -49,12 +47,7 @@ export type AppendUserTurnTranscriptMessageParams = { updateMode?: UserTurnTranscriptUpdateMode; }; -export type AppendInlineUserTurnTranscriptMessageParams = Omit< - AppendUserTurnTranscriptMessageParams, - "updateMode" ->; - -export type PersistUserTurnTranscriptParams = { +type PersistUserTurnTranscriptParams = { input?: UserTurnInput; message?: PersistedUserTurnMessage; sessionId: string; @@ -69,12 +62,12 @@ export type PersistUserTurnTranscriptParams = { updateMode?: UserTurnTranscriptUpdateMode; }; -export type UserTurnTranscriptPersistenceTarget = Omit< +type UserTurnTranscriptPersistenceTarget = Omit< PersistUserTurnTranscriptParams, "input" | "message" | "updateMode" >; -export type UserTurnTranscriptFileTarget = { +type UserTurnTranscriptFileTarget = { transcriptPath: string; sessionId?: string; agentId?: string; @@ -83,18 +76,16 @@ export type UserTurnTranscriptFileTarget = { config?: OpenClawConfig; }; -export type UserTurnTranscriptTarget = - | UserTurnTranscriptPersistenceTarget - | UserTurnTranscriptFileTarget; +type UserTurnTranscriptTarget = UserTurnTranscriptPersistenceTarget | UserTurnTranscriptFileTarget; -export type UserTurnTranscriptPersistResult = { +type UserTurnTranscriptPersistResult = { sessionFile: string; sessionEntry: SessionEntry | undefined; messageId: string; message: PersistedUserTurnMessage; }; -export type UserTurnTranscriptTargetResolver = +type UserTurnTranscriptTargetResolver = | UserTurnTranscriptTarget | (() => UserTurnTranscriptTarget | undefined | Promise); @@ -117,7 +108,7 @@ export type UserTurnTranscriptRecorder = { }) => Promise; }; -export type CreateUserTurnTranscriptRecorderParams = { +type CreateUserTurnTranscriptRecorderParams = { input?: UserTurnInput; message?: PersistedUserTurnMessage; target: UserTurnTranscriptTargetResolver; @@ -126,7 +117,7 @@ export type CreateUserTurnTranscriptRecorderParams = { onPersistenceError?: (error: unknown) => void; }; -export type PersistedUserTurnTextFieldSource = { +type PersistedUserTurnTextFieldSource = { Transcript?: string | null; RawBody?: string | null; CommandBody?: string | null; @@ -135,12 +126,12 @@ export type PersistedUserTurnTextFieldSource = { BodyStripped?: string | null; }; -export type ResolvePersistedUserTurnTextOptions = { +type ResolvePersistedUserTurnTextOptions = { hasMedia?: boolean; fallback?: string | null; }; -export type PersistedUserTurnMediaFieldSource = { +type PersistedUserTurnMediaFieldSource = { MediaPath?: string | null; MediaPaths?: readonly (string | null | undefined)[] | null; MediaUrl?: string | null; @@ -285,7 +276,7 @@ export function buildPersistedUserTurnMediaInputsFromFields( return media; } -export function buildPersistedUserTurnMediaFields( +function buildPersistedUserTurnMediaFields( media: readonly PersistedUserTurnMediaInput[] | null | undefined, ): PersistedUserTurnMediaFields { const entries = Array.isArray(media) ? media : []; @@ -305,9 +296,7 @@ export function buildPersistedUserTurnMediaFields( }; } -export function buildPersistedUserTurnMessage( - params: BuildPersistedUserTurnMessageParams, -): PersistedUserTurnMessage { +export function buildPersistedUserTurnMessage(params: UserTurnInput): PersistedUserTurnMessage { const mediaFields = buildPersistedUserTurnMediaFields(params.media); const hasMedia = Boolean(mediaFields.MediaPath); const text = normalizeTranscriptText(params.text); @@ -440,15 +429,6 @@ export async function appendUserTurnTranscriptMessage( }; } -export async function appendInlineUserTurnTranscriptMessage( - params: AppendInlineUserTurnTranscriptMessageParams, -): ReturnType { - return await appendUserTurnTranscriptMessage({ - ...params, - updateMode: "inline", - }); -} - export async function persistUserTurnTranscript( params: PersistUserTurnTranscriptParams, ): Promise {