mirror of https://github.com/openclaw/openclaw.git
223 lines
6.2 KiB
TypeScript
223 lines
6.2 KiB
TypeScript
import type { OpenClawConfig } from "../config/config.js";
|
|
import type { SessionAcpMeta } from "../config/sessions/types.js";
|
|
import { logVerbose } from "../globals.js";
|
|
import { getAcpSessionManager } from "./control-plane/manager.js";
|
|
import { resolveAcpAgentFromSessionKey } from "./control-plane/manager.utils.js";
|
|
import { resolveConfiguredAcpBindingSpecBySessionKey } from "./persistent-bindings.resolve.js";
|
|
import {
|
|
buildConfiguredAcpSessionKey,
|
|
normalizeText,
|
|
type ConfiguredAcpBindingSpec,
|
|
type ResolvedConfiguredAcpBinding,
|
|
} from "./persistent-bindings.types.js";
|
|
import { readAcpSessionEntry } from "./runtime/session-meta.js";
|
|
|
|
function sessionMatchesConfiguredBinding(params: {
|
|
cfg: OpenClawConfig;
|
|
spec: ConfiguredAcpBindingSpec;
|
|
meta: SessionAcpMeta;
|
|
}): boolean {
|
|
const desiredAgent = (params.spec.acpAgentId ?? params.spec.agentId).trim().toLowerCase();
|
|
const currentAgent = (params.meta.agent ?? "").trim().toLowerCase();
|
|
if (!currentAgent || currentAgent !== desiredAgent) {
|
|
return false;
|
|
}
|
|
|
|
if (params.meta.mode !== params.spec.mode) {
|
|
return false;
|
|
}
|
|
|
|
const desiredBackend = params.spec.backend?.trim() || params.cfg.acp?.backend?.trim() || "";
|
|
if (desiredBackend) {
|
|
const currentBackend = (params.meta.backend ?? "").trim();
|
|
if (!currentBackend || currentBackend !== desiredBackend) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const desiredCwd = params.spec.cwd?.trim();
|
|
if (desiredCwd !== undefined) {
|
|
const currentCwd = (params.meta.runtimeOptions?.cwd ?? params.meta.cwd ?? "").trim();
|
|
if (desiredCwd !== currentCwd) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export async function ensureConfiguredAcpBindingSession(params: {
|
|
cfg: OpenClawConfig;
|
|
spec: ConfiguredAcpBindingSpec;
|
|
}): Promise<{ ok: true; sessionKey: string } | { ok: false; sessionKey: string; error: string }> {
|
|
const sessionKey = buildConfiguredAcpSessionKey(params.spec);
|
|
const acpManager = getAcpSessionManager();
|
|
try {
|
|
const resolution = acpManager.resolveSession({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
});
|
|
if (
|
|
resolution.kind === "ready" &&
|
|
sessionMatchesConfiguredBinding({
|
|
cfg: params.cfg,
|
|
spec: params.spec,
|
|
meta: resolution.meta,
|
|
})
|
|
) {
|
|
return {
|
|
ok: true,
|
|
sessionKey,
|
|
};
|
|
}
|
|
|
|
if (resolution.kind !== "none") {
|
|
await acpManager.closeSession({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
reason: "config-binding-reconfigure",
|
|
clearMeta: false,
|
|
allowBackendUnavailable: true,
|
|
requireAcpSession: false,
|
|
});
|
|
}
|
|
|
|
await acpManager.initializeSession({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
agent: params.spec.acpAgentId ?? params.spec.agentId,
|
|
mode: params.spec.mode,
|
|
cwd: params.spec.cwd,
|
|
backendId: params.spec.backend,
|
|
});
|
|
|
|
return {
|
|
ok: true,
|
|
sessionKey,
|
|
};
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
logVerbose(
|
|
`acp-configured-binding: failed ensuring ${params.spec.channel}:${params.spec.accountId}:${params.spec.conversationId} -> ${sessionKey}: ${message}`,
|
|
);
|
|
return {
|
|
ok: false,
|
|
sessionKey,
|
|
error: message,
|
|
};
|
|
}
|
|
}
|
|
|
|
export async function ensureConfiguredAcpBindingReady(params: {
|
|
cfg: OpenClawConfig;
|
|
configuredBinding: ResolvedConfiguredAcpBinding | null;
|
|
}): Promise<{ ok: true } | { ok: false; error: string }> {
|
|
if (!params.configuredBinding) {
|
|
return { ok: true };
|
|
}
|
|
const ensured = await ensureConfiguredAcpBindingSession({
|
|
cfg: params.cfg,
|
|
spec: params.configuredBinding.spec,
|
|
});
|
|
if (ensured.ok) {
|
|
return { ok: true };
|
|
}
|
|
return {
|
|
ok: false,
|
|
error: ensured.error ?? "unknown error",
|
|
};
|
|
}
|
|
|
|
export async function resetAcpSessionInPlace(params: {
|
|
cfg: OpenClawConfig;
|
|
sessionKey: string;
|
|
reason: "new" | "reset";
|
|
}): Promise<{ ok: true } | { ok: false; skipped?: boolean; error?: string }> {
|
|
const sessionKey = params.sessionKey.trim();
|
|
if (!sessionKey) {
|
|
return {
|
|
ok: false,
|
|
skipped: true,
|
|
};
|
|
}
|
|
|
|
const meta = readAcpSessionEntry({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
})?.acp;
|
|
const configuredBinding =
|
|
!meta || !normalizeText(meta.agent)
|
|
? resolveConfiguredAcpBindingSpecBySessionKey({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
})
|
|
: null;
|
|
if (!meta) {
|
|
if (configuredBinding) {
|
|
const ensured = await ensureConfiguredAcpBindingSession({
|
|
cfg: params.cfg,
|
|
spec: configuredBinding,
|
|
});
|
|
if (ensured.ok) {
|
|
return { ok: true };
|
|
}
|
|
return {
|
|
ok: false,
|
|
error: ensured.error,
|
|
};
|
|
}
|
|
return {
|
|
ok: false,
|
|
skipped: true,
|
|
};
|
|
}
|
|
|
|
const acpManager = getAcpSessionManager();
|
|
const agent =
|
|
normalizeText(meta.agent) ??
|
|
configuredBinding?.acpAgentId ??
|
|
configuredBinding?.agentId ??
|
|
resolveAcpAgentFromSessionKey(sessionKey, "main");
|
|
const mode = meta.mode === "oneshot" ? "oneshot" : "persistent";
|
|
const runtimeOptions = { ...meta.runtimeOptions };
|
|
const cwd = normalizeText(runtimeOptions.cwd ?? meta.cwd);
|
|
|
|
try {
|
|
await acpManager.closeSession({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
reason: `${params.reason}-in-place-reset`,
|
|
clearMeta: false,
|
|
allowBackendUnavailable: true,
|
|
requireAcpSession: false,
|
|
});
|
|
|
|
await acpManager.initializeSession({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
agent,
|
|
mode,
|
|
cwd,
|
|
backendId: normalizeText(meta.backend) ?? normalizeText(params.cfg.acp?.backend),
|
|
});
|
|
|
|
const runtimeOptionsPatch = Object.fromEntries(
|
|
Object.entries(runtimeOptions).filter(([, value]) => value !== undefined),
|
|
) as SessionAcpMeta["runtimeOptions"];
|
|
if (runtimeOptionsPatch && Object.keys(runtimeOptionsPatch).length > 0) {
|
|
await acpManager.updateSessionRuntimeOptions({
|
|
cfg: params.cfg,
|
|
sessionKey,
|
|
patch: runtimeOptionsPatch,
|
|
});
|
|
}
|
|
return { ok: true };
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
logVerbose(`acp-configured-binding: failed reset for ${sessionKey}: ${message}`);
|
|
return {
|
|
ok: false,
|
|
error: message,
|
|
};
|
|
}
|
|
}
|