openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
87 lines (86 loc) • 4.49 kB
JavaScript
import { c as readVoiceSessionRecordInTransaction, n as assertVoiceSessionOwnership, u as writeVoiceSessionRecordInTransaction } from "./client-voice-session-store-H3mgDR6I.mjs";
//#region src/talk/client-voice-session-write.kernel.ts
/** The caller owns the transaction; all predicates use its current canonical row. */
function mutateVoiceSessionInDatabase(database, input) {
if (input.kind === "effect" && !input.event.runId) return;
let record = readVoiceSessionRecordInTransaction(database, input.voiceSessionId);
if (!record) {
if (input.kind === "delivered" || input.kind === "effect") return;
if (input.kind !== "create") throw new Error("voice session not found");
record = {
version: 1,
voiceSessionId: input.voiceSessionId,
agentId: input.agentId,
sessionKey: input.sessionKey,
...input.provider ? { provider: input.provider } : {},
origin: input.origin,
...input.transcriptCapable === true ? { transcriptCapable: true } : {},
status: "open",
createdAt: input.now,
updatedAt: input.now,
consultRunIds: [],
effects: [],
transcriptFailureKeys: []
};
}
assertVoiceSessionOwnership(record, input);
switch (input.kind) {
case "consult":
if (record.consultRunIds.includes(input.runId)) return record;
record.consultRunIds.push(input.runId);
break;
case "effect": {
const { event } = input;
const runId = event.runId;
const existing = event.toolCallId ? record.effects.find((effect) => effect.runId === runId && effect.toolCallId === event.toolCallId) : record.effects.findLast((effect) => effect.runId === runId && effect.toolName === event.toolName && effect.status === "started");
if (event.type !== "tool.execution.started" && !existing) return record;
if (event.type !== "tool.execution.started" && existing) {
existing.status = event.type === "tool.execution.completed" ? "succeeded" : event.type === "tool.execution.blocked" ? "blocked" : event.terminalReason === "cancelled" ? "cancelled" : "failed";
existing.finishedAt = event.ts;
} else if (event.mutatingAction === true && (!event.toolCallId || !existing)) record.effects.push({
runId,
...event.toolCallId ? { toolCallId: event.toolCallId } : {},
toolName: event.toolName,
startedAt: event.ts,
status: "started"
});
break;
}
case "create":
if (record.origin !== input.origin) throw new Error("voice session origin does not match");
if (record.status !== "open") throw new Error("voice session is already closed");
if (record.provider && input.provider && record.provider !== input.provider) throw new Error("voice session provider does not match");
record.provider ??= input.provider;
if (input.transcriptCapable === true) record.transcriptCapable = true;
break;
case "reserve":
if (record.status !== "open") throw new Error("voice session is closed");
if (record.origin !== input.origin) throw new Error("voice session origin does not allow this transcript source");
if (!record.transcriptFailureKeys.includes(input.failureKey)) {
if (record.transcriptFailureKeys.length >= 41) throw new Error("voice transcript persistence has too many unresolved entries");
record.transcriptFailureKeys.push(input.failureKey);
}
break;
case "confirm":
if (input.role === "user") record.hasUserTranscript = true;
record.transcriptFailureKeys = record.transcriptFailureKeys.filter((key) => key !== input.failureKey);
break;
case "close":
if (input.expectedOrigin && record.origin !== input.expectedOrigin) throw new Error("relay-owned voice sessions close through talk.session.close");
if (input.staleBefore !== void 0 && (record.status !== "open" || record.updatedAt > input.staleBefore)) return;
if (record.transcriptFailureKeys.length > 0 && input.transcriptFailurePolicy === "require-success") throw new Error("voice transcript persistence must be retried before close");
if (input.transcriptFailurePolicy === "retain-and-close" && record.origin !== "relay") throw new Error("only relay voice sessions may close with unresolved transcripts");
if (record.status === "closed") return record;
record.status = "closed";
record.closedAt = input.now;
break;
case "delivered":
if (record.digestDeliveredAt) return record;
record.digestDeliveredAt = input.deliveredAt;
}
record.updatedAt = input.now;
writeVoiceSessionRecordInTransaction(database, record);
return record;
}
//#endregion
export { mutateVoiceSessionInDatabase };