

























@@ -15,7 +15,6 @@ import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/st
1515import { resolveRuntimeConfigCacheKey } from "../../config/runtime-snapshot.js";
1616import type { OpenClawConfig } from "../../config/types.openclaw.js";
1717import { logVerbose } from "../../globals.js";
18-import { formatErrorMessage } from "../../infra/errors.js";
1918import { normalizeAgentId } from "../../routing/session-key.js";
2019import { isAcpSessionKey } from "../../sessions/session-key-utils.js";
2120import {
@@ -47,6 +46,12 @@ import {
4746} from "./manager.runtime-controls.js";
4847import { ManagerRuntimeHandleCache } from "./manager.runtime-handle-cache.js";
4948import { ensureManagerRuntimeHandle } from "./manager.runtime-handle-ensure.js";
49+import {
50+discardPersistedManagerRuntimeState,
51+isRecoverableManagerAcpxExitError,
52+prepareFreshManagerRuntimeHandleRetry,
53+tryPrepareFreshManagerRuntimeSession,
54+} from "./manager.runtime-resume-state.js";
5055import { consumeAcpTurnStream } from "./manager.turn-stream.js";
5156import {
5257awaitTurnWithTimeout,
@@ -912,14 +917,16 @@ export class AcpSessionManager {
912917 ? "ACP turn failed before completion."
913918 : "Could not initialize ACP session runtime.",
914919});
915-retryFreshHandle = await this.prepareFreshHandleRetry({
920+retryFreshHandle = await prepareFreshManagerRuntimeHandleRetry({
916921 attempt,
917922cfg: input.cfg,
918923 sessionKey,
919924error: acpError,
920925 sawTurnOutput,
921926 runtime,
922927 meta,
928+runtimeHandles: this.runtimeHandles,
929+writeSessionMeta: async (writeParams) => await this.writeSessionMeta(writeParams),
923930});
924931if (retryFreshHandle) {
925932continue;
@@ -1103,18 +1110,13 @@ export class AcpSessionManager {
11031110let runtimeNotice: string | undefined;
11041111if (shouldSkipRuntimeClose) {
11051112if (input.discardPersistentState) {
1106-const configuredBackend = (meta.backend || input.cfg.acp?.backend || "").trim();
1107-try {
1108-await this.deps
1109-.getRuntimeBackend(configuredBackend || undefined)
1110-?.runtime.prepareFreshSession?.({
1111- sessionKey,
1112-});
1113-} catch (error) {
1114-logVerbose(
1115-`acp close fast-reset: unable to prepare fresh session for ${sessionKey}: ${error instanceof Error ? error.message : String(error)}`,
1116-);
1117-}
1113+await tryPrepareFreshManagerRuntimeSession({
1114+deps: this.deps,
1115+cfg: input.cfg,
1116+ meta,
1117+ sessionKey,
1118+logPrefix: "acp close fast-reset",
1119+});
11181120}
11191121this.runtimeHandles.clear(sessionKey);
11201122} else {
@@ -1149,23 +1151,17 @@ export class AcpSessionManager {
11491151(input.discardPersistentState && acpError.code === "ACP_SESSION_INIT_FAILED") ||
11501152(input.discardPersistentState &&
11511153acpError.code === "ACP_BACKEND_UNSUPPORTED_CONTROL") ||
1152-this.isRecoverableAcpxExitError(acpError.message))
1154+isRecoverableManagerAcpxExitError(acpError.message))
11531155) {
11541156if (input.discardPersistentState) {
1155-const configuredBackend = (meta.backend || input.cfg.acp?.backend || "").trim();
1156-try {
1157-const runtimeBackend = this.deps.getRuntimeBackend(configuredBackend || undefined);
1158-if (!runtimeBackend) {
1159-throw acpError;
1160-}
1161-await runtimeBackend.runtime.prepareFreshSession?.({
1162- sessionKey,
1163-});
1164-} catch (recoveryError) {
1165-logVerbose(
1166-`acp close recovery: unable to prepare fresh session for ${sessionKey}: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`,
1167-);
1168-}
1157+await tryPrepareFreshManagerRuntimeSession({
1158+deps: this.deps,
1159+cfg: input.cfg,
1160+ meta,
1161+ sessionKey,
1162+logPrefix: "acp close recovery",
1163+missingBackendError: acpError,
1164+});
11691165}
11701166// Treat unavailable backends as terminal for this cached handle so it
11711167// cannot continue counting against maxConcurrentSessions.
@@ -1179,9 +1175,10 @@ export class AcpSessionManager {
1179117511801176let metaCleared = false;
11811177if (input.discardPersistentState && !input.clearMeta) {
1182-await this.discardPersistedRuntimeState({
1178+await discardPersistedManagerRuntimeState({
11831179cfg: input.cfg,
11841180 sessionKey,
1181+writeSessionMeta: async (writeParams) => await this.writeSessionMeta(writeParams),
11851182});
11861183}
11871184@@ -1305,163 +1302,6 @@ export class AcpSessionManager {
13051302this.errorCountsByCode.set(normalized, (this.errorCountsByCode.get(normalized) ?? 0) + 1);
13061303}
130713041308-private async prepareFreshHandleRetry(params: {
1309-attempt: number;
1310-cfg: OpenClawConfig;
1311-sessionKey: string;
1312-error: AcpRuntimeError;
1313-sawTurnOutput: boolean;
1314-runtime?: AcpRuntime;
1315-meta?: SessionAcpMeta;
1316-}): Promise<boolean> {
1317-if (params.attempt > 0 || params.sawTurnOutput) {
1318-return false;
1319-}
1320-if (this.isRecoverableAcpxExitError(params.error.message)) {
1321-this.runtimeHandles.clear(params.sessionKey);
1322-logVerbose(
1323-`acp-manager: retrying ${params.sessionKey} with a fresh runtime handle after early turn failure: ${params.error.message}`,
1324-);
1325-return true;
1326-}
1327-if (
1328-!params.runtime ||
1329-!params.meta ||
1330-params.meta.mode !== "persistent" ||
1331-!this.isRecoverableMissingPersistentSessionError(params.error.message)
1332-) {
1333-return false;
1334-}
1335-const cleared = await this.clearPersistedRuntimeResumeState({
1336-cfg: params.cfg,
1337-sessionKey: params.sessionKey,
1338-});
1339-if (!cleared) {
1340-return false;
1341-}
1342-if (params.runtime.prepareFreshSession) {
1343-try {
1344-await params.runtime.prepareFreshSession({
1345-sessionKey: params.sessionKey,
1346-});
1347-} catch (error) {
1348-logVerbose(
1349-`acp-manager: failed preparing a fresh persistent session for ${params.sessionKey}: ${formatErrorMessage(error)}`,
1350-);
1351-return false;
1352-}
1353-}
1354-this.runtimeHandles.clear(params.sessionKey);
1355-logVerbose(
1356-`acp-manager: retrying ${params.sessionKey} with a fresh persistent session after missing backend resume target: ${params.error.message}`,
1357-);
1358-return true;
1359-}
1360-1361-private isRecoverableAcpxExitError(message: string): boolean {
1362-return /^acpx exited with (code \d+|signal [a-z0-9]+)/i.test(message.trim());
1363-}
1364-1365-private isRecoverableMissingPersistentSessionError(message: string): boolean {
1366-const normalized = message.trim();
1367-return (
1368-/persistent acp session .* could not be resumed/i.test(normalized) &&
1369-/(resource not found|no matching session)/i.test(normalized)
1370-);
1371-}
1372-1373-private async clearPersistedRuntimeResumeState(params: {
1374-cfg: OpenClawConfig;
1375-sessionKey: string;
1376-}): Promise<boolean> {
1377-const now = Date.now();
1378-const updated = await this.writeSessionMeta({
1379-cfg: params.cfg,
1380-sessionKey: params.sessionKey,
1381-mutate: (current, entry) => {
1382-if (!entry) {
1383-return null;
1384-}
1385-const base = current;
1386-if (!base) {
1387-return null;
1388-}
1389-const currentIdentity = resolveSessionIdentityFromMeta(base);
1390-if (!currentIdentity?.acpxSessionId && !currentIdentity?.agentSessionId) {
1391-return base;
1392-}
1393-const nextIdentity = {
1394-state: "pending" as const,
1395- ...(currentIdentity.acpxRecordId ? { acpxRecordId: currentIdentity.acpxRecordId } : {}),
1396-source: currentIdentity.source,
1397-lastUpdatedAt: now,
1398-};
1399-return {
1400-backend: base.backend,
1401-agent: base.agent,
1402-runtimeSessionName: base.runtimeSessionName,
1403-identity: nextIdentity,
1404-mode: base.mode,
1405- ...(base.runtimeOptions ? { runtimeOptions: base.runtimeOptions } : {}),
1406- ...(base.cwd ? { cwd: base.cwd } : {}),
1407-state: base.state,
1408-lastActivityAt: now,
1409- ...(base.lastError ? { lastError: base.lastError } : {}),
1410-};
1411-},
1412-});
1413-if (!updated) {
1414-logVerbose(
1415-`acp-manager: unable to clear persisted runtime resume state for ${params.sessionKey}`,
1416-);
1417-return false;
1418-}
1419-return true;
1420-}
1421-1422-private async discardPersistedRuntimeState(params: {
1423-cfg: OpenClawConfig;
1424-sessionKey: string;
1425-}): Promise<void> {
1426-const now = Date.now();
1427-await this.writeSessionMeta({
1428-cfg: params.cfg,
1429-sessionKey: params.sessionKey,
1430-mutate: (current, entry) => {
1431-if (!entry) {
1432-return null;
1433-}
1434-const base = current;
1435-if (!base) {
1436-return null;
1437-}
1438-const currentIdentity = resolveSessionIdentityFromMeta(base);
1439-const nextIdentity = currentIdentity
1440- ? {
1441-state: "pending" as const,
1442- ...(currentIdentity.acpxRecordId
1443- ? { acpxRecordId: currentIdentity.acpxRecordId }
1444- : {}),
1445-source: currentIdentity.source,
1446-lastUpdatedAt: now,
1447-}
1448- : undefined;
1449-return {
1450-backend: base.backend,
1451-agent: base.agent,
1452-runtimeSessionName: base.runtimeSessionName,
1453- ...(nextIdentity ? { identity: nextIdentity } : {}),
1454-mode: base.mode,
1455- ...(base.runtimeOptions ? { runtimeOptions: base.runtimeOptions } : {}),
1456- ...(base.cwd ? { cwd: base.cwd } : {}),
1457-state: "idle",
1458-lastActivityAt: now,
1459-};
1460-},
1461-failOnError: true,
1462-});
1463-}
1464-14651305private async resolveRuntimeCapabilities(params: {
14661306runtime: AcpRuntime;
14671307handle: AcpRuntimeHandle;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。