import type { SessionAcpIdentity, SessionAcpIdentitySource, SessionAcpMeta, } from "../../config/sessions/types.js"; import type { AcpRuntimeHandle, AcpRuntimeStatus } from "./types.js"; function normalizeText(value: unknown): string | undefined { if (typeof value !== "string") { return undefined; } const trimmed = value.trim(); return trimmed || undefined; } function normalizeIdentityState(value: unknown): SessionAcpIdentity["state"] | undefined { if (value !== "pending" && value !== "resolved") { return undefined; } return value; } function normalizeIdentitySource(value: unknown): SessionAcpIdentitySource | undefined { if (value !== "ensure" && value !== "status" && value !== "event") { return undefined; } return value; } function normalizeIdentity( identity: SessionAcpIdentity | undefined, ): SessionAcpIdentity | undefined { if (!identity) { return undefined; } const state = normalizeIdentityState(identity.state); const source = normalizeIdentitySource(identity.source); const acpxRecordId = normalizeText(identity.acpxRecordId); const acpxSessionId = normalizeText(identity.acpxSessionId); const agentSessionId = normalizeText(identity.agentSessionId); const lastUpdatedAt = typeof identity.lastUpdatedAt === "number" && Number.isFinite(identity.lastUpdatedAt) ? identity.lastUpdatedAt : undefined; const hasAnyId = Boolean(acpxRecordId || acpxSessionId || agentSessionId); if (!state && !source && !hasAnyId && lastUpdatedAt === undefined) { return undefined; } const resolved = Boolean(acpxSessionId || agentSessionId); const normalizedState = state ?? (resolved ? "resolved" : "pending"); return { state: normalizedState, ...(acpxRecordId ? { acpxRecordId } : {}), ...(acpxSessionId ? { acpxSessionId } : {}), ...(agentSessionId ? { agentSessionId } : {}), source: source ?? "status", lastUpdatedAt: lastUpdatedAt ?? Date.now(), }; } export function resolveSessionIdentityFromMeta( meta: SessionAcpMeta | undefined, ): SessionAcpIdentity | undefined { if (!meta) { return undefined; } return normalizeIdentity(meta.identity); } export function identityHasStableSessionId(identity: SessionAcpIdentity | undefined): boolean { return Boolean(identity?.acpxSessionId || identity?.agentSessionId); } export function isSessionIdentityPending(identity: SessionAcpIdentity | undefined): boolean { if (!identity) { return true; } return identity.state === "pending"; } export function identityEquals( left: SessionAcpIdentity | undefined, right: SessionAcpIdentity | undefined, ): boolean { const a = normalizeIdentity(left); const b = normalizeIdentity(right); if (!a && !b) { return true; } if (!a || !b) { return false; } return ( a.state === b.state && a.acpxRecordId === b.acpxRecordId && a.acpxSessionId === b.acpxSessionId && a.agentSessionId === b.agentSessionId && a.source === b.source ); } export function mergeSessionIdentity(params: { current: SessionAcpIdentity | undefined; incoming: SessionAcpIdentity | undefined; now: number; }): SessionAcpIdentity | undefined { const current = normalizeIdentity(params.current); const incoming = normalizeIdentity(params.incoming); if (!current) { if (!incoming) { return undefined; } return { ...incoming, lastUpdatedAt: params.now }; } if (!incoming) { return current; } const currentResolved = current.state === "resolved"; const incomingResolved = incoming.state === "resolved"; const allowIncomingValue = !currentResolved || incomingResolved; const nextRecordId = allowIncomingValue && incoming.acpxRecordId ? incoming.acpxRecordId : current.acpxRecordId; const nextAcpxSessionId = allowIncomingValue && incoming.acpxSessionId ? incoming.acpxSessionId : current.acpxSessionId; const nextAgentSessionId = allowIncomingValue && incoming.agentSessionId ? incoming.agentSessionId : current.agentSessionId; const nextResolved = Boolean(nextAcpxSessionId || nextAgentSessionId); const nextState: SessionAcpIdentity["state"] = nextResolved ? "resolved" : currentResolved ? "resolved" : incoming.state; const nextSource = allowIncomingValue ? incoming.source : current.source; const next: SessionAcpIdentity = { state: nextState, ...(nextRecordId ? { acpxRecordId: nextRecordId } : {}), ...(nextAcpxSessionId ? { acpxSessionId: nextAcpxSessionId } : {}), ...(nextAgentSessionId ? { agentSessionId: nextAgentSessionId } : {}), source: nextSource, lastUpdatedAt: params.now, }; return next; } export function createIdentityFromEnsure(params: { handle: AcpRuntimeHandle; now: number; }): SessionAcpIdentity | undefined { const acpxRecordId = normalizeText((params.handle as { acpxRecordId?: unknown }).acpxRecordId); const acpxSessionId = normalizeText(params.handle.backendSessionId); const agentSessionId = normalizeText(params.handle.agentSessionId); if (!acpxRecordId && !acpxSessionId && !agentSessionId) { return undefined; } return { state: "pending", ...(acpxRecordId ? { acpxRecordId } : {}), ...(acpxSessionId ? { acpxSessionId } : {}), ...(agentSessionId ? { agentSessionId } : {}), source: "ensure", lastUpdatedAt: params.now, }; } export function createIdentityFromStatus(params: { status: AcpRuntimeStatus | undefined; now: number; }): SessionAcpIdentity | undefined { if (!params.status) { return undefined; } const details = params.status.details; const acpxRecordId = normalizeText((params.status as { acpxRecordId?: unknown }).acpxRecordId) ?? normalizeText(details?.acpxRecordId); const acpxSessionId = normalizeText(params.status.backendSessionId) ?? normalizeText(details?.backendSessionId) ?? normalizeText(details?.acpxSessionId); const agentSessionId = normalizeText(params.status.agentSessionId) ?? normalizeText(details?.agentSessionId); if (!acpxRecordId && !acpxSessionId && !agentSessionId) { return undefined; } const resolved = Boolean(acpxSessionId || agentSessionId); return { state: resolved ? "resolved" : "pending", ...(acpxRecordId ? { acpxRecordId } : {}), ...(acpxSessionId ? { acpxSessionId } : {}), ...(agentSessionId ? { agentSessionId } : {}), source: "status", lastUpdatedAt: params.now, }; } export function resolveRuntimeHandleIdentifiersFromIdentity( identity: SessionAcpIdentity | undefined, ): { backendSessionId?: string; agentSessionId?: string } { if (!identity) { return {}; } return { ...(identity.acpxSessionId ? { backendSessionId: identity.acpxSessionId } : {}), ...(identity.agentSessionId ? { agentSessionId: identity.agentSessionId } : {}), }; }