Files
openclaw/src/agents/cli-session.ts
2026-03-27 00:19:24 +00:00

143 lines
4.7 KiB
TypeScript

import crypto from "node:crypto";
import { CLAUDE_CLI_BACKEND_ID } from "../../extensions/anthropic/cli-shared.js";
import type { CliSessionBinding, SessionEntry } from "../config/sessions.js";
import { normalizeProviderId } from "./model-selection.js";
function trimOptional(value: string | undefined): string | undefined {
const trimmed = value?.trim();
return trimmed ? trimmed : undefined;
}
export function hashCliSessionText(value: string | undefined): string | undefined {
const trimmed = trimOptional(value);
if (!trimmed) {
return undefined;
}
return crypto.createHash("sha256").update(trimmed).digest("hex");
}
export function getCliSessionBinding(
entry: SessionEntry | undefined,
provider: string,
): CliSessionBinding | undefined {
if (!entry) {
return undefined;
}
const normalized = normalizeProviderId(provider);
const fromBindings = entry.cliSessionBindings?.[normalized];
const bindingSessionId = trimOptional(fromBindings?.sessionId);
if (bindingSessionId) {
return {
sessionId: bindingSessionId,
authProfileId: trimOptional(fromBindings?.authProfileId),
extraSystemPromptHash: trimOptional(fromBindings?.extraSystemPromptHash),
mcpConfigHash: trimOptional(fromBindings?.mcpConfigHash),
};
}
const fromMap = entry.cliSessionIds?.[normalized];
if (fromMap?.trim()) {
return { sessionId: fromMap.trim() };
}
if (normalized === CLAUDE_CLI_BACKEND_ID) {
const legacy = entry.claudeCliSessionId?.trim();
if (legacy) {
return { sessionId: legacy };
}
}
return undefined;
}
export function getCliSessionId(
entry: SessionEntry | undefined,
provider: string,
): string | undefined {
return getCliSessionBinding(entry, provider)?.sessionId;
}
export function setCliSessionId(entry: SessionEntry, provider: string, sessionId: string): void {
setCliSessionBinding(entry, provider, { sessionId });
}
export function setCliSessionBinding(
entry: SessionEntry,
provider: string,
binding: CliSessionBinding,
): void {
const normalized = normalizeProviderId(provider);
const trimmed = binding.sessionId.trim();
if (!trimmed) {
return;
}
entry.cliSessionBindings = {
...entry.cliSessionBindings,
[normalized]: {
sessionId: trimmed,
...(trimOptional(binding.authProfileId)
? { authProfileId: trimOptional(binding.authProfileId) }
: {}),
...(trimOptional(binding.extraSystemPromptHash)
? { extraSystemPromptHash: trimOptional(binding.extraSystemPromptHash) }
: {}),
...(trimOptional(binding.mcpConfigHash)
? { mcpConfigHash: trimOptional(binding.mcpConfigHash) }
: {}),
},
};
entry.cliSessionIds = { ...entry.cliSessionIds, [normalized]: trimmed };
if (normalized === CLAUDE_CLI_BACKEND_ID) {
entry.claudeCliSessionId = trimmed;
}
}
export function clearCliSession(entry: SessionEntry, provider: string): void {
const normalized = normalizeProviderId(provider);
if (entry.cliSessionBindings?.[normalized] !== undefined) {
const next = { ...entry.cliSessionBindings };
delete next[normalized];
entry.cliSessionBindings = Object.keys(next).length > 0 ? next : undefined;
}
if (entry.cliSessionIds?.[normalized] !== undefined) {
const next = { ...entry.cliSessionIds };
delete next[normalized];
entry.cliSessionIds = Object.keys(next).length > 0 ? next : undefined;
}
if (normalized === CLAUDE_CLI_BACKEND_ID) {
delete entry.claudeCliSessionId;
}
}
export function clearAllCliSessions(entry: SessionEntry): void {
delete entry.cliSessionBindings;
delete entry.cliSessionIds;
delete entry.claudeCliSessionId;
}
export function resolveCliSessionReuse(params: {
binding?: CliSessionBinding;
authProfileId?: string;
extraSystemPromptHash?: string;
mcpConfigHash?: string;
}): { sessionId?: string; invalidatedReason?: "auth-profile" | "system-prompt" | "mcp" } {
const binding = params.binding;
const sessionId = trimOptional(binding?.sessionId);
if (!sessionId) {
return {};
}
const currentAuthProfileId = trimOptional(params.authProfileId);
const currentExtraSystemPromptHash = trimOptional(params.extraSystemPromptHash);
const currentMcpConfigHash = trimOptional(params.mcpConfigHash);
if (binding?.authProfileId && trimOptional(binding.authProfileId) !== currentAuthProfileId) {
return { invalidatedReason: "auth-profile" };
}
if (
binding?.extraSystemPromptHash &&
trimOptional(binding.extraSystemPromptHash) !== currentExtraSystemPromptHash
) {
return { invalidatedReason: "system-prompt" };
}
if (binding?.mcpConfigHash && trimOptional(binding.mcpConfigHash) !== currentMcpConfigHash) {
return { invalidatedReason: "mcp" };
}
return { sessionId };
}