diff --git a/src/auto-reply/reply/get-reply-run.ts b/src/auto-reply/reply/get-reply-run.ts index 83a22407ec3..c951fe69f4d 100644 --- a/src/auto-reply/reply/get-reply-run.ts +++ b/src/auto-reply/reply/get-reply-run.ts @@ -1145,6 +1145,7 @@ export async function runPreparedReply( ? (internalOpts?.queuedFollowupAbortSignal ?? opts?.abortSignal) : undefined; const userTurnMediaForPersistence = buildPersistedUserTurnMediaInputsFromFields(ctx); + const inputProvenance = ctx.InputProvenance ?? sessionCtx.InputProvenance; const userTurnTranscriptText = resolvePersistedUserTurnText(transcriptBody, { hasMedia: userTurnMediaForPersistence.length > 0, }); @@ -1152,6 +1153,7 @@ export async function runPreparedReply( userTurnTranscriptText !== undefined || userTurnMediaForPersistence.length > 0 ? { text: userTurnTranscriptText, + ...(inputProvenance ? { provenance: inputProvenance } : {}), ...(userTurnMediaForPersistence.length > 0 ? { media: userTurnMediaForPersistence, @@ -1265,7 +1267,7 @@ export async function runPreparedReply( timeoutMs, blockReplyBreak: resolvedBlockStreamingBreak, ownerNumbers: command.ownerList.length > 0 ? command.ownerList : undefined, - inputProvenance: ctx.InputProvenance ?? sessionCtx.InputProvenance, + inputProvenance, extraSystemPrompt: extraSystemPromptParts.join("\n\n") || undefined, sourceReplyDeliveryMode: isRoomEvent ? "message_tool_only" : opts?.sourceReplyDeliveryMode, silentReplyPromptMode, diff --git a/src/gateway/server-methods/chat.ts b/src/gateway/server-methods/chat.ts index f91527d967d..3d47de50fc5 100644 --- a/src/gateway/server-methods/chat.ts +++ b/src/gateway/server-methods/chat.ts @@ -2600,6 +2600,7 @@ export const chatHandlers: GatewayRequestHandlers = { text: rawMessage, timestamp: now, idempotencyKey: `${clientRunId}:user`, + ...(systemInputProvenance ? { provenance: systemInputProvenance } : {}), }; const userTurnInputPromise: Promise = userTurnMediaPromise.then((media) => ({ ...baseUserTurnInput, diff --git a/src/sessions/user-turn-transcript.test.ts b/src/sessions/user-turn-transcript.test.ts index eb07d0ef88d..7feeca42a29 100644 --- a/src/sessions/user-turn-transcript.test.ts +++ b/src/sessions/user-turn-transcript.test.ts @@ -231,6 +231,11 @@ describe("user turn transcript persistence", () => { text: "What is in this image?", media: [{ path: "/tmp/image.png", contentType: "image/png" }], timestamp: 123, + provenance: { + kind: "inter_session", + sourceSessionKey: "source-main", + sourceTool: "sessions_send", + }, }, updateMode: "none", }); @@ -245,6 +250,11 @@ describe("user turn transcript persistence", () => { role: "user", content: "What is in this image?", MediaPath: "/tmp/image.png", + provenance: { + kind: "inter_session", + sourceSessionKey: "source-main", + sourceTool: "sessions_send", + }, MediaType: "image/png", }), ]); diff --git a/src/sessions/user-turn-transcript.ts b/src/sessions/user-turn-transcript.ts index 714e73f03cd..72048839145 100644 --- a/src/sessions/user-turn-transcript.ts +++ b/src/sessions/user-turn-transcript.ts @@ -2,6 +2,7 @@ import path from "node:path"; import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { appendSessionTranscriptMessage } from "../config/sessions/transcript-append.js"; import { mimeTypeFromFilePath } from "../media/mime.js"; +import { applyInputProvenanceToUserMessage, type InputProvenance } from "./input-provenance.js"; import { emitSessionTranscriptUpdate } from "./transcript-events.js"; type TranscriptAppendConfig = Parameters[0]["config"]; @@ -34,6 +35,7 @@ export type UserTurnInput = { media?: readonly PersistedUserTurnMediaInput[] | null; timestamp?: number; idempotencyKey?: string; + provenance?: InputProvenance; mediaOnlyText?: string; }; @@ -285,13 +287,14 @@ function buildPersistedUserTurnMessage(params: UserTurnInput): PersistedUserTurn const hasMedia = Boolean(mediaFields.MediaPath); const text = normalizeTranscriptText(params.text); const content = text || (hasMedia ? (params.mediaOnlyText ?? "") : ""); - return { + const message = { role: "user", content, timestamp: params.timestamp ?? Date.now(), ...(params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : {}), ...mediaFields, } as PersistedUserTurnMessage; + return applyInputProvenanceToUserMessage(message, params.provenance) as PersistedUserTurnMessage; } function resolvePersistedUserTurnMessage(