UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

161 lines (160 loc) 8.79 kB
import { s as readErrorName } from "./error-coercion-D_-xJ90S.js"; import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js"; import "./errors-Db3Ymjlb.js"; import { r as normalizeCliSessionReseedReceipt } from "./cli-session-binding-Aa6nlDa8.js"; import { a as isFailoverError } from "./error-Bb_OF8ag.js"; import "./failover-error-BkNxlp8A.js"; import crypto from "node:crypto"; //#region src/agents/cli-session.ts /** * CLI session persistence helpers. * Keeps provider-keyed session bindings, reuse fingerprints, and legacy * Claude CLI state in one normalized session-store contract. */ const CLAUDE_CLI_BACKEND_ID = "claude-cli"; /** Whether a failover proves the provider-side conversation can no longer be resumed. */ function isCliSessionInvalidatingFailoverReason(reason) { return reason === "session_expired"; } /** Hash CLI session-sensitive text so reuse checks can compare stable fingerprints. */ function hashCliSessionText(value) { const trimmed = normalizeOptionalString(value); if (!trimmed) return; return crypto.createHash("sha256").update(trimmed).digest("hex"); } /** Projects explicit native continuity; diagnostic sessionId can name the local session. */ function applyCliSessionBindingResult(entry, provider, meta) { if (meta?.clearCliSessionBinding === true) clearCliSession(entry, provider); else if (meta?.cliSessionBinding?.sessionId.trim()) setCliSessionBinding(entry, provider, meta.cliSessionBinding); else return false; return true; } /** Revalidates the exact turn owner at native continuity's synchronous commit edge. */ function assertCliSessionBindingResultCommitAllowed(meta, assertSettlementCurrent, abortSignal) { assertSettlementCurrent(); if (meta?.clearCliSessionBinding !== true) abortSignal?.throwIfAborted(); } /** Store a CLI session binding and mirror it to legacy/simple session-id fields. */ function setCliSessionBinding(entry, provider, binding) { const normalized = normalizeProviderId(provider); const trimmed = binding.sessionId.trim(); if (!trimmed) return; const previousBinding = entry.cliSessionBindings?.[normalized]; const previousReceipt = normalizeOptionalString(previousBinding?.sessionId) === trimmed ? normalizeCliSessionReseedReceipt(previousBinding?.reseedReceipt) : void 0; const reseedReceipt = normalizeCliSessionReseedReceipt(binding.reseedReceipt) ?? previousReceipt; entry.cliSessionBindings = { ...entry.cliSessionBindings, [normalized]: { sessionId: trimmed, ...normalizeOptionalString(binding.resumeCheckpointId) ? { resumeCheckpointId: normalizeOptionalString(binding.resumeCheckpointId) } : {}, ...binding.forceReuse === true ? { forceReuse: true } : {}, ...binding.forkNextResume === true ? { forkNextResume: true } : {}, ...normalizeOptionalString(binding.authProfileId) ? { authProfileId: normalizeOptionalString(binding.authProfileId) } : {}, ...normalizeOptionalString(binding.authEpoch) ? { authEpoch: normalizeOptionalString(binding.authEpoch) } : {}, ...typeof binding.authEpochVersion === "number" && Number.isFinite(binding.authEpochVersion) ? { authEpochVersion: binding.authEpochVersion } : {}, ...normalizeOptionalString(binding.extraSystemPromptHash) ? { extraSystemPromptHash: normalizeOptionalString(binding.extraSystemPromptHash) } : {}, ...normalizeOptionalString(binding.messageToolPolicyHash) ? { messageToolPolicyHash: normalizeOptionalString(binding.messageToolPolicyHash) } : {}, ...normalizeOptionalString(binding.promptToolNamesHash) ? { promptToolNamesHash: normalizeOptionalString(binding.promptToolNamesHash) } : {}, ...normalizeOptionalString(binding.cwdHash) ? { cwdHash: normalizeOptionalString(binding.cwdHash) } : {}, ...normalizeOptionalString(binding.mcpConfigHash) ? { mcpConfigHash: normalizeOptionalString(binding.mcpConfigHash) } : {}, ...normalizeOptionalString(binding.mcpResumeHash) ? { mcpResumeHash: normalizeOptionalString(binding.mcpResumeHash) } : {}, ...reseedReceipt ? { reseedReceipt } : {} } }; entry.cliSessionIds = { ...entry.cliSessionIds, [normalized]: trimmed }; if (normalized === CLAUDE_CLI_BACKEND_ID) entry.claudeCliSessionId = trimmed; } /** Remove the stored CLI session binding for one provider. */ function clearCliSession(entry, provider) { const normalized = normalizeProviderId(provider); if (entry.cliSessionBindings?.[normalized] !== void 0) { const next = { ...entry.cliSessionBindings }; delete next[normalized]; entry.cliSessionBindings = Object.keys(next).length > 0 ? next : void 0; } if (entry.cliSessionIds?.[normalized] !== void 0) { const next = { ...entry.cliSessionIds }; delete next[normalized]; entry.cliSessionIds = Object.keys(next).length > 0 ? next : void 0; } if (normalized === CLAUDE_CLI_BACKEND_ID) entry.claudeCliSessionId = void 0; } /** Decide whether a failed CLI turn invalidates the binding it tried to resume. */ function shouldClearFailedCliSessionBinding(params) { if (!normalizeOptionalString(params.binding?.sessionId)) return false; if (params.hasNewGeneratedMediaTask === true) return false; if (isFailoverError(params.error)) return isCliSessionInvalidatingFailoverReason(params.error.reason); return params.binding?.forkNextResume !== true && readErrorName(params.error) === "AbortError"; } /** Stable reason used when recording why a failed reused CLI session was cleared. */ function resolveCliSessionClearReason(error) { return isFailoverError(error) ? error.reason : readErrorName(error) ?? "error"; } /** Decide whether a stored CLI session can be reused for the current auth/prompt/cwd/MCP state. */ function resolveCliSessionReuse(params) { const binding = params.binding; const sessionId = normalizeOptionalString(binding?.sessionId); if (!sessionId) return { mode: "none" }; if (binding?.forceReuse === true) return { mode: "reuse", sessionId }; const currentAuthProfileId = normalizeOptionalString(params.authProfileId); const currentAuthEpoch = normalizeOptionalString(params.authEpoch); const currentExtraSystemPromptHash = normalizeOptionalString(params.extraSystemPromptHash); const currentMessageToolPolicyHash = normalizeOptionalString(params.messageToolPolicyHash); const currentPromptToolNamesHash = normalizeOptionalString(params.promptToolNamesHash); const currentCwdHash = normalizeOptionalString(params.cwdHash); const currentMcpConfigHash = normalizeOptionalString(params.mcpConfigHash); const currentMcpResumeHash = normalizeOptionalString(params.mcpResumeHash); const storedAuthProfileId = normalizeOptionalString(binding?.authProfileId); const storedAuthEpoch = normalizeOptionalString(binding?.authEpoch); const hasMatchingVersionedAuthEpoch = binding?.authEpochVersion === params.authEpochVersion && storedAuthEpoch !== void 0 && currentAuthEpoch !== void 0 && storedAuthEpoch === currentAuthEpoch; if (storedAuthProfileId !== currentAuthProfileId) { if (!hasMatchingVersionedAuthEpoch) return { mode: "invalidate", invalidatedReason: "auth-profile" }; } if (binding?.authEpochVersion === params.authEpochVersion && storedAuthEpoch !== currentAuthEpoch) return { mode: "invalidate", invalidatedReason: "auth-epoch" }; if (normalizeOptionalString(binding?.messageToolPolicyHash) !== currentMessageToolPolicyHash) return { mode: "invalidate", invalidatedReason: "message-policy" }; const storedCwdHash = normalizeOptionalString(binding?.cwdHash); if (storedCwdHash !== void 0 && storedCwdHash !== currentCwdHash) return { mode: "invalidate", invalidatedReason: "cwd" }; const storedMcpResumeHash = normalizeOptionalString(binding?.mcpResumeHash); if (storedMcpResumeHash && currentMcpResumeHash) { if (storedMcpResumeHash !== currentMcpResumeHash) return { mode: "invalidate", invalidatedReason: "mcp" }; } else if (normalizeOptionalString(binding?.mcpConfigHash) !== currentMcpConfigHash) return { mode: "invalidate", invalidatedReason: "mcp" }; const driftReasons = []; if (normalizeOptionalString(binding?.extraSystemPromptHash) !== currentExtraSystemPromptHash) driftReasons.push("system-prompt"); if (normalizeOptionalString(binding?.promptToolNamesHash) !== currentPromptToolNamesHash) driftReasons.push("prompt-tools"); if (driftReasons.length > 0) return { mode: "reuse-with-drift", sessionId, drift: { reasons: driftReasons } }; return { mode: "reuse", sessionId }; } //#endregion export { isCliSessionInvalidatingFailoverReason as a, setCliSessionBinding as c, hashCliSessionText as i, shouldClearFailedCliSessionBinding as l, assertCliSessionBindingResultCommitAllowed as n, resolveCliSessionClearReason as o, clearCliSession as r, resolveCliSessionReuse as s, applyCliSessionBindingResult as t };