@openclaw/acpx 2026.9.3 → 2026.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/activity.svg +3 -0
- package/assets/icon.png +0 -0
- package/dist/{config-D2FTk0K7.js → .setup/config-DvCm0_Dp.mjs} +37 -2
- package/dist/{pi-session-catalog-runtime-BIieInfo.js → .setup/pi-session-catalog-runtime-CbLyvshh.mjs} +5 -14
- package/dist/{register.runtime-CksaMeEx.js → .setup/register.runtime-CTfxhkFm.mjs} +4 -1
- package/dist/{runtime-CSNBTQIs.js → .setup/runtime-Du-YvDnr.mjs} +533 -402
- package/dist/{service-BAf6nuci.js → .setup/service-DZYyo6-7.mjs} +14 -8
- package/dist/{session-owner-migration-D36AH9RC.js → .setup/session-owner-migration-BuKHYP6F.mjs} +4 -4
- package/dist/{session-resource-UWe7qD3m.js → .setup/session-resource-Dzl0U7kK.mjs} +2 -2
- package/dist/doctor-contract-api.js +3 -3
- package/dist/index.js +4 -28
- package/dist/register.runtime.js +1 -1
- package/openclaw.plugin.json +27 -0
- package/package.json +11 -8
- package/skills/acp-router/SKILL.md +2 -2
- package/dist/config-schema-DN_uAi4R.js +0 -37
- /package/dist/{pi-session-paths-EMbd4Hkz.js → .setup/pi-session-paths-EMbd4Hkz.mjs} +0 -0
- /package/dist/{process-lease-B83BGiLj.js → .setup/process-lease-B83BGiLj.mjs} +0 -0
- /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
|
@@ -1,56 +1,125 @@
|
|
|
1
|
-
import { _ as splitCommandParts, a as hashAcpxProcessCommand, g as renderAgentCommand, l as readAcpxProcessLeaseIdentity, t as ACPX_PROBE_LEASE_SESSION_KEY, u as withAcpxLeaseArgs } from "./process-lease-B83BGiLj.
|
|
2
|
-
import { AcpRuntimeError } from "
|
|
3
|
-
import { t as resolveAcpxSessionResource } from "./session-resource-
|
|
4
|
-
import { a as CODEX_ACP_PACKAGE, i as isOpenClawLeaseAwareAcpxProcessCommand, n as cleanupOpenClawOwnedAcpxPendingLease, o as OPENCLAW_CODEX_CONFIG_ARG, r as cleanupOpenClawOwnedAcpxProcessTree } from "./service-
|
|
5
|
-
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
1
|
+
import { _ as splitCommandParts, a as hashAcpxProcessCommand, g as renderAgentCommand, l as readAcpxProcessLeaseIdentity, t as ACPX_PROBE_LEASE_SESSION_KEY, u as withAcpxLeaseArgs } from "./process-lease-B83BGiLj.mjs";
|
|
2
|
+
import { AcpRuntimeError } from "../runtime-api.js";
|
|
3
|
+
import { t as resolveAcpxSessionResource } from "./session-resource-Dzl0U7kK.mjs";
|
|
4
|
+
import { a as CODEX_ACP_PACKAGE, i as isOpenClawLeaseAwareAcpxProcessCommand, n as cleanupOpenClawOwnedAcpxPendingLease, o as OPENCLAW_CODEX_CONFIG_ARG, r as cleanupOpenClawOwnedAcpxProcessTree } from "./service-DZYyo6-7.mjs";
|
|
6
5
|
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
6
|
import path, { resolve } from "node:path";
|
|
8
7
|
import fs from "node:fs/promises";
|
|
9
8
|
import { randomUUID } from "node:crypto";
|
|
10
9
|
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
10
|
+
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
11
11
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
12
12
|
import { isDeepStrictEqual } from "node:util";
|
|
13
13
|
import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, decodeAcpxRuntimeHandleState as decodeAcpxRuntimeHandleState$1, encodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
|
|
14
14
|
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
15
15
|
import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
16
|
-
//#region extensions/acpx/src/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
//#region extensions/acpx/src/runtime-generations.ts
|
|
17
|
+
var AcpxGenerationRegistry = class {
|
|
18
|
+
constructor(sessionStore, delegate, createDelegate) {
|
|
19
|
+
this.sessionStore = sessionStore;
|
|
20
|
+
this.delegate = delegate;
|
|
21
|
+
this.createDelegate = createDelegate;
|
|
22
|
+
this.generations = /* @__PURE__ */ new Map();
|
|
23
|
+
this.isolatedSessionResources = /* @__PURE__ */ new Set();
|
|
24
|
+
this.resetDelegates = /* @__PURE__ */ new Set();
|
|
25
|
+
this.retiringDelegates = /* @__PURE__ */ new WeakSet();
|
|
26
|
+
this.nextGenerationId = 0;
|
|
27
|
+
this.generationOwner = Symbol("acpx-runtime-owner");
|
|
28
|
+
this.stopping = false;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
get isStopping() {
|
|
31
|
+
return this.stopping;
|
|
32
|
+
}
|
|
33
|
+
assertRunning() {
|
|
34
|
+
if (this.stopping) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
35
|
+
}
|
|
36
|
+
fromCaptured(resource, captured) {
|
|
37
|
+
return captured?.owner === this.generationOwner ? captured : this.currentGeneration(resource);
|
|
38
|
+
}
|
|
39
|
+
prepareFresh(resource) {
|
|
40
|
+
const generation = this.generations.get(resource);
|
|
41
|
+
if (generation) this.retireGeneration(generation);
|
|
42
|
+
else this.sessionStore.markFresh(resource);
|
|
43
|
+
}
|
|
44
|
+
resolveDelegate(generation) {
|
|
45
|
+
this.assertRunning();
|
|
46
|
+
if (!generation.delegate) {
|
|
47
|
+
generation.delegate = generation.afterReset ? this.createDelegate() : this.delegate;
|
|
48
|
+
if (generation.delegate !== this.delegate) this.resetDelegates.add(generation.delegate);
|
|
42
49
|
}
|
|
43
|
-
|
|
44
|
-
}
|
|
50
|
+
return generation.delegate;
|
|
51
|
+
}
|
|
52
|
+
currentGeneration(resource) {
|
|
53
|
+
if (this.stopping) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
|
|
54
|
+
let generation = this.generations.get(resource);
|
|
55
|
+
if (!generation) {
|
|
56
|
+
const fresh = this.sessionStore.isFresh(resource);
|
|
57
|
+
const afterReset = fresh || this.isolatedSessionResources.has(resource);
|
|
58
|
+
if (afterReset) this.isolatedSessionResources.add(resource);
|
|
59
|
+
generation = {
|
|
60
|
+
id: ++this.nextGenerationId,
|
|
61
|
+
owner: this.generationOwner,
|
|
62
|
+
resource,
|
|
63
|
+
ensureQueue: new KeyedAsyncQueue(),
|
|
64
|
+
retired: false,
|
|
65
|
+
activeOperations: 0,
|
|
66
|
+
activeRecordOperations: /* @__PURE__ */ new Map(),
|
|
67
|
+
closedRecordIds: /* @__PURE__ */ new Set(),
|
|
68
|
+
records: /* @__PURE__ */ new Map(),
|
|
69
|
+
closeCompleted: false,
|
|
70
|
+
afterReset,
|
|
71
|
+
awaitPriorWrites: fresh
|
|
72
|
+
};
|
|
73
|
+
this.generations.set(resource, generation);
|
|
74
|
+
}
|
|
75
|
+
return generation;
|
|
76
|
+
}
|
|
77
|
+
retireGeneration(generation) {
|
|
78
|
+
generation.retired = true;
|
|
79
|
+
if (this.generations.get(generation.resource) === generation) {
|
|
80
|
+
this.generations.delete(generation.resource);
|
|
81
|
+
this.sessionStore.markFresh(generation.resource);
|
|
82
|
+
}
|
|
83
|
+
this.releaseRetiredDelegate(generation);
|
|
84
|
+
}
|
|
85
|
+
releaseRetiredDelegate(generation) {
|
|
86
|
+
const delegate = generation.delegate;
|
|
87
|
+
if (!generation.retired || generation.activeOperations !== 0 || !delegate || delegate === this.delegate || this.retiringDelegates.has(delegate)) return;
|
|
88
|
+
this.retiringDelegates.add(delegate);
|
|
89
|
+
delegate.shutdown().then(() => this.resetDelegates.delete(delegate), () => {});
|
|
90
|
+
}
|
|
91
|
+
retainGenerationOperation(generation, recordId) {
|
|
92
|
+
generation.activeOperations += 1;
|
|
93
|
+
generation.activeRecordOperations.set(recordId, (generation.activeRecordOperations.get(recordId) ?? 0) + 1);
|
|
94
|
+
return () => {
|
|
95
|
+
const remaining = (generation.activeRecordOperations.get(recordId) ?? 1) - 1;
|
|
96
|
+
if (remaining === 0) {
|
|
97
|
+
generation.activeRecordOperations.delete(recordId);
|
|
98
|
+
generation.closedRecordIds.delete(recordId);
|
|
99
|
+
} else generation.activeRecordOperations.set(recordId, remaining);
|
|
100
|
+
generation.activeOperations -= 1;
|
|
101
|
+
if (!generation.retired && generation.closeCompleted && generation.activeOperations === 0 && generation.records.size === 0 && this.generations.get(generation.resource) === generation) {
|
|
102
|
+
generation.retired = true;
|
|
103
|
+
this.generations.delete(generation.resource);
|
|
104
|
+
}
|
|
105
|
+
this.releaseRetiredDelegate(generation);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
assertCurrentGeneration(generation) {
|
|
109
|
+
if (this.stopping || generation.retired) throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP runtime operation was superseded by reset.");
|
|
110
|
+
}
|
|
111
|
+
async shutdown() {
|
|
112
|
+
this.stopping = true;
|
|
113
|
+
const errors = (await Promise.allSettled([this.delegate, ...this.resetDelegates].map((delegate) => delegate.shutdown()))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
114
|
+
if (errors.length) throw new AggregateError(errors, "ACP runtime shutdown failed.");
|
|
115
|
+
this.resetDelegates.clear();
|
|
116
|
+
this.generations.clear();
|
|
117
|
+
this.isolatedSessionResources.clear();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
45
120
|
//#endregion
|
|
46
|
-
//#region extensions/acpx/src/runtime.ts
|
|
47
|
-
/**
|
|
48
|
-
* OpenClaw ACPX runtime adapter. It wraps the upstream acpx runtime with
|
|
49
|
-
* OpenClaw session metadata, lease tracking, model scoping, and cleanup policy.
|
|
50
|
-
*/
|
|
51
|
-
const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
|
|
52
|
-
const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
|
|
53
|
-
const OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV = "OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY";
|
|
121
|
+
//#region extensions/acpx/src/runtime-session-store.ts
|
|
122
|
+
/** Generation-bound persistence and process-lease metadata for ACPX resets. */
|
|
54
123
|
function withOpenClawLeaseSessionMetadata(record, lease) {
|
|
55
124
|
return {
|
|
56
125
|
...record,
|
|
@@ -58,31 +127,12 @@ function withOpenClawLeaseSessionMetadata(record, lease) {
|
|
|
58
127
|
openclawGatewayInstanceId: lease.gatewayInstanceId
|
|
59
128
|
};
|
|
60
129
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
function codexWrapperStderrLogFileName(leaseId) {
|
|
66
|
-
return `${CODEX_WRAPPER_STDERR_LOG_PREFIX}.${safeDiagnosticFilePart(leaseId)}.log`;
|
|
67
|
-
}
|
|
68
|
-
function compactDiagnosticText(value) {
|
|
69
|
-
return value.replace(/\s+/g, " ").trim();
|
|
70
|
-
}
|
|
71
|
-
function isGenericInternalAcpErrorMessage(message) {
|
|
72
|
-
return message.trim() === "Internal error";
|
|
73
|
-
}
|
|
74
|
-
function isGenericInternalAcpError(error) {
|
|
75
|
-
return error instanceof Error && isGenericInternalAcpErrorMessage(error.message);
|
|
76
|
-
}
|
|
77
|
-
async function readCodexWrapperStderrTail(params) {
|
|
78
|
-
if (!params.wrapperRoot || !params.leaseId) return "";
|
|
79
|
-
try {
|
|
80
|
-
const text = await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8");
|
|
81
|
-
return compactDiagnosticText(redactSensitiveText(sliceUtf16Safe(text, -6e3)));
|
|
82
|
-
} catch {
|
|
83
|
-
return "";
|
|
84
|
-
}
|
|
130
|
+
function captureGenerationRecord(generation, record) {
|
|
131
|
+
if (record.closed || generation.closedRecordIds.has(record.acpxRecordId)) generation.records.delete(record.acpxRecordId);
|
|
132
|
+
else generation.records.set(record.acpxRecordId, record);
|
|
85
133
|
}
|
|
134
|
+
const acpxGenerationKey = Symbol("openclaw.acpxGeneration");
|
|
135
|
+
const acpxOperationScope = new AsyncLocalStorage();
|
|
86
136
|
function readSessionRecordName(record) {
|
|
87
137
|
if (typeof record !== "object" || record === null) return "";
|
|
88
138
|
const { name } = record;
|
|
@@ -120,7 +170,7 @@ function readOpenClawGatewayInstanceIdFromRecord(record) {
|
|
|
120
170
|
return typeof openclawGatewayInstanceId === "string" ? openclawGatewayInstanceId.trim() || void 0 : void 0;
|
|
121
171
|
}
|
|
122
172
|
function extractGeneratedWrapperPath(command) {
|
|
123
|
-
return splitCommandParts(command ?? "").find((part) =>
|
|
173
|
+
return splitCommandParts(command ?? "").find((part) => (part.split(/[\\/]/).pop() ?? "") === "codex-acp-wrapper.mjs" || (part.split(/[\\/]/).pop() ?? "") === "claude-agent-acp-wrapper.mjs") ?? "";
|
|
124
174
|
}
|
|
125
175
|
function selectCurrentSessionLease(params) {
|
|
126
176
|
const sessionKeys = new Set(normalizeStringEntries(params.sessionKeys));
|
|
@@ -132,71 +182,217 @@ function selectCurrentSessionLease(params) {
|
|
|
132
182
|
}
|
|
133
183
|
function createResetAwareSessionStore(baseStore, params) {
|
|
134
184
|
const freshSessionKeys = /* @__PURE__ */ new Set();
|
|
185
|
+
const stateQueue = new KeyedAsyncQueue();
|
|
186
|
+
const pendingWrites = /* @__PURE__ */ new Map();
|
|
135
187
|
return {
|
|
136
188
|
async load(sessionId) {
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
189
|
+
const scope = acpxOperationScope.getStore();
|
|
190
|
+
if (scope?.closeRecord && (sessionId === scope.generation.resource || sessionId === scope.closeRecord.acpxRecordId)) return scope.closeRecord;
|
|
191
|
+
const resource = scope?.generation.resource ?? sessionId.trim();
|
|
192
|
+
const pending = pendingWrites.get(sessionId.trim());
|
|
193
|
+
if (pending && (scope?.generation.awaitPriorWrites || freshSessionKeys.has(resource))) await Promise.allSettled(pending);
|
|
194
|
+
const load = async () => {
|
|
195
|
+
if (scope?.generation.retired) return;
|
|
196
|
+
const normalized = sessionId.trim();
|
|
197
|
+
if (normalized && freshSessionKeys.has(normalized)) return;
|
|
198
|
+
const record = await baseStore.load(sessionId);
|
|
199
|
+
if (scope?.generation.retired || freshSessionKeys.has(scope?.generation.resource ?? normalized)) return;
|
|
200
|
+
if (scope && record) captureGenerationRecord(scope.generation, record);
|
|
201
|
+
if (!record || !params?.leaseStore || !params.gatewayInstanceId) return record;
|
|
202
|
+
const sessionName = readSessionRecordName(record) || normalized;
|
|
203
|
+
const lease = selectCurrentSessionLease({
|
|
204
|
+
leases: await params.leaseStore.listOpen(params.gatewayInstanceId),
|
|
205
|
+
sessionKeys: [sessionName, normalized],
|
|
206
|
+
rootPid: readRecordAgentPid(record)
|
|
207
|
+
});
|
|
208
|
+
if (!lease) return record;
|
|
209
|
+
if (scope?.generation.retired) return;
|
|
210
|
+
const leasedRecord = withOpenClawLeaseSessionMetadata(record, lease);
|
|
211
|
+
if (scope) captureGenerationRecord(scope.generation, leasedRecord);
|
|
212
|
+
return leasedRecord;
|
|
213
|
+
};
|
|
214
|
+
return await load();
|
|
149
215
|
},
|
|
150
216
|
async save(record) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
217
|
+
const scope = acpxOperationScope.getStore();
|
|
218
|
+
if (scope) captureGenerationRecord(scope.generation, record);
|
|
219
|
+
const resource = record.acpxRecordId;
|
|
220
|
+
const retiredCloseRecord = scope?.generation.retired && record.closed && record.acpx?.reset_on_next_ensure === true ? scope.closeRecord : void 0;
|
|
221
|
+
const writeRecord = async () => {
|
|
222
|
+
if (scope?.generation.retired) {
|
|
223
|
+
if (!retiredCloseRecord) return;
|
|
224
|
+
const persisted = await baseStore.load(record.acpxRecordId);
|
|
225
|
+
if (!persisted || persisted.acpxRecordId !== retiredCloseRecord.acpxRecordId || persisted.acpSessionId !== retiredCloseRecord.acpSessionId || persisted.createdAt !== retiredCloseRecord.createdAt) return;
|
|
226
|
+
await baseStore.save(record);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
let recordToSave = record;
|
|
230
|
+
const launch = params?.launchScope?.getStore();
|
|
231
|
+
const sessionName = readSessionRecordName(record);
|
|
232
|
+
const agentCommand = readRecordAgentCommand(record);
|
|
233
|
+
const leasedCommand = launch?.leasedCommand ?? agentCommand;
|
|
234
|
+
const leaseIdentity = launch ?? readAcpxProcessLeaseIdentity(leasedCommand);
|
|
235
|
+
if (params?.leaseStore && params.gatewayInstanceId && params.wrapperRoot && (!launch || sessionName === launch.sessionKey) && leasedCommand && leaseIdentity?.gatewayInstanceId === params.gatewayInstanceId && isOpenClawLeaseAwareAcpxProcessCommand({
|
|
236
|
+
command: leasedCommand,
|
|
237
|
+
wrapperRoot: params.wrapperRoot
|
|
238
|
+
})) {
|
|
239
|
+
const existing = await params.leaseStore.load(leaseIdentity.leaseId);
|
|
240
|
+
if (scope?.generation.retired) return;
|
|
241
|
+
if (!existing || existing.gatewayInstanceId === leaseIdentity.gatewayInstanceId && existing.sessionKey === sessionName && existing.wrapperRoot === params.wrapperRoot) {
|
|
242
|
+
const adoptingLease = Boolean(launch && !isDeepStrictEqual(splitCommandParts(launch.resolvedCommand), splitCommandParts(launch.leasedCommand)));
|
|
243
|
+
const persistedCommand = launch && !adoptingLease ? launch.resolvedCommand : leasedCommand;
|
|
244
|
+
recordToSave = withOpenClawLeaseSessionMetadata({
|
|
245
|
+
...adoptingLease ? {
|
|
246
|
+
...record,
|
|
247
|
+
pid: void 0,
|
|
248
|
+
processId: void 0,
|
|
249
|
+
agentStartedAt: void 0
|
|
250
|
+
} : record,
|
|
251
|
+
agentCommand: renderAgentCommand(persistedCommand),
|
|
252
|
+
agentArgv: Array.isArray(persistedCommand) ? persistedCommand : void 0
|
|
253
|
+
}, leaseIdentity);
|
|
254
|
+
}
|
|
189
255
|
}
|
|
256
|
+
if (scope?.generation.retired) return;
|
|
257
|
+
await baseStore.save(recordToSave);
|
|
258
|
+
if (scope && !scope.generation.retired) scope.generation.awaitPriorWrites = false;
|
|
259
|
+
if (sessionName && !scope?.generation.retired) freshSessionKeys.delete(sessionName);
|
|
260
|
+
};
|
|
261
|
+
const writes = pendingWrites.get(resource) ?? /* @__PURE__ */ new Set();
|
|
262
|
+
const previous = [...writes];
|
|
263
|
+
const write = scope?.generation.awaitPriorWrites || retiredCloseRecord ? Promise.allSettled(previous).then(() => stateQueue.enqueue(resource, writeRecord)) : writeRecord();
|
|
264
|
+
writes.add(write);
|
|
265
|
+
pendingWrites.set(resource, writes);
|
|
266
|
+
try {
|
|
267
|
+
await write;
|
|
268
|
+
} finally {
|
|
269
|
+
writes.delete(write);
|
|
270
|
+
if (writes.size === 0 && pendingWrites.get(resource) === writes) pendingWrites.delete(resource);
|
|
190
271
|
}
|
|
191
|
-
await baseStore.save(recordToSave);
|
|
192
|
-
if (sessionName) freshSessionKeys.delete(sessionName);
|
|
193
272
|
},
|
|
273
|
+
loadForClose: (sessionKey) => baseStore.load(sessionKey),
|
|
274
|
+
isFresh: (sessionKey) => freshSessionKeys.has(sessionKey),
|
|
194
275
|
markFresh(sessionKey) {
|
|
195
276
|
const normalized = sessionKey.trim();
|
|
196
277
|
if (normalized) freshSessionKeys.add(normalized);
|
|
197
278
|
}
|
|
198
279
|
};
|
|
199
280
|
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region extensions/acpx/src/runtime-process-cleanup.ts
|
|
283
|
+
/** Capture OpenClaw wrapper cleanup ownership before a backend close can yield. */
|
|
284
|
+
async function prepareAcpxProcessCleanup(params) {
|
|
285
|
+
const { leaseStore, gatewayInstanceId, wrapperRoot, deps } = params;
|
|
286
|
+
const rootPid = readRecordAgentPid(params.record);
|
|
287
|
+
const rootCommand = params.command ? renderAgentCommand(params.command) : void 0;
|
|
288
|
+
const identity = readAcpxProcessLeaseIdentity(params.command);
|
|
289
|
+
const leaseId = readOpenClawLeaseIdFromRecord(params.record) ?? identity?.leaseId;
|
|
290
|
+
const expectedGatewayInstanceId = readOpenClawGatewayInstanceIdFromRecord(params.record) ?? identity?.gatewayInstanceId;
|
|
291
|
+
const sessionKeys = [params.sessionKey, readSessionRecordName(params.record)];
|
|
292
|
+
const openLeases = rootPid && gatewayInstanceId && leaseStore ? await leaseStore.listOpen(gatewayInstanceId) : [];
|
|
293
|
+
const selectedLease = rootPid ? selectCurrentSessionLease({
|
|
294
|
+
leases: openLeases,
|
|
295
|
+
sessionKeys,
|
|
296
|
+
rootPid
|
|
297
|
+
}) : void 0;
|
|
298
|
+
const loadedLease = leaseId ? await leaseStore?.load(leaseId) : void 0;
|
|
299
|
+
const ownedLease = selectedLease ?? (loadedLease && loadedLease.gatewayInstanceId === gatewayInstanceId && (!rootPid || loadedLease.rootPid === rootPid) && sessionKeys.includes(loadedLease.sessionKey) ? loadedLease : void 0);
|
|
300
|
+
const lease = ownedLease ? { ...ownedLease } : void 0;
|
|
301
|
+
return async () => {
|
|
302
|
+
if (lease && lease.gatewayInstanceId === gatewayInstanceId) {
|
|
303
|
+
await leaseStore?.markState(lease.leaseId, "closing");
|
|
304
|
+
const result = lease.rootPid > 0 ? await cleanupOpenClawOwnedAcpxProcessTree({
|
|
305
|
+
rootPid: lease.rootPid,
|
|
306
|
+
rootCommand,
|
|
307
|
+
expectedLeaseId: lease.leaseId,
|
|
308
|
+
expectedGatewayInstanceId: lease.gatewayInstanceId,
|
|
309
|
+
wrapperRoot: lease.wrapperRoot,
|
|
310
|
+
deps
|
|
311
|
+
}) : await cleanupOpenClawOwnedAcpxPendingLease({
|
|
312
|
+
leaseId: lease.leaseId,
|
|
313
|
+
gatewayInstanceId: lease.gatewayInstanceId,
|
|
314
|
+
wrapperRoot: lease.wrapperRoot,
|
|
315
|
+
wrapperPath: lease.wrapperPath,
|
|
316
|
+
deps
|
|
317
|
+
});
|
|
318
|
+
await leaseStore?.markState(lease.leaseId, result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" || lease.rootPid <= 0 && (result.skippedReason === "ambiguous-root" || result.skippedReason === "unverified-root") ? "open" : result.terminatedPids.length > 0 || result.skippedReason === "missing-root" ? "closed" : "lost");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (!rootPid || !rootCommand) return;
|
|
322
|
+
await cleanupOpenClawOwnedAcpxProcessTree({
|
|
323
|
+
rootPid,
|
|
324
|
+
rootCommand,
|
|
325
|
+
...leaseId ? { expectedLeaseId: leaseId } : {},
|
|
326
|
+
...expectedGatewayInstanceId ? { expectedGatewayInstanceId } : {},
|
|
327
|
+
wrapperRoot,
|
|
328
|
+
deps
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region extensions/acpx/src/session-owner.ts
|
|
334
|
+
function requireAcpxOwnerMigration(sessionKey) {
|
|
335
|
+
throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", `ACP session "${sessionKey}" has an unqualified or unverifiable backend locator. Stop the Gateway and run "openclaw doctor --fix" to migrate ownership without losing history, then restart.`, { detailCode: "SESSION_OWNER_MIGRATION_REQUIRED" });
|
|
336
|
+
}
|
|
337
|
+
function assertAcpxSessionOwnerLocator(target, legacyBareSessionKeys) {
|
|
338
|
+
const resource = resolveAcpxSessionResource(target);
|
|
339
|
+
const qualified = resource === target.sessionKey.trim().toLowerCase();
|
|
340
|
+
const persisted = target.persistedHandle;
|
|
341
|
+
if (!qualified && (legacyBareSessionKeys?.has(target.sessionKey.trim().toLowerCase()) || legacyBareSessionKeys?.has(resource) && !persisted)) requireAcpxOwnerMigration(target.sessionKey);
|
|
342
|
+
if (persisted) {
|
|
343
|
+
const decoded = decodeAcpxRuntimeHandleState$1(persisted.runtimeSessionName);
|
|
344
|
+
if (!qualified && !decoded || decoded && (decoded.name !== resource || persisted.acpxRecordId && decoded.acpxRecordId !== persisted.acpxRecordId)) requireAcpxOwnerMigration(target.sessionKey);
|
|
345
|
+
}
|
|
346
|
+
return resource;
|
|
347
|
+
}
|
|
348
|
+
/** Preserve physical oneshot record IDs and the upstream-encoded runtime handle. */
|
|
349
|
+
function toAcpxResourceInput(input) {
|
|
350
|
+
const sessionKey = assertAcpxSessionOwnerLocator({
|
|
351
|
+
...input.handle,
|
|
352
|
+
persistedHandle: input.handle
|
|
353
|
+
});
|
|
354
|
+
return {
|
|
355
|
+
...input,
|
|
356
|
+
handle: {
|
|
357
|
+
...input.handle,
|
|
358
|
+
sessionKey
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
//#endregion
|
|
363
|
+
//#region extensions/acpx/src/runtime.ts
|
|
364
|
+
/**
|
|
365
|
+
* OpenClaw ACPX runtime adapter. It wraps the upstream acpx runtime with
|
|
366
|
+
* OpenClaw session metadata, lease tracking, model scoping, and cleanup policy.
|
|
367
|
+
*/
|
|
368
|
+
const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
|
|
369
|
+
const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
|
|
370
|
+
const OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV = "OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY";
|
|
371
|
+
const CODEX_WRAPPER_STDERR_LOG_PREFIX = "codex-acp-wrapper.stderr";
|
|
372
|
+
function safeDiagnosticFilePart(value) {
|
|
373
|
+
return value.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120) || "unknown";
|
|
374
|
+
}
|
|
375
|
+
function codexWrapperStderrLogFileName(leaseId) {
|
|
376
|
+
return `${CODEX_WRAPPER_STDERR_LOG_PREFIX}.${safeDiagnosticFilePart(leaseId)}.log`;
|
|
377
|
+
}
|
|
378
|
+
function compactDiagnosticText(value) {
|
|
379
|
+
return value.replace(/\s+/g, " ").trim();
|
|
380
|
+
}
|
|
381
|
+
function isGenericInternalAcpErrorMessage(message) {
|
|
382
|
+
return message.trim() === "Internal error";
|
|
383
|
+
}
|
|
384
|
+
function isGenericInternalAcpError(error) {
|
|
385
|
+
return error instanceof Error && isGenericInternalAcpErrorMessage(error.message);
|
|
386
|
+
}
|
|
387
|
+
async function readCodexWrapperStderrTail(params) {
|
|
388
|
+
if (!params.wrapperRoot || !params.leaseId) return "";
|
|
389
|
+
try {
|
|
390
|
+
const text = await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8");
|
|
391
|
+
return compactDiagnosticText(redactSensitiveText(sliceUtf16Safe(text, -6e3)));
|
|
392
|
+
} catch {
|
|
393
|
+
return "";
|
|
394
|
+
}
|
|
395
|
+
}
|
|
200
396
|
const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
|
|
201
397
|
const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
|
|
202
398
|
const CODEX_ACP_AGENT_ID = "codex";
|
|
@@ -362,7 +558,7 @@ function withAcpxSessionOptions(input) {
|
|
|
362
558
|
...existingOptions,
|
|
363
559
|
model
|
|
364
560
|
} : existingOptions;
|
|
365
|
-
const { modelExplicit: _modelExplicit, ...rest } = input;
|
|
561
|
+
const { modelExplicit: _modelExplicit, thinkingExplicit: _thinkingExplicit, ...rest } = input;
|
|
366
562
|
return {
|
|
367
563
|
...rest,
|
|
368
564
|
...sessionOptions ? { sessionOptions } : {}
|
|
@@ -402,10 +598,6 @@ function resolveAgentCommand(params) {
|
|
|
402
598
|
if (!normalizedAgentName) return;
|
|
403
599
|
return splitCommandParts(params.agentRegistry.resolve(normalizedAgentName));
|
|
404
600
|
}
|
|
405
|
-
function shouldUseDistinctBridgeDelegate(options) {
|
|
406
|
-
const { mcpServers } = options;
|
|
407
|
-
return Array.isArray(mcpServers) && mcpServers.length > 0;
|
|
408
|
-
}
|
|
409
601
|
function withManagedToolsMcpSessionEnv(params) {
|
|
410
602
|
const sessionKey = params.sessionKey.trim();
|
|
411
603
|
if (!params.pluginToolsEnabled && !params.openclawToolsEnabled || !sessionKey || !params.mcpServers?.length) return params.mcpServers;
|
|
@@ -436,12 +628,9 @@ var AcpxRuntime = class {
|
|
|
436
628
|
constructor(options, testOptions) {
|
|
437
629
|
this.ownerAwareSessions = 1;
|
|
438
630
|
this.launchCommandScope = new AsyncLocalStorage();
|
|
439
|
-
this.
|
|
631
|
+
this.sessionScope = new AsyncLocalStorage();
|
|
632
|
+
this.probeQueue = new KeyedAsyncQueue();
|
|
440
633
|
this.launchLeaseScope = new AsyncLocalStorage();
|
|
441
|
-
this.sessionEnsureQueue = new KeyedAsyncQueue();
|
|
442
|
-
this.processLeaseTransitionQueue = new KeyedAsyncQueue();
|
|
443
|
-
this.processLeaseOperationCounts = /* @__PURE__ */ new Map();
|
|
444
|
-
this.uncertainProcessLeaseIds = /* @__PURE__ */ new Set();
|
|
445
634
|
this.legacyBareSessionKeys = new Set(options.openclawLegacyBareSessionKeys);
|
|
446
635
|
const { openclawProcessCleanup, ...delegateTestOptions } = testOptions ?? {};
|
|
447
636
|
this.processCleanupDeps = openclawProcessCleanup;
|
|
@@ -466,69 +655,101 @@ var AcpxRuntime = class {
|
|
|
466
655
|
},
|
|
467
656
|
list: () => this.agentRegistry.list()
|
|
468
657
|
};
|
|
469
|
-
|
|
658
|
+
this.createDelegate = () => new AcpxRuntime$1({
|
|
470
659
|
...options,
|
|
471
660
|
sessionStore: this.sessionStore,
|
|
472
|
-
agentRegistry: this.scopedAgentRegistry
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
661
|
+
agentRegistry: this.scopedAgentRegistry,
|
|
662
|
+
mcpServers: (context) => {
|
|
663
|
+
const servers = typeof options.mcpServers === "function" ? options.mcpServers(context) : options.mcpServers ?? [];
|
|
664
|
+
if (isOpenClawBridgeCommand(context.agentArgv ?? context.agentCommand)) return [];
|
|
665
|
+
const target = this.sessionScope.getStore();
|
|
666
|
+
if (!this.managedToolsMcpBridgeEnabled) return servers;
|
|
667
|
+
if (!target) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP tool bridge has no session owner");
|
|
668
|
+
return withManagedToolsMcpSessionEnv({
|
|
669
|
+
pluginToolsEnabled: this.pluginToolsMcpBridgeEnabled,
|
|
670
|
+
openclawToolsEnabled: this.openclawToolsMcpBridgeEnabled,
|
|
671
|
+
mcpServers: servers,
|
|
672
|
+
...target
|
|
673
|
+
});
|
|
674
|
+
},
|
|
675
|
+
processLifecycle: {
|
|
676
|
+
onBeforeSpawn: async (launch) => {
|
|
677
|
+
await options.processLifecycle?.onBeforeSpawn?.(launch);
|
|
678
|
+
await this.recordProcessLaunch(launch);
|
|
679
|
+
},
|
|
680
|
+
onSpawned: async (process) => {
|
|
681
|
+
await this.recordProcessLaunch(process);
|
|
682
|
+
await options.processLifecycle?.onSpawned?.(process);
|
|
683
|
+
},
|
|
684
|
+
onSpawnFailed: options.processLifecycle?.onSpawnFailed,
|
|
685
|
+
onExit: options.processLifecycle?.onExit
|
|
686
|
+
}
|
|
687
|
+
}, delegateTestOptions);
|
|
688
|
+
this.delegate = this.createDelegate();
|
|
689
|
+
this.generationRegistry = new AcpxGenerationRegistry(this.sessionStore, this.delegate, this.createDelegate);
|
|
481
690
|
this.probeAgent = normalizeAgentName(options.probeAgent) ?? "codex";
|
|
482
691
|
const probeCommand = resolveAgentCommand({
|
|
483
692
|
agentName: this.probeAgent,
|
|
484
693
|
agentRegistry: this.agentRegistry
|
|
485
694
|
});
|
|
486
695
|
this.probeCommand = probeCommand;
|
|
487
|
-
const useBridgeSafeProbe = this.managedToolsMcpBridgeEnabled || isOpenClawBridgeCommand(probeCommand);
|
|
488
|
-
this.probeDelegate = useBridgeSafeProbe ? this.bridgeSafeDelegate : this.delegate;
|
|
489
696
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
697
|
+
async runInGeneration(target, scope, run) {
|
|
698
|
+
const release = this.generationRegistry.retainGenerationOperation(scope.generation, scope.recordId ?? target.acpxRecordId ?? scope.generation.resource);
|
|
699
|
+
try {
|
|
700
|
+
return await this.sessionScope.run(target, () => acpxOperationScope.run(scope, run));
|
|
701
|
+
} finally {
|
|
702
|
+
release();
|
|
703
|
+
}
|
|
493
704
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const cached = this.managedToolsSessionDelegates.get(normalizedSessionKey);
|
|
498
|
-
if (cached) return cached;
|
|
499
|
-
const delegate = new AcpxRuntime$1({
|
|
500
|
-
...this.delegateOptions,
|
|
501
|
-
mcpServers: withManagedToolsMcpSessionEnv({
|
|
502
|
-
pluginToolsEnabled: this.pluginToolsMcpBridgeEnabled,
|
|
503
|
-
openclawToolsEnabled: this.openclawToolsMcpBridgeEnabled,
|
|
504
|
-
mcpServers: this.delegateOptions.mcpServers,
|
|
505
|
-
sessionKey: target.sessionKey,
|
|
506
|
-
agentId: target.agentId
|
|
507
|
-
})
|
|
508
|
-
}, this.delegateTestOptions);
|
|
509
|
-
this.managedToolsSessionDelegates.set(normalizedSessionKey, delegate);
|
|
510
|
-
return delegate;
|
|
705
|
+
resolveDelegateForSession(params) {
|
|
706
|
+
const generation = acpxOperationScope.getStore()?.generation ?? this.generationRegistry.currentGeneration(resolveAcpxSessionResource(params));
|
|
707
|
+
return this.generationRegistry.resolveDelegate(generation);
|
|
511
708
|
}
|
|
512
|
-
|
|
513
|
-
assertAcpxSessionOwnerLocator({
|
|
709
|
+
generationForHandle(handle) {
|
|
710
|
+
const resource = assertAcpxSessionOwnerLocator({
|
|
514
711
|
...handle,
|
|
515
712
|
persistedHandle: handle
|
|
516
713
|
}, this.legacyBareSessionKeys);
|
|
517
|
-
const
|
|
714
|
+
const capturedGeneration = handle[acpxGenerationKey];
|
|
715
|
+
return this.generationRegistry.fromCaptured(resource, capturedGeneration);
|
|
716
|
+
}
|
|
717
|
+
async loadOperationSnapshotForHandle(handle, allowRetired = false, generation = this.generationForHandle(handle)) {
|
|
718
|
+
const resource = generation.resource;
|
|
719
|
+
if (!allowRetired) this.generationRegistry.assertCurrentGeneration(generation);
|
|
720
|
+
const ownedRecord = generation.records.get(handle.acpxRecordId ?? resource);
|
|
721
|
+
if (ownedRecord && (handle.acpxRecordId && ownedRecord.acpxRecordId !== handle.acpxRecordId || handle.backendSessionId && ownedRecord.acpSessionId && ownedRecord.acpSessionId !== handle.backendSessionId)) throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP handle no longer owns this runtime generation.");
|
|
722
|
+
let record = allowRetired ? generation.retired ? ownedRecord : await this.sessionStore.loadForClose(handle.acpxRecordId ?? resource) : await acpxOperationScope.run({ generation }, () => this.sessionStore.load(handle.acpxRecordId ?? resource));
|
|
723
|
+
if (allowRetired && generation.retired && ownedRecord) record = ownedRecord;
|
|
724
|
+
if (allowRetired && record) captureGenerationRecord(generation, record);
|
|
725
|
+
if (!allowRetired) this.generationRegistry.assertCurrentGeneration(generation);
|
|
726
|
+
if (record && (handle.acpxRecordId && handle.acpxRecordId !== record.acpxRecordId || handle.backendSessionId && record.acpSessionId && handle.backendSessionId !== record.acpSessionId)) throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP handle no longer owns this runtime record.");
|
|
727
|
+
const command = readRecordAgentCommand(record) ?? resolveAgentCommand({
|
|
728
|
+
agentName: readAgentFromHandle(handle),
|
|
729
|
+
agentRegistry: this.agentRegistry
|
|
730
|
+
});
|
|
731
|
+
const identity = readAcpxProcessLeaseIdentity(command);
|
|
732
|
+
if (identity && this.processLeaseStore && this.gatewayInstanceId && this.wrapperRoot) {
|
|
733
|
+
const lease = await this.processLeaseStore.load(identity.leaseId);
|
|
734
|
+
if (identity.gatewayInstanceId !== this.gatewayInstanceId) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another gateway`);
|
|
735
|
+
if (lease && (lease.gatewayInstanceId !== identity.gatewayInstanceId || lease.sessionKey !== resolveAcpxSessionResource(handle) || lease.wrapperRoot !== this.wrapperRoot)) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another session`);
|
|
736
|
+
}
|
|
737
|
+
if (!allowRetired) this.generationRegistry.assertCurrentGeneration(generation);
|
|
518
738
|
return {
|
|
519
739
|
record,
|
|
520
|
-
command
|
|
521
|
-
|
|
522
|
-
agentRegistry: this.agentRegistry
|
|
523
|
-
})
|
|
740
|
+
command,
|
|
741
|
+
generation
|
|
524
742
|
};
|
|
525
743
|
}
|
|
526
744
|
resolveDelegateForOperationSnapshot(handle, snapshot) {
|
|
527
|
-
return
|
|
745
|
+
return acpxOperationScope.run({
|
|
746
|
+
generation: snapshot.generation,
|
|
747
|
+
recordId: snapshot.record?.acpxRecordId
|
|
748
|
+
}, () => this.resolveDelegateForSession({
|
|
528
749
|
command: snapshot.command,
|
|
529
750
|
sessionKey: handle.sessionKey,
|
|
530
751
|
agentId: handle.agentId
|
|
531
|
-
});
|
|
752
|
+
}));
|
|
532
753
|
}
|
|
533
754
|
async readReusablePersistentSessionCommand(params) {
|
|
534
755
|
if (params.mode !== "persistent" || !params.command) return;
|
|
@@ -556,10 +777,8 @@ var AcpxRuntime = class {
|
|
|
556
777
|
agent: normalizeAgentName(params.agent) ?? params.agent,
|
|
557
778
|
command: params.reusableCommand ?? params.command
|
|
558
779
|
}, params.run);
|
|
559
|
-
const processLeaseStore = this.processLeaseStore;
|
|
560
780
|
const reusableIdentity = readAcpxProcessLeaseIdentity(params.reusableCommand);
|
|
561
|
-
const
|
|
562
|
-
const leaseId = canReuseLeaseIdentity ? reusableIdentity.leaseId : params.finalizeCompletedProbe ? `probe-${hashAcpxProcessCommand(`${this.gatewayInstanceId}\0${extractGeneratedWrapperPath(params.command)}`)}` : randomUUID();
|
|
781
|
+
const leaseId = reusableIdentity?.gatewayInstanceId === this.gatewayInstanceId ? reusableIdentity.leaseId : params.finalizeCompletedProbe ? `probe-${hashAcpxProcessCommand(`${this.gatewayInstanceId}\0${extractGeneratedWrapperPath(params.command)}`)}` : randomUUID();
|
|
563
782
|
const leasedCommand = withAcpxLeaseArgs({
|
|
564
783
|
command: params.command,
|
|
565
784
|
leaseId,
|
|
@@ -573,149 +792,41 @@ var AcpxRuntime = class {
|
|
|
573
792
|
resolvedCommand: params.reusableCommand ?? leasedCommand,
|
|
574
793
|
leasedCommand
|
|
575
794
|
};
|
|
576
|
-
await this.
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
rootPid: 0,
|
|
587
|
-
commandHash: hashAcpxProcessCommand(leasedCommand),
|
|
588
|
-
startedAt: Date.now(),
|
|
589
|
-
state: "open"
|
|
590
|
-
});
|
|
795
|
+
const result = await this.launchLeaseScope.run(launch, () => this.launchCommandScope.run({
|
|
796
|
+
agent: normalizeAgentName(params.agent) ?? params.agent,
|
|
797
|
+
command: launch.resolvedCommand
|
|
798
|
+
}, params.run));
|
|
799
|
+
if (params.finalizeCompletedProbe) await cleanupOpenClawOwnedAcpxPendingLease({
|
|
800
|
+
leaseId,
|
|
801
|
+
gatewayInstanceId: launch.gatewayInstanceId,
|
|
802
|
+
wrapperRoot: launch.wrapperRoot,
|
|
803
|
+
wrapperPath: extractGeneratedWrapperPath(leasedCommand),
|
|
804
|
+
deps: this.processCleanupDeps
|
|
591
805
|
});
|
|
592
|
-
|
|
593
|
-
const result = await this.launchLeaseScope.run(launch, () => this.launchCommandScope.run({
|
|
594
|
-
agent: normalizeAgentName(params.agent) ?? params.agent,
|
|
595
|
-
command: launch.resolvedCommand
|
|
596
|
-
}, params.run));
|
|
597
|
-
if (params.finalizeCompletedProbe) await this.finalizeCompletedProbeLease(launch);
|
|
598
|
-
else await this.finalizeProcessLeaseForSession(params.sessionKey, launch);
|
|
599
|
-
return result;
|
|
600
|
-
} catch (error) {
|
|
601
|
-
await this.releaseProcessLeaseAfterUncertainFailure(launch);
|
|
602
|
-
throw error;
|
|
603
|
-
}
|
|
806
|
+
return result;
|
|
604
807
|
}
|
|
605
|
-
async
|
|
606
|
-
|
|
607
|
-
const processLeaseStore = this.processLeaseStore;
|
|
608
|
-
const wrapperRoot = this.wrapperRoot;
|
|
609
|
-
const recordPid = readRecordAgentPid(record);
|
|
610
|
-
const command = readRecordAgentCommand(record);
|
|
808
|
+
async recordProcessLaunch(process) {
|
|
809
|
+
const command = [process.command, ...process.args];
|
|
611
810
|
const identity = readAcpxProcessLeaseIdentity(command);
|
|
612
|
-
if (!
|
|
811
|
+
if (!identity || !this.processLeaseStore || !this.wrapperRoot) return;
|
|
812
|
+
const sessionKey = process.scope.kind === "runtime-session" ? process.scope.sessionKey : ACPX_PROBE_LEASE_SESSION_KEY;
|
|
813
|
+
const existing = await this.processLeaseStore.load(identity.leaseId);
|
|
814
|
+
if (identity.gatewayInstanceId !== this.gatewayInstanceId || existing && (existing.gatewayInstanceId !== identity.gatewayInstanceId || existing.sessionKey !== sessionKey || existing.wrapperRoot !== this.wrapperRoot)) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP process lease belongs to another owner");
|
|
815
|
+
if (!isOpenClawLeaseAwareAcpxProcessCommand({
|
|
613
816
|
command,
|
|
614
|
-
wrapperRoot
|
|
615
|
-
}))
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
wrapperRoot,
|
|
626
|
-
wrapperPath: extractGeneratedWrapperPath(command),
|
|
627
|
-
rootPid: recordPid ?? 0,
|
|
628
|
-
commandHash: hashAcpxProcessCommand(command),
|
|
629
|
-
startedAt: Date.now(),
|
|
630
|
-
state: "open"
|
|
631
|
-
});
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
if (existing.gatewayInstanceId !== identity.gatewayInstanceId || existing.sessionKey !== resolveAcpxSessionResource(handle) || existing.wrapperRoot !== wrapperRoot) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another session`);
|
|
635
|
-
});
|
|
636
|
-
return identity;
|
|
637
|
-
}
|
|
638
|
-
async retainProcessLeaseOperation(identity, prepare) {
|
|
639
|
-
await this.processLeaseTransitionQueue.enqueue(identity.leaseId, async () => {
|
|
640
|
-
await prepare();
|
|
641
|
-
this.processLeaseOperationCounts.set(identity.leaseId, (this.processLeaseOperationCounts.get(identity.leaseId) ?? 0) + 1);
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
async releaseProcessLeaseOperation(identity, finalize) {
|
|
645
|
-
if (!identity) return;
|
|
646
|
-
await this.processLeaseTransitionQueue.enqueue(identity.leaseId, async () => {
|
|
647
|
-
const count = this.processLeaseOperationCounts.get(identity.leaseId) ?? 0;
|
|
648
|
-
if (count > 1) {
|
|
649
|
-
this.processLeaseOperationCounts.set(identity.leaseId, count - 1);
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
if (count === 0) return;
|
|
653
|
-
this.processLeaseOperationCounts.delete(identity.leaseId);
|
|
654
|
-
await finalize();
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
async finalizeProcessLeaseForOperation(handle, identity) {
|
|
658
|
-
await this.finalizeProcessLeaseForSession(handle.acpxRecordId ?? resolveAcpxSessionResource(handle), identity);
|
|
659
|
-
}
|
|
660
|
-
async finalizeProcessLeaseForSession(sessionId, identity) {
|
|
661
|
-
if (!identity || !this.processLeaseStore) return;
|
|
662
|
-
const processLeaseStore = this.processLeaseStore;
|
|
663
|
-
await this.releaseProcessLeaseOperation(identity, async () => {
|
|
664
|
-
const lease = await processLeaseStore.load(identity.leaseId);
|
|
665
|
-
if (!lease || lease.gatewayInstanceId !== identity.gatewayInstanceId) return;
|
|
666
|
-
if (lease.rootPid <= 0) {
|
|
667
|
-
if (this.uncertainProcessLeaseIds.has(identity.leaseId)) return;
|
|
668
|
-
await processLeaseStore.markState(identity.leaseId, "lost");
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
this.uncertainProcessLeaseIds.delete(identity.leaseId);
|
|
672
|
-
try {
|
|
673
|
-
const record = await this.sessionStore.load(sessionId);
|
|
674
|
-
const recordIdentity = readAcpxProcessLeaseIdentity(readRecordAgentCommand(record));
|
|
675
|
-
if (recordIdentity?.leaseId !== identity.leaseId || recordIdentity.gatewayInstanceId !== identity.gatewayInstanceId) await processLeaseStore.markState(identity.leaseId, "lost");
|
|
676
|
-
} catch {}
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
async finalizeCompletedProbeLease(identity) {
|
|
680
|
-
if (!this.processLeaseStore) return;
|
|
681
|
-
const processLeaseStore = this.processLeaseStore;
|
|
682
|
-
await this.releaseProcessLeaseOperation(identity, async () => {
|
|
683
|
-
const lease = await processLeaseStore.load(identity.leaseId);
|
|
684
|
-
if (!lease || lease.gatewayInstanceId !== identity.gatewayInstanceId) return;
|
|
685
|
-
if (lease.rootPid > 0) return;
|
|
686
|
-
await cleanupOpenClawOwnedAcpxPendingLease({
|
|
687
|
-
leaseId: lease.leaseId,
|
|
688
|
-
gatewayInstanceId: lease.gatewayInstanceId,
|
|
689
|
-
wrapperRoot: lease.wrapperRoot,
|
|
690
|
-
wrapperPath: lease.wrapperPath,
|
|
691
|
-
deps: this.processCleanupDeps
|
|
692
|
-
});
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
async releaseProcessLeaseAfterUncertainFailure(identity) {
|
|
696
|
-
if (!identity || !this.processLeaseStore) return;
|
|
697
|
-
this.uncertainProcessLeaseIds.add(identity.leaseId);
|
|
698
|
-
const processLeaseStore = this.processLeaseStore;
|
|
699
|
-
await this.releaseProcessLeaseOperation(identity, async () => {
|
|
700
|
-
const lease = await processLeaseStore.load(identity.leaseId);
|
|
701
|
-
if (!lease || lease.gatewayInstanceId !== identity.gatewayInstanceId || lease.rootPid > 0) this.uncertainProcessLeaseIds.delete(identity.leaseId);
|
|
817
|
+
wrapperRoot: this.wrapperRoot
|
|
818
|
+
})) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP process lease has no owned wrapper");
|
|
819
|
+
await this.processLeaseStore.save({
|
|
820
|
+
...identity,
|
|
821
|
+
sessionKey,
|
|
822
|
+
wrapperRoot: this.wrapperRoot,
|
|
823
|
+
wrapperPath: extractGeneratedWrapperPath(command),
|
|
824
|
+
rootPid: "pid" in process ? process.pid : 0,
|
|
825
|
+
commandHash: hashAcpxProcessCommand(command),
|
|
826
|
+
startedAt: "startedAt" in process ? Date.parse(process.startedAt) : Date.now(),
|
|
827
|
+
state: "open"
|
|
702
828
|
});
|
|
703
829
|
}
|
|
704
|
-
async runWithProcessLeaseForHandle(handle, record, run) {
|
|
705
|
-
const identity = await this.prepareProcessLeaseForOperation(handle, record);
|
|
706
|
-
try {
|
|
707
|
-
return await run();
|
|
708
|
-
} finally {
|
|
709
|
-
await this.finalizeProcessLeaseForOperation(handle, identity);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
async finalizeProcessLeaseAfter(handle, identityPromise, resultPromise) {
|
|
713
|
-
try {
|
|
714
|
-
return await resultPromise;
|
|
715
|
-
} finally {
|
|
716
|
-
await this.finalizeProcessLeaseForOperation(handle, await identityPromise);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
830
|
async withCodexWrapperDiagnostics(params) {
|
|
720
831
|
try {
|
|
721
832
|
return await params.run();
|
|
@@ -736,69 +847,47 @@ var AcpxRuntime = class {
|
|
|
736
847
|
leaseId: readOpenClawLeaseIdFromRecord(record)
|
|
737
848
|
});
|
|
738
849
|
}
|
|
739
|
-
async
|
|
740
|
-
|
|
741
|
-
const rootPid = readRecordAgentPid(record);
|
|
742
|
-
const sessionKeys = [resolveAcpxSessionResource(handle), readSessionRecordName(record)];
|
|
743
|
-
const selectedLease = selectCurrentSessionLease({
|
|
744
|
-
leases: this.gatewayInstanceId && this.processLeaseStore ? await this.processLeaseStore.listOpen(this.gatewayInstanceId) : [],
|
|
745
|
-
sessionKeys,
|
|
746
|
-
rootPid
|
|
747
|
-
});
|
|
748
|
-
const loadedLease = leaseId ? await this.processLeaseStore?.load(leaseId) : void 0;
|
|
749
|
-
const lease = selectedLease ?? (loadedLease && loadedLease.gatewayInstanceId === this.gatewayInstanceId && (!rootPid || loadedLease.rootPid === rootPid) && sessionKeys.includes(loadedLease.sessionKey) ? loadedLease : void 0);
|
|
750
|
-
if (lease && lease.gatewayInstanceId === this.gatewayInstanceId && lease.rootPid > 0) {
|
|
751
|
-
await this.processLeaseStore?.markState(lease.leaseId, "closing");
|
|
752
|
-
const result = await cleanupOpenClawOwnedAcpxProcessTree({
|
|
753
|
-
rootPid: lease.rootPid,
|
|
754
|
-
rootCommand: record?.agentCommand,
|
|
755
|
-
expectedLeaseId: lease.leaseId,
|
|
756
|
-
expectedGatewayInstanceId: lease.gatewayInstanceId,
|
|
757
|
-
wrapperRoot: lease.wrapperRoot,
|
|
758
|
-
deps: this.processCleanupDeps
|
|
759
|
-
});
|
|
760
|
-
await this.processLeaseStore?.markState(lease.leaseId, result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" ? "open" : result.terminatedPids.length > 0 || result.skippedReason === "missing-root" ? "closed" : "lost");
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
const rootCommand = readRecordAgentCommand(record) ?? resolveAgentCommand({
|
|
764
|
-
agentName: readAgentFromHandle(handle),
|
|
765
|
-
agentRegistry: this.agentRegistry
|
|
766
|
-
});
|
|
767
|
-
if (!rootPid || !rootCommand) return;
|
|
768
|
-
const expectedGatewayInstanceId = readOpenClawGatewayInstanceIdFromRecord(record);
|
|
769
|
-
await cleanupOpenClawOwnedAcpxProcessTree({
|
|
770
|
-
rootPid,
|
|
771
|
-
rootCommand: renderAgentCommand(rootCommand),
|
|
772
|
-
...leaseId ? { expectedLeaseId: leaseId } : {},
|
|
773
|
-
...expectedGatewayInstanceId ? { expectedGatewayInstanceId } : {},
|
|
774
|
-
wrapperRoot: this.wrapperRoot,
|
|
775
|
-
deps: this.processCleanupDeps
|
|
776
|
-
});
|
|
850
|
+
async shutdown() {
|
|
851
|
+
await this.generationRegistry.shutdown();
|
|
777
852
|
}
|
|
778
853
|
isHealthy() {
|
|
779
|
-
return this.
|
|
854
|
+
return this.delegate.isHealthy();
|
|
780
855
|
}
|
|
781
856
|
async probeAvailability() {
|
|
782
|
-
await this.runWithLaunchLease({
|
|
857
|
+
await this.probeQueue.enqueue(this.probeAgent, () => this.runWithLaunchLease({
|
|
783
858
|
agent: this.probeAgent,
|
|
784
859
|
sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
|
|
785
860
|
command: this.probeCommand,
|
|
786
861
|
finalizeCompletedProbe: true,
|
|
787
|
-
run: () => this.
|
|
788
|
-
});
|
|
862
|
+
run: () => this.delegate.probeAvailability()
|
|
863
|
+
}));
|
|
789
864
|
}
|
|
790
865
|
async doctor() {
|
|
791
|
-
return await this.runWithLaunchLease({
|
|
866
|
+
return await this.probeQueue.enqueue(this.probeAgent, () => this.runWithLaunchLease({
|
|
792
867
|
agent: this.probeAgent,
|
|
793
868
|
sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
|
|
794
869
|
command: this.probeCommand,
|
|
795
870
|
finalizeCompletedProbe: true,
|
|
796
|
-
run: () => this.
|
|
797
|
-
});
|
|
871
|
+
run: () => this.delegate.doctor()
|
|
872
|
+
}));
|
|
798
873
|
}
|
|
799
874
|
async ensureSession(input) {
|
|
800
875
|
const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
|
|
801
|
-
|
|
876
|
+
const generation = this.generationRegistry.currentGeneration(resource);
|
|
877
|
+
return await generation.ensureQueue.enqueue(resource + "\0" + generation.id, () => this.runInGeneration(input, { generation }, async () => {
|
|
878
|
+
this.generationRegistry.assertCurrentGeneration(generation);
|
|
879
|
+
const handle = {
|
|
880
|
+
...await this.ensureSessionUnlocked(input),
|
|
881
|
+
[acpxGenerationKey]: generation
|
|
882
|
+
};
|
|
883
|
+
if (generation.retired && !this.generationRegistry.isStopping) await this.close({
|
|
884
|
+
handle,
|
|
885
|
+
reason: "superseded-initialization",
|
|
886
|
+
discardPersistentState: true
|
|
887
|
+
});
|
|
888
|
+
this.generationRegistry.assertCurrentGeneration(generation);
|
|
889
|
+
return handle;
|
|
890
|
+
}));
|
|
802
891
|
}
|
|
803
892
|
async ensureSessionUnlocked(logicalInput) {
|
|
804
893
|
assertSupportedRuntimeSessionMode(logicalInput.mode);
|
|
@@ -820,20 +909,23 @@ var AcpxRuntime = class {
|
|
|
820
909
|
sessionKey: resolveAcpxSessionResource(logicalInput)
|
|
821
910
|
};
|
|
822
911
|
const isCodexAcp = normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
|
|
912
|
+
const dropInheritedCodexMax = isCodexAcp && input.thinking === "max" && input.thinkingExplicit === false;
|
|
913
|
+
const effectiveInput = dropInheritedCodexMax ? { ...input } : input;
|
|
914
|
+
if (dropInheritedCodexMax) delete effectiveInput.thinking;
|
|
823
915
|
const claudeModelOverride = isClaudeAcpCommand(command) ? normalizeClaudeAcpModelOverride(input.model) : void 0;
|
|
824
|
-
const codexClassification = isCodexAcp ? classifyCodexAcpModelRequest(
|
|
916
|
+
const codexClassification = isCodexAcp ? classifyCodexAcpModelRequest(effectiveInput.model, effectiveInput.thinking) : void 0;
|
|
825
917
|
if (codexClassification?.kind === "unsupported" && input.modelExplicit) failUnsupportedCodexAcpModel(input.model ?? "");
|
|
826
918
|
const classifiedCodexOverride = codexClassification?.kind === "override" ? codexClassification.override : codexClassification?.thinkingOverride;
|
|
827
919
|
const codexModelOverride = classifiedCodexOverride && Object.keys(classifiedCodexOverride).length > 0 ? classifiedCodexOverride : void 0;
|
|
828
|
-
const requestedModel =
|
|
920
|
+
const requestedModel = effectiveInput.model?.trim();
|
|
829
921
|
const appliedModel = isCodexAcp && requestedModel ? codexModelOverride?.model ? {
|
|
830
922
|
kind: "applied",
|
|
831
923
|
model: requestedModel
|
|
832
924
|
} : { kind: "dropped" } : void 0;
|
|
833
|
-
const ensureInput = isCodexAcp ? withCodexSessionModel(
|
|
834
|
-
...
|
|
925
|
+
const ensureInput = isCodexAcp ? withCodexSessionModel(effectiveInput, codexModelOverride) : claudeModelOverride ? {
|
|
926
|
+
...effectiveInput,
|
|
835
927
|
model: claudeModelOverride
|
|
836
|
-
} :
|
|
928
|
+
} : effectiveInput;
|
|
837
929
|
const stableLaunchCommand = codexModelOverride && command ? appendCodexAcpConfigOverrides(command, codexModelOverride) : command;
|
|
838
930
|
const reusableCommand = await this.readReusablePersistentSessionCommand({
|
|
839
931
|
sessionKey: input.sessionKey,
|
|
@@ -855,7 +947,8 @@ var AcpxRuntime = class {
|
|
|
855
947
|
})
|
|
856
948
|
}),
|
|
857
949
|
...logicalTarget,
|
|
858
|
-
...appliedModel ? { appliedModel } : {}
|
|
950
|
+
...appliedModel ? { appliedModel } : {},
|
|
951
|
+
...dropInheritedCodexMax ? { appliedThinking: { kind: "dropped" } } : {}
|
|
859
952
|
};
|
|
860
953
|
}
|
|
861
954
|
async *runTurn(input) {
|
|
@@ -888,18 +981,27 @@ var AcpxRuntime = class {
|
|
|
888
981
|
fallbackCode: "ACP_TURN_FAILED",
|
|
889
982
|
run
|
|
890
983
|
});
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
const { command } = snapshot;
|
|
984
|
+
const turnPromise = this.loadOperationSnapshotForHandle(input.handle).then((snapshot) => {
|
|
985
|
+
const { command, generation } = snapshot;
|
|
986
|
+
this.generationRegistry.assertCurrentGeneration(generation);
|
|
895
987
|
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
896
|
-
return withTurnDiagnostics(command, async () =>
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
988
|
+
return this.sessionScope.run(input.handle, () => acpxOperationScope.run({ generation }, () => withTurnDiagnostics(command, async () => {
|
|
989
|
+
const release = this.generationRegistry.retainGenerationOperation(generation, snapshot.record?.acpxRecordId ?? input.handle.acpxRecordId ?? generation.resource);
|
|
990
|
+
try {
|
|
991
|
+
const turn = delegate.startTurn({
|
|
992
|
+
...toAcpxResourceInput(input),
|
|
993
|
+
timeoutMs: 0
|
|
994
|
+
});
|
|
995
|
+
turn.result.then(release, release);
|
|
996
|
+
return {
|
|
997
|
+
command,
|
|
998
|
+
turn
|
|
999
|
+
};
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
release();
|
|
1002
|
+
throw error;
|
|
1003
|
+
}
|
|
1004
|
+
})));
|
|
903
1005
|
});
|
|
904
1006
|
return {
|
|
905
1007
|
requestId: input.requestId,
|
|
@@ -915,7 +1017,7 @@ var AcpxRuntime = class {
|
|
|
915
1017
|
await withTurnDiagnostics(command, () => Promise.reject(error));
|
|
916
1018
|
}
|
|
917
1019
|
} },
|
|
918
|
-
result:
|
|
1020
|
+
result: turnPromise.then(({ command, turn }) => withTurnDiagnostics(command, async () => {
|
|
919
1021
|
const result = await turn.result;
|
|
920
1022
|
if (result.status !== "failed" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(result.error.message)) return result;
|
|
921
1023
|
const stderrTail = await this.readCodexTurnFailureStderr({ handle: input.handle });
|
|
@@ -928,7 +1030,7 @@ var AcpxRuntime = class {
|
|
|
928
1030
|
message: `Internal error: ${stderrTail}`
|
|
929
1031
|
}
|
|
930
1032
|
};
|
|
931
|
-
}))
|
|
1033
|
+
})),
|
|
932
1034
|
cancel(inputArgs) {
|
|
933
1035
|
return turnPromise.then(({ turn }) => turn.cancel(inputArgs));
|
|
934
1036
|
},
|
|
@@ -937,20 +1039,33 @@ var AcpxRuntime = class {
|
|
|
937
1039
|
}
|
|
938
1040
|
};
|
|
939
1041
|
}
|
|
940
|
-
getCapabilities(input) {
|
|
941
|
-
|
|
1042
|
+
async getCapabilities(input) {
|
|
1043
|
+
const capabilities = await this.delegate.getCapabilities(input?.handle ? toAcpxResourceInput({ handle: input.handle }) : input);
|
|
1044
|
+
return {
|
|
1045
|
+
...capabilities,
|
|
1046
|
+
controls: capabilities.controls.filter((control) => control !== "session/set_model")
|
|
1047
|
+
};
|
|
942
1048
|
}
|
|
943
1049
|
async getStatus(input) {
|
|
944
1050
|
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
945
|
-
return this.
|
|
1051
|
+
return this.runInGeneration(input.handle, {
|
|
1052
|
+
generation: snapshot.generation,
|
|
1053
|
+
recordId: snapshot.record?.acpxRecordId
|
|
1054
|
+
}, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).getStatus(toAcpxResourceInput(input)));
|
|
946
1055
|
}
|
|
947
1056
|
async setMode(input) {
|
|
948
1057
|
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
949
|
-
await this.
|
|
1058
|
+
await this.runInGeneration(input.handle, {
|
|
1059
|
+
generation: snapshot.generation,
|
|
1060
|
+
recordId: snapshot.record?.acpxRecordId
|
|
1061
|
+
}, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).setMode(toAcpxResourceInput(input)));
|
|
950
1062
|
}
|
|
951
1063
|
async setConfigOption(input) {
|
|
952
1064
|
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
953
|
-
return await this.
|
|
1065
|
+
return await this.runInGeneration(input.handle, {
|
|
1066
|
+
generation: snapshot.generation,
|
|
1067
|
+
recordId: snapshot.record?.acpxRecordId
|
|
1068
|
+
}, () => this.setConfigOptionUnlocked(input, snapshot));
|
|
954
1069
|
}
|
|
955
1070
|
async setConfigOptionUnlocked(logicalInput, snapshot) {
|
|
956
1071
|
const { command } = snapshot;
|
|
@@ -969,6 +1084,7 @@ var AcpxRuntime = class {
|
|
|
969
1084
|
key: "model",
|
|
970
1085
|
value: override.model
|
|
971
1086
|
}) : void 0;
|
|
1087
|
+
this.generationRegistry.assertCurrentGeneration(snapshot.generation);
|
|
972
1088
|
if (override.reasoningEffort) return await delegate.setConfigOption({
|
|
973
1089
|
...input,
|
|
974
1090
|
key: "reasoning_effort",
|
|
@@ -995,39 +1111,54 @@ var AcpxRuntime = class {
|
|
|
995
1111
|
}
|
|
996
1112
|
async cancel(input) {
|
|
997
1113
|
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
998
|
-
await this.
|
|
1114
|
+
await this.runInGeneration(input.handle, {
|
|
1115
|
+
generation: snapshot.generation,
|
|
1116
|
+
recordId: snapshot.record?.acpxRecordId
|
|
1117
|
+
}, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).cancel(toAcpxResourceInput(input)));
|
|
999
1118
|
}
|
|
1000
1119
|
async prepareFreshSession(input) {
|
|
1001
1120
|
const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
|
|
1002
|
-
this.
|
|
1121
|
+
this.generationRegistry.prepareFresh(resource);
|
|
1003
1122
|
this.legacyBareSessionKeys.delete(resource);
|
|
1004
1123
|
}
|
|
1005
1124
|
async close(input) {
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
try {
|
|
1125
|
+
const generation = this.generationForHandle(input.handle);
|
|
1126
|
+
await this.runInGeneration(input.handle, { generation }, async () => {
|
|
1127
|
+
const snapshot = await this.loadOperationSnapshotForHandle(input.handle, true, generation);
|
|
1010
1128
|
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1129
|
+
await acpxOperationScope.run({
|
|
1130
|
+
generation,
|
|
1131
|
+
closeRecord: snapshot.record
|
|
1132
|
+
}, async () => {
|
|
1133
|
+
if (input.discardPersistentState && decodeAcpxRuntimeHandleState(input.handle.runtimeSessionName)?.mode !== "oneshot") {
|
|
1134
|
+
this.generationRegistry.retireGeneration(generation);
|
|
1135
|
+
this.legacyBareSessionKeys.delete(generation.resource);
|
|
1136
|
+
}
|
|
1137
|
+
const cleanup = await prepareAcpxProcessCleanup({
|
|
1138
|
+
record: snapshot.record,
|
|
1139
|
+
command: snapshot.command,
|
|
1140
|
+
sessionKey: resolveAcpxSessionResource(input.handle),
|
|
1141
|
+
gatewayInstanceId: this.gatewayInstanceId,
|
|
1142
|
+
wrapperRoot: this.wrapperRoot,
|
|
1143
|
+
leaseStore: this.processLeaseStore,
|
|
1144
|
+
deps: this.processCleanupDeps
|
|
1145
|
+
}).catch((error) => async () => {
|
|
1146
|
+
throw error;
|
|
1017
1147
|
});
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1148
|
+
try {
|
|
1149
|
+
await delegate.close(toAcpxResourceInput(input));
|
|
1150
|
+
} finally {
|
|
1151
|
+
await cleanup();
|
|
1152
|
+
}
|
|
1153
|
+
const recordId = snapshot.record?.acpxRecordId ?? input.handle.acpxRecordId ?? generation.resource;
|
|
1154
|
+
const currentRecord = generation.records.get(recordId);
|
|
1155
|
+
if (!currentRecord || currentRecord.acpSessionId === snapshot.record?.acpSessionId && currentRecord.createdAt === snapshot.record?.createdAt) {
|
|
1156
|
+
generation.records.delete(recordId);
|
|
1157
|
+
if (generation.activeRecordOperations.has(recordId)) generation.closedRecordIds.add(recordId);
|
|
1158
|
+
}
|
|
1159
|
+
generation.closeCompleted = true;
|
|
1026
1160
|
});
|
|
1027
|
-
}
|
|
1028
|
-
if (cleanupSucceeded) await this.finalizeProcessLeaseForOperation(input.handle, closeLease);
|
|
1029
|
-
else await this.releaseProcessLeaseAfterUncertainFailure(closeLease);
|
|
1030
|
-
}
|
|
1161
|
+
});
|
|
1031
1162
|
}
|
|
1032
1163
|
};
|
|
1033
1164
|
/** Test-only hooks for ACPX runtime behavior that is otherwise private. */
|