openskynet / src /acp /control-plane /manager.utils.ts
Darochin's picture
Mirror OpenSkyNet workspace snapshot from Git HEAD
fc93158 verified
import type { OpenClawConfig } from "../../config/config.js";
import {
canonicalizeMainSessionAlias,
resolveMainSessionKey,
} from "../../config/sessions/main-session.js";
import type { SessionAcpMeta } from "../../config/sessions/types.js";
import {
normalizeAgentId,
normalizeMainKey,
parseAgentSessionKey,
} from "../../routing/session-key.js";
import { ACP_ERROR_CODES, AcpRuntimeError } from "../runtime/errors.js";
import type { AcpSessionResolution } from "./manager.types.js";
export function resolveAcpAgentFromSessionKey(sessionKey: string, fallback = "main"): string {
const parsed = parseAgentSessionKey(sessionKey);
return normalizeAgentId(parsed?.agentId ?? fallback);
}
export function resolveMissingMetaError(sessionKey: string): AcpRuntimeError {
return new AcpRuntimeError(
"ACP_SESSION_INIT_FAILED",
`ACP metadata is missing for ${sessionKey}. Recreate this ACP session with /acp spawn and rebind the thread.`,
);
}
export function resolveAcpSessionResolutionError(
resolution: AcpSessionResolution,
): AcpRuntimeError | null {
if (resolution.kind === "ready") {
return null;
}
if (resolution.kind === "stale") {
return resolution.error;
}
return new AcpRuntimeError(
"ACP_SESSION_INIT_FAILED",
`Session is not ACP-enabled: ${resolution.sessionKey}`,
);
}
export function requireReadySessionMeta(resolution: AcpSessionResolution): SessionAcpMeta {
if (resolution.kind === "ready") {
return resolution.meta;
}
throw resolveAcpSessionResolutionError(resolution);
}
export function normalizeSessionKey(sessionKey: string): string {
return sessionKey.trim();
}
export function canonicalizeAcpSessionKey(params: {
cfg: OpenClawConfig;
sessionKey: string;
}): string {
const normalized = normalizeSessionKey(params.sessionKey);
if (!normalized) {
return "";
}
const lowered = normalized.toLowerCase();
if (lowered === "global" || lowered === "unknown") {
return lowered;
}
const parsed = parseAgentSessionKey(lowered);
if (parsed) {
return canonicalizeMainSessionAlias({
cfg: params.cfg,
agentId: parsed.agentId,
sessionKey: lowered,
});
}
const mainKey = normalizeMainKey(params.cfg.session?.mainKey);
if (lowered === "main" || lowered === mainKey) {
return resolveMainSessionKey(params.cfg);
}
return lowered;
}
export function normalizeActorKey(sessionKey: string): string {
return sessionKey.trim().toLowerCase();
}
export function normalizeAcpErrorCode(code: string | undefined): AcpRuntimeError["code"] {
if (!code) {
return "ACP_TURN_FAILED";
}
const normalized = code.trim().toUpperCase();
for (const allowed of ACP_ERROR_CODES) {
if (allowed === normalized) {
return allowed;
}
}
return "ACP_TURN_FAILED";
}
export function createUnsupportedControlError(params: {
backend: string;
control: string;
}): AcpRuntimeError {
return new AcpRuntimeError(
"ACP_BACKEND_UNSUPPORTED_CONTROL",
`ACP backend "${params.backend}" does not support ${params.control}.`,
);
}
export function resolveRuntimeIdleTtlMs(cfg: OpenClawConfig): number {
const ttlMinutes = cfg.acp?.runtime?.ttlMinutes;
if (typeof ttlMinutes !== "number" || !Number.isFinite(ttlMinutes) || ttlMinutes <= 0) {
return 0;
}
return Math.round(ttlMinutes * 60 * 1000);
}
export function hasLegacyAcpIdentityProjection(meta: SessionAcpMeta): boolean {
const raw = meta as Record<string, unknown>;
return (
Object.hasOwn(raw, "backendSessionId") ||
Object.hasOwn(raw, "agentSessionId") ||
Object.hasOwn(raw, "sessionIdsProvisional")
);
}