@openclaw/acpx 2026.9.5 → 2026.9.6
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/README.md +51 -0
- package/dist/.setup/command-line-CPBLOiZM.mjs +138 -0
- package/dist/.setup/{config-DvCm0_Dp.mjs → config-CdPsIued.mjs} +19 -4
- package/dist/.setup/harness-attempt-CDXMeP5Z.mjs +369 -0
- package/dist/.setup/{pi-session-catalog-runtime-CbLyvshh.mjs → pi-session-catalog-runtime-UhiQSaDw.mjs} +6 -6
- package/dist/.setup/{process-lease-B83BGiLj.mjs → process-lease-C3dNPtu8.mjs} +2 -62
- package/dist/.setup/{register.runtime-CTfxhkFm.mjs → register.runtime-Bm4HrMCp.mjs} +51 -15
- package/dist/.setup/service-DMae5pPH.mjs +2740 -0
- package/dist/.setup/{session-owner-migration-BuKHYP6F.mjs → session-owner-migration-CdJSYRk1.mjs} +15 -12
- package/dist/.setup/{session-resource-Dzl0U7kK.mjs → session-resource-BaJ6bs5d.mjs} +13 -1
- package/dist/doctor-contract-api.js +3 -3
- package/dist/index.js +266 -6
- package/dist/register.runtime.js +1 -1
- package/openclaw.plugin.json +18 -2
- package/package.json +5 -5
- package/skills/acp-router/SKILL.md +2 -2
- package/dist/.setup/rolldown-runtime-8H4AJuhK.mjs +0 -14
- package/dist/.setup/runtime-Du-YvDnr.mjs +0 -1171
- package/dist/.setup/service-DZYyo6-7.mjs +0 -1571
- package/dist/.setup/{pi-session-paths-EMbd4Hkz.mjs → pi-session-paths-CIvyk6KB.mjs} +2 -2
|
@@ -1,1171 +0,0 @@
|
|
|
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";
|
|
5
|
-
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
-
import path, { resolve } from "node:path";
|
|
7
|
-
import fs from "node:fs/promises";
|
|
8
|
-
import { randomUUID } from "node:crypto";
|
|
9
|
-
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
10
|
-
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
11
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
12
|
-
import { isDeepStrictEqual } from "node:util";
|
|
13
|
-
import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, decodeAcpxRuntimeHandleState as decodeAcpxRuntimeHandleState$1, encodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
|
|
14
|
-
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
15
|
-
import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
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;
|
|
29
|
-
}
|
|
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);
|
|
49
|
-
}
|
|
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
|
-
};
|
|
120
|
-
//#endregion
|
|
121
|
-
//#region extensions/acpx/src/runtime-session-store.ts
|
|
122
|
-
/** Generation-bound persistence and process-lease metadata for ACPX resets. */
|
|
123
|
-
function withOpenClawLeaseSessionMetadata(record, lease) {
|
|
124
|
-
return {
|
|
125
|
-
...record,
|
|
126
|
-
openclawLeaseId: lease.leaseId,
|
|
127
|
-
openclawGatewayInstanceId: lease.gatewayInstanceId
|
|
128
|
-
};
|
|
129
|
-
}
|
|
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);
|
|
133
|
-
}
|
|
134
|
-
const acpxGenerationKey = Symbol("openclaw.acpxGeneration");
|
|
135
|
-
const acpxOperationScope = new AsyncLocalStorage();
|
|
136
|
-
function readSessionRecordName(record) {
|
|
137
|
-
if (typeof record !== "object" || record === null) return "";
|
|
138
|
-
const { name } = record;
|
|
139
|
-
return typeof name === "string" ? name.trim() : "";
|
|
140
|
-
}
|
|
141
|
-
function readRecordAgentCommand(record) {
|
|
142
|
-
return record?.agentArgv ?? record?.agentCommand;
|
|
143
|
-
}
|
|
144
|
-
function readRecordCwd(record) {
|
|
145
|
-
if (typeof record !== "object" || record === null) return;
|
|
146
|
-
const { cwd } = record;
|
|
147
|
-
return typeof cwd === "string" ? cwd.trim() || void 0 : void 0;
|
|
148
|
-
}
|
|
149
|
-
function readRecordResetOnNextEnsure(record) {
|
|
150
|
-
if (typeof record !== "object" || record === null) return false;
|
|
151
|
-
const { acpx } = record;
|
|
152
|
-
if (typeof acpx !== "object" || acpx === null) return false;
|
|
153
|
-
return acpx.reset_on_next_ensure === true;
|
|
154
|
-
}
|
|
155
|
-
function readRecordAgentPid(record) {
|
|
156
|
-
if (typeof record !== "object" || record === null) return;
|
|
157
|
-
const { pid, processId } = record;
|
|
158
|
-
const rawPid = pid ?? processId;
|
|
159
|
-
const numericPid = typeof rawPid === "number" ? rawPid : typeof rawPid === "string" ? parseStrictPositiveInteger(rawPid) : void 0;
|
|
160
|
-
return numericPid && Number.isInteger(numericPid) && numericPid > 0 ? numericPid : void 0;
|
|
161
|
-
}
|
|
162
|
-
function readOpenClawLeaseIdFromRecord(record) {
|
|
163
|
-
if (typeof record !== "object" || record === null) return;
|
|
164
|
-
const { openclawLeaseId } = record;
|
|
165
|
-
return typeof openclawLeaseId === "string" ? openclawLeaseId.trim() || void 0 : void 0;
|
|
166
|
-
}
|
|
167
|
-
function readOpenClawGatewayInstanceIdFromRecord(record) {
|
|
168
|
-
if (typeof record !== "object" || record === null) return;
|
|
169
|
-
const { openclawGatewayInstanceId } = record;
|
|
170
|
-
return typeof openclawGatewayInstanceId === "string" ? openclawGatewayInstanceId.trim() || void 0 : void 0;
|
|
171
|
-
}
|
|
172
|
-
function extractGeneratedWrapperPath(command) {
|
|
173
|
-
return splitCommandParts(command ?? "").find((part) => (part.split(/[\\/]/).pop() ?? "") === "codex-acp-wrapper.mjs" || (part.split(/[\\/]/).pop() ?? "") === "claude-agent-acp-wrapper.mjs") ?? "";
|
|
174
|
-
}
|
|
175
|
-
function selectCurrentSessionLease(params) {
|
|
176
|
-
const sessionKeys = new Set(normalizeStringEntries(params.sessionKeys));
|
|
177
|
-
const candidates = params.leases.filter((lease) => sessionKeys.has(lease.sessionKey));
|
|
178
|
-
if (params.rootPid) return candidates.find((lease) => lease.rootPid === params.rootPid);
|
|
179
|
-
let selected;
|
|
180
|
-
for (const lease of candidates) if (!selected || lease.startedAt > selected.startedAt) selected = lease;
|
|
181
|
-
return selected;
|
|
182
|
-
}
|
|
183
|
-
function createResetAwareSessionStore(baseStore, params) {
|
|
184
|
-
const freshSessionKeys = /* @__PURE__ */ new Set();
|
|
185
|
-
const stateQueue = new KeyedAsyncQueue();
|
|
186
|
-
const pendingWrites = /* @__PURE__ */ new Map();
|
|
187
|
-
return {
|
|
188
|
-
async load(sessionId) {
|
|
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();
|
|
215
|
-
},
|
|
216
|
-
async save(record) {
|
|
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
|
-
}
|
|
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);
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
loadForClose: (sessionKey) => baseStore.load(sessionKey),
|
|
274
|
-
isFresh: (sessionKey) => freshSessionKeys.has(sessionKey),
|
|
275
|
-
markFresh(sessionKey) {
|
|
276
|
-
const normalized = sessionKey.trim();
|
|
277
|
-
if (normalized) freshSessionKeys.add(normalized);
|
|
278
|
-
}
|
|
279
|
-
};
|
|
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
|
-
}
|
|
396
|
-
const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
|
|
397
|
-
const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
|
|
398
|
-
const CODEX_ACP_AGENT_ID = "codex";
|
|
399
|
-
const CODEX_ACP_OPENCLAW_PREFIX = "openai/";
|
|
400
|
-
const CLAUDE_ACP_OPENCLAW_PREFIX = /^(?:anthropic|amazon-bedrock)\//i;
|
|
401
|
-
const CODEX_ACP_THINKING_ALIASES = /* @__PURE__ */ new Map([
|
|
402
|
-
["off", void 0],
|
|
403
|
-
["minimal", "low"],
|
|
404
|
-
["low", "low"],
|
|
405
|
-
["medium", "medium"],
|
|
406
|
-
["high", "high"],
|
|
407
|
-
["x-high", "xhigh"],
|
|
408
|
-
["x_high", "xhigh"],
|
|
409
|
-
["extra-high", "xhigh"],
|
|
410
|
-
["extra_high", "xhigh"],
|
|
411
|
-
["extra high", "xhigh"],
|
|
412
|
-
["xhigh", "xhigh"]
|
|
413
|
-
]);
|
|
414
|
-
function normalizeAgentName(value) {
|
|
415
|
-
const normalized = value?.trim().toLowerCase();
|
|
416
|
-
return normalized ? normalized : void 0;
|
|
417
|
-
}
|
|
418
|
-
function readAgentFromSessionKey(sessionKey) {
|
|
419
|
-
const normalized = sessionKey?.trim();
|
|
420
|
-
if (!normalized) return;
|
|
421
|
-
return normalizeAgentName(/^agent:(?<agent>[^:]+):/i.exec(normalized)?.groups?.agent);
|
|
422
|
-
}
|
|
423
|
-
function readAgentFromHandle(handle) {
|
|
424
|
-
return normalizeAgentName(decodeAcpxRuntimeHandleState(handle.runtimeSessionName)?.agent) ?? readAgentFromSessionKey(handle.sessionKey);
|
|
425
|
-
}
|
|
426
|
-
function basename(value) {
|
|
427
|
-
return value.split(/[\\/]/).pop() ?? value;
|
|
428
|
-
}
|
|
429
|
-
function isEnvAssignment(value) {
|
|
430
|
-
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value);
|
|
431
|
-
}
|
|
432
|
-
function unwrapEnvCommand(parts) {
|
|
433
|
-
const command = parts.at(0);
|
|
434
|
-
if (!command || basename(command) !== "env") return parts;
|
|
435
|
-
let index = 1;
|
|
436
|
-
while (true) {
|
|
437
|
-
const part = parts.at(index);
|
|
438
|
-
if (!part || !isEnvAssignment(part)) break;
|
|
439
|
-
index += 1;
|
|
440
|
-
}
|
|
441
|
-
return parts.slice(index);
|
|
442
|
-
}
|
|
443
|
-
function matchesExecutableName(value, executableName) {
|
|
444
|
-
const normalized = basename(value).toLowerCase();
|
|
445
|
-
return normalized === executableName || normalized === `${executableName}.exe`;
|
|
446
|
-
}
|
|
447
|
-
function matchesPackageSpec(value, packageName) {
|
|
448
|
-
const normalized = value.trim().toLowerCase();
|
|
449
|
-
return normalized === packageName || normalized.startsWith(`${packageName}@`);
|
|
450
|
-
}
|
|
451
|
-
function stripModuleExtension(value) {
|
|
452
|
-
return value.replace(/\.[cm]?js$/i, "").toLowerCase();
|
|
453
|
-
}
|
|
454
|
-
function isAcpCommand(command, params) {
|
|
455
|
-
if (!command) return false;
|
|
456
|
-
const parts = unwrapEnvCommand(splitCommandParts(command));
|
|
457
|
-
if (!parts.length) return false;
|
|
458
|
-
if (parts.some((part) => matchesPackageSpec(part, params.packageName))) return true;
|
|
459
|
-
const commandName = basename(parts[0] ?? "");
|
|
460
|
-
if (matchesExecutableName(commandName, params.executableName)) return true;
|
|
461
|
-
if (!matchesExecutableName(commandName, "node")) return false;
|
|
462
|
-
const scriptName = stripModuleExtension(basename(parts[1] ?? ""));
|
|
463
|
-
return scriptName === params.executableName || scriptName === `${params.executableName}-wrapper`;
|
|
464
|
-
}
|
|
465
|
-
function isOpenClawBridgeCommand(command) {
|
|
466
|
-
if (!command) return false;
|
|
467
|
-
const parts = unwrapEnvCommand(splitCommandParts(command));
|
|
468
|
-
if (basename(parts[0] ?? "") === OPENCLAW_BRIDGE_EXECUTABLE) return parts[1] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
469
|
-
if (basename(parts[0] ?? "") !== "node") return false;
|
|
470
|
-
const scriptName = basename(parts[1] ?? "");
|
|
471
|
-
return /^openclaw(?:\.[cm]?js)?$/i.test(scriptName) && parts[2] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
472
|
-
}
|
|
473
|
-
function isCodexAcpCommand(command) {
|
|
474
|
-
return isAcpCommand(command, {
|
|
475
|
-
packageName: CODEX_ACP_PACKAGE,
|
|
476
|
-
executableName: "codex-acp"
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
function isClaudeAcpCommand(command) {
|
|
480
|
-
return isAcpCommand(command, {
|
|
481
|
-
packageName: "@agentclientprotocol/claude-agent-acp",
|
|
482
|
-
executableName: "claude-agent-acp"
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
function failUnsupportedCodexAcpModel(rawModel) {
|
|
486
|
-
throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Codex ACP model "${rawModel}" is not supported. Use openai/<model> or <model>/<reasoning-effort>.`);
|
|
487
|
-
}
|
|
488
|
-
const WIRE_TIMEOUT_CONFIG_KEYS = /* @__PURE__ */ new Set(["timeout", "timeout_seconds"]);
|
|
489
|
-
function assertSupportedRuntimeSessionMode(mode) {
|
|
490
|
-
if (mode === "persistent" || mode === "oneshot") return;
|
|
491
|
-
throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Unsupported ACP runtime session mode ${JSON.stringify(mode)}. Expected one of: persistent, oneshot.`);
|
|
492
|
-
}
|
|
493
|
-
function failUnsupportedCodexAcpThinking(rawThinking) {
|
|
494
|
-
throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Codex ACP thinking level "${rawThinking}" is not supported. Use off, minimal, low, medium, high, or xhigh.`);
|
|
495
|
-
}
|
|
496
|
-
function normalizeCodexAcpReasoningEffort(rawThinking) {
|
|
497
|
-
const normalized = rawThinking?.trim().toLowerCase();
|
|
498
|
-
if (!normalized) return;
|
|
499
|
-
if (!CODEX_ACP_THINKING_ALIASES.has(normalized)) failUnsupportedCodexAcpThinking(rawThinking ?? "");
|
|
500
|
-
return CODEX_ACP_THINKING_ALIASES.get(normalized);
|
|
501
|
-
}
|
|
502
|
-
function isCodexAcpReasoningEffortAlias(value) {
|
|
503
|
-
const normalized = value?.trim().toLowerCase();
|
|
504
|
-
return Boolean(normalized && CODEX_ACP_THINKING_ALIASES.has(normalized));
|
|
505
|
-
}
|
|
506
|
-
function classifyCodexAcpModelRequest(rawModel, rawThinking) {
|
|
507
|
-
const raw = rawModel?.trim();
|
|
508
|
-
const thinkingReasoningEffort = normalizeCodexAcpReasoningEffort(rawThinking);
|
|
509
|
-
const thinkingOnlyOverride = thinkingReasoningEffort ? { reasoningEffort: thinkingReasoningEffort } : void 0;
|
|
510
|
-
if (!raw) return {
|
|
511
|
-
kind: "override",
|
|
512
|
-
override: thinkingOnlyOverride ?? {}
|
|
513
|
-
};
|
|
514
|
-
let value = raw;
|
|
515
|
-
let hadOpenAiQualifier = false;
|
|
516
|
-
if (value.toLowerCase().startsWith(CODEX_ACP_OPENCLAW_PREFIX)) {
|
|
517
|
-
value = value.slice(7);
|
|
518
|
-
hadOpenAiQualifier = true;
|
|
519
|
-
}
|
|
520
|
-
let model = value.trim();
|
|
521
|
-
let modelReasoningEffort;
|
|
522
|
-
const slashIndex = value.lastIndexOf("/");
|
|
523
|
-
if (slashIndex >= 0 && isCodexAcpReasoningEffortAlias(value.slice(slashIndex + 1))) {
|
|
524
|
-
modelReasoningEffort = normalizeCodexAcpReasoningEffort(value.slice(slashIndex + 1));
|
|
525
|
-
model = value.slice(0, slashIndex).trim();
|
|
526
|
-
}
|
|
527
|
-
if (hadOpenAiQualifier && (!model || model.includes("/"))) failUnsupportedCodexAcpModel(raw);
|
|
528
|
-
if (!model || model.includes("/")) return thinkingOnlyOverride ? {
|
|
529
|
-
kind: "unsupported",
|
|
530
|
-
thinkingOverride: thinkingOnlyOverride
|
|
531
|
-
} : { kind: "unsupported" };
|
|
532
|
-
const reasoningEffort = rawThinking?.trim() ? thinkingReasoningEffort : modelReasoningEffort;
|
|
533
|
-
return {
|
|
534
|
-
kind: "override",
|
|
535
|
-
override: {
|
|
536
|
-
model,
|
|
537
|
-
...reasoningEffort ? { reasoningEffort } : {}
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
function withCodexSessionModel(input, override) {
|
|
542
|
-
const next = { ...input };
|
|
543
|
-
if (override?.model) next.model = override.model;
|
|
544
|
-
else delete next.model;
|
|
545
|
-
return next;
|
|
546
|
-
}
|
|
547
|
-
function normalizeClaudeAcpModelOverride(rawModel) {
|
|
548
|
-
const raw = rawModel?.trim();
|
|
549
|
-
if (!raw) return;
|
|
550
|
-
const prefix = raw.match(CLAUDE_ACP_OPENCLAW_PREFIX);
|
|
551
|
-
if (!prefix) return raw;
|
|
552
|
-
return raw.slice(prefix[0].length).trim() || void 0;
|
|
553
|
-
}
|
|
554
|
-
function withAcpxSessionOptions(input) {
|
|
555
|
-
const existingOptions = input.sessionOptions;
|
|
556
|
-
const model = input.model?.trim() || existingOptions?.model;
|
|
557
|
-
const sessionOptions = model ? {
|
|
558
|
-
...existingOptions,
|
|
559
|
-
model
|
|
560
|
-
} : existingOptions;
|
|
561
|
-
const { modelExplicit: _modelExplicit, thinkingExplicit: _thinkingExplicit, ...rest } = input;
|
|
562
|
-
return {
|
|
563
|
-
...rest,
|
|
564
|
-
...sessionOptions ? { sessionOptions } : {}
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
function isAcpModelCapabilityMissingError(error) {
|
|
568
|
-
return isRequestedModelUnsupportedError(error) && error.reason === "missing-capability";
|
|
569
|
-
}
|
|
570
|
-
async function ensureDelegateSessionWithModelFallback(delegate, input) {
|
|
571
|
-
try {
|
|
572
|
-
return await delegate.ensureSession(withAcpxSessionOptions(input));
|
|
573
|
-
} catch (error) {
|
|
574
|
-
if (input.modelExplicit || !input.model || !isAcpModelCapabilityMissingError(error)) throw error;
|
|
575
|
-
return {
|
|
576
|
-
...await delegate.ensureSession(withAcpxSessionOptions({
|
|
577
|
-
...input,
|
|
578
|
-
model: void 0
|
|
579
|
-
})),
|
|
580
|
-
appliedModel: { kind: "dropped" }
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
function appendCodexAcpConfigOverrides(command, override) {
|
|
585
|
-
const config = {
|
|
586
|
-
...override.model ? { model: override.model } : {},
|
|
587
|
-
...override.reasoningEffort ? { model_reasoning_effort: override.reasoningEffort } : {}
|
|
588
|
-
};
|
|
589
|
-
if (Object.keys(config).length === 0) return command;
|
|
590
|
-
return [
|
|
591
|
-
...splitCommandParts(command),
|
|
592
|
-
OPENCLAW_CODEX_CONFIG_ARG,
|
|
593
|
-
JSON.stringify(config)
|
|
594
|
-
];
|
|
595
|
-
}
|
|
596
|
-
function resolveAgentCommand(params) {
|
|
597
|
-
const normalizedAgentName = normalizeAgentName(params.agentName);
|
|
598
|
-
if (!normalizedAgentName) return;
|
|
599
|
-
return splitCommandParts(params.agentRegistry.resolve(normalizedAgentName));
|
|
600
|
-
}
|
|
601
|
-
function withManagedToolsMcpSessionEnv(params) {
|
|
602
|
-
const sessionKey = params.sessionKey.trim();
|
|
603
|
-
if (!params.pluginToolsEnabled && !params.openclawToolsEnabled || !sessionKey || !params.mcpServers?.length) return params.mcpServers;
|
|
604
|
-
let changed = false;
|
|
605
|
-
const nextServers = params.mcpServers.map((server) => {
|
|
606
|
-
const isManagedPluginTools = params.pluginToolsEnabled && server.name === ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME;
|
|
607
|
-
const isManagedOpenClawTools = params.openclawToolsEnabled && server.name === ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME;
|
|
608
|
-
if (!isManagedPluginTools && !isManagedOpenClawTools || !("command" in server)) return server;
|
|
609
|
-
changed = true;
|
|
610
|
-
const env = [...server.env.filter((entry) => entry.name !== OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV), {
|
|
611
|
-
name: OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV,
|
|
612
|
-
value: sessionKey
|
|
613
|
-
}];
|
|
614
|
-
return {
|
|
615
|
-
...server,
|
|
616
|
-
env,
|
|
617
|
-
args: params.agentId ? [
|
|
618
|
-
...server.args,
|
|
619
|
-
"--openclaw-agent-id",
|
|
620
|
-
params.agentId
|
|
621
|
-
] : server.args
|
|
622
|
-
};
|
|
623
|
-
});
|
|
624
|
-
return changed ? nextServers : params.mcpServers;
|
|
625
|
-
}
|
|
626
|
-
/** OpenClaw-managed ACP runtime implementation backed by the upstream acpx runtime. */
|
|
627
|
-
var AcpxRuntime = class {
|
|
628
|
-
constructor(options, testOptions) {
|
|
629
|
-
this.ownerAwareSessions = 1;
|
|
630
|
-
this.launchCommandScope = new AsyncLocalStorage();
|
|
631
|
-
this.sessionScope = new AsyncLocalStorage();
|
|
632
|
-
this.probeQueue = new KeyedAsyncQueue();
|
|
633
|
-
this.launchLeaseScope = new AsyncLocalStorage();
|
|
634
|
-
this.legacyBareSessionKeys = new Set(options.openclawLegacyBareSessionKeys);
|
|
635
|
-
const { openclawProcessCleanup, ...delegateTestOptions } = testOptions ?? {};
|
|
636
|
-
this.processCleanupDeps = openclawProcessCleanup;
|
|
637
|
-
this.wrapperRoot = options.openclawWrapperRoot;
|
|
638
|
-
this.gatewayInstanceId = options.openclawGatewayInstanceId;
|
|
639
|
-
this.processLeaseStore = options.openclawProcessLeaseStore;
|
|
640
|
-
this.pluginToolsMcpBridgeEnabled = options.pluginToolsMcpBridgeEnabled === true;
|
|
641
|
-
this.openclawToolsMcpBridgeEnabled = options.openclawToolsMcpBridgeEnabled === true;
|
|
642
|
-
this.managedToolsMcpBridgeEnabled = this.pluginToolsMcpBridgeEnabled || this.openclawToolsMcpBridgeEnabled;
|
|
643
|
-
this.cwd = options.cwd;
|
|
644
|
-
this.sessionStore = createResetAwareSessionStore(options.sessionStore, {
|
|
645
|
-
gatewayInstanceId: this.gatewayInstanceId,
|
|
646
|
-
leaseStore: this.processLeaseStore,
|
|
647
|
-
launchScope: this.launchLeaseScope,
|
|
648
|
-
wrapperRoot: this.wrapperRoot
|
|
649
|
-
});
|
|
650
|
-
this.agentRegistry = options.agentRegistry;
|
|
651
|
-
this.scopedAgentRegistry = {
|
|
652
|
-
resolve: (agentName) => {
|
|
653
|
-
const launch = this.launchCommandScope.getStore();
|
|
654
|
-
return launch && launch.agent === normalizeAgentName(agentName) && launch.command ? launch.command : this.agentRegistry.resolve(agentName);
|
|
655
|
-
},
|
|
656
|
-
list: () => this.agentRegistry.list()
|
|
657
|
-
};
|
|
658
|
-
this.createDelegate = () => new AcpxRuntime$1({
|
|
659
|
-
...options,
|
|
660
|
-
sessionStore: this.sessionStore,
|
|
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);
|
|
690
|
-
this.probeAgent = normalizeAgentName(options.probeAgent) ?? "codex";
|
|
691
|
-
const probeCommand = resolveAgentCommand({
|
|
692
|
-
agentName: this.probeAgent,
|
|
693
|
-
agentRegistry: this.agentRegistry
|
|
694
|
-
});
|
|
695
|
-
this.probeCommand = probeCommand;
|
|
696
|
-
}
|
|
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
|
-
}
|
|
704
|
-
}
|
|
705
|
-
resolveDelegateForSession(params) {
|
|
706
|
-
const generation = acpxOperationScope.getStore()?.generation ?? this.generationRegistry.currentGeneration(resolveAcpxSessionResource(params));
|
|
707
|
-
return this.generationRegistry.resolveDelegate(generation);
|
|
708
|
-
}
|
|
709
|
-
generationForHandle(handle) {
|
|
710
|
-
const resource = assertAcpxSessionOwnerLocator({
|
|
711
|
-
...handle,
|
|
712
|
-
persistedHandle: handle
|
|
713
|
-
}, this.legacyBareSessionKeys);
|
|
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);
|
|
738
|
-
return {
|
|
739
|
-
record,
|
|
740
|
-
command,
|
|
741
|
-
generation
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
resolveDelegateForOperationSnapshot(handle, snapshot) {
|
|
745
|
-
return acpxOperationScope.run({
|
|
746
|
-
generation: snapshot.generation,
|
|
747
|
-
recordId: snapshot.record?.acpxRecordId
|
|
748
|
-
}, () => this.resolveDelegateForSession({
|
|
749
|
-
command: snapshot.command,
|
|
750
|
-
sessionKey: handle.sessionKey,
|
|
751
|
-
agentId: handle.agentId
|
|
752
|
-
}));
|
|
753
|
-
}
|
|
754
|
-
async readReusablePersistentSessionCommand(params) {
|
|
755
|
-
if (params.mode !== "persistent" || !params.command) return;
|
|
756
|
-
const existing = await this.sessionStore.load(params.sessionKey);
|
|
757
|
-
if (!existing || readRecordResetOnNextEnsure(existing)) return;
|
|
758
|
-
const recordCwd = readRecordCwd(existing);
|
|
759
|
-
if (!recordCwd || resolve(recordCwd) !== resolve(params.cwd?.trim() || this.cwd)) return;
|
|
760
|
-
const recordCommand = readRecordAgentCommand(existing);
|
|
761
|
-
if (!recordCommand) return;
|
|
762
|
-
const leaseIdentity = readAcpxProcessLeaseIdentity(recordCommand);
|
|
763
|
-
if (leaseIdentity && leaseIdentity.gatewayInstanceId !== this.gatewayInstanceId) return;
|
|
764
|
-
const stableRecordCommand = leaseIdentity ? withAcpxLeaseArgs({
|
|
765
|
-
command: params.command,
|
|
766
|
-
leaseId: leaseIdentity.leaseId,
|
|
767
|
-
gatewayInstanceId: leaseIdentity.gatewayInstanceId
|
|
768
|
-
}) : params.command;
|
|
769
|
-
if (!isDeepStrictEqual(splitCommandParts(recordCommand), splitCommandParts(stableRecordCommand))) return;
|
|
770
|
-
return !params.resumeSessionId || existing.acpSessionId === params.resumeSessionId ? recordCommand : void 0;
|
|
771
|
-
}
|
|
772
|
-
async runWithLaunchLease(params) {
|
|
773
|
-
if (!params.command || !this.wrapperRoot || !this.gatewayInstanceId || !this.processLeaseStore || !isOpenClawLeaseAwareAcpxProcessCommand({
|
|
774
|
-
command: params.command,
|
|
775
|
-
wrapperRoot: this.wrapperRoot
|
|
776
|
-
})) return await this.launchCommandScope.run({
|
|
777
|
-
agent: normalizeAgentName(params.agent) ?? params.agent,
|
|
778
|
-
command: params.reusableCommand ?? params.command
|
|
779
|
-
}, params.run);
|
|
780
|
-
const reusableIdentity = readAcpxProcessLeaseIdentity(params.reusableCommand);
|
|
781
|
-
const leaseId = reusableIdentity?.gatewayInstanceId === this.gatewayInstanceId ? reusableIdentity.leaseId : params.finalizeCompletedProbe ? `probe-${hashAcpxProcessCommand(`${this.gatewayInstanceId}\0${extractGeneratedWrapperPath(params.command)}`)}` : randomUUID();
|
|
782
|
-
const leasedCommand = withAcpxLeaseArgs({
|
|
783
|
-
command: params.command,
|
|
784
|
-
leaseId,
|
|
785
|
-
gatewayInstanceId: this.gatewayInstanceId
|
|
786
|
-
});
|
|
787
|
-
const launch = {
|
|
788
|
-
leaseId,
|
|
789
|
-
gatewayInstanceId: this.gatewayInstanceId,
|
|
790
|
-
sessionKey: params.sessionKey,
|
|
791
|
-
wrapperRoot: this.wrapperRoot,
|
|
792
|
-
resolvedCommand: params.reusableCommand ?? leasedCommand,
|
|
793
|
-
leasedCommand
|
|
794
|
-
};
|
|
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
|
|
805
|
-
});
|
|
806
|
-
return result;
|
|
807
|
-
}
|
|
808
|
-
async recordProcessLaunch(process) {
|
|
809
|
-
const command = [process.command, ...process.args];
|
|
810
|
-
const identity = readAcpxProcessLeaseIdentity(command);
|
|
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({
|
|
816
|
-
command,
|
|
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"
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
async withCodexWrapperDiagnostics(params) {
|
|
831
|
-
try {
|
|
832
|
-
return await params.run();
|
|
833
|
-
} catch (error) {
|
|
834
|
-
if (!isCodexAcpCommand(params.command) || !isGenericInternalAcpError(error)) throw error;
|
|
835
|
-
const stderrTail = params.handle ? await this.readCodexTurnFailureStderr({ handle: params.handle }) : await readCodexWrapperStderrTail({
|
|
836
|
-
wrapperRoot: this.wrapperRoot,
|
|
837
|
-
leaseId: this.launchLeaseScope.getStore()?.leaseId
|
|
838
|
-
});
|
|
839
|
-
if (!stderrTail) throw error;
|
|
840
|
-
throw new AcpRuntimeError(params.fallbackCode, `Internal error: ${stderrTail}`, { cause: error });
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
async readCodexTurnFailureStderr(params) {
|
|
844
|
-
const record = await this.sessionStore.load(params.handle.acpxRecordId ?? resolveAcpxSessionResource(params.handle));
|
|
845
|
-
return readCodexWrapperStderrTail({
|
|
846
|
-
wrapperRoot: this.wrapperRoot,
|
|
847
|
-
leaseId: readOpenClawLeaseIdFromRecord(record)
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
async shutdown() {
|
|
851
|
-
await this.generationRegistry.shutdown();
|
|
852
|
-
}
|
|
853
|
-
isHealthy() {
|
|
854
|
-
return this.delegate.isHealthy();
|
|
855
|
-
}
|
|
856
|
-
async probeAvailability() {
|
|
857
|
-
await this.probeQueue.enqueue(this.probeAgent, () => this.runWithLaunchLease({
|
|
858
|
-
agent: this.probeAgent,
|
|
859
|
-
sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
|
|
860
|
-
command: this.probeCommand,
|
|
861
|
-
finalizeCompletedProbe: true,
|
|
862
|
-
run: () => this.delegate.probeAvailability()
|
|
863
|
-
}));
|
|
864
|
-
}
|
|
865
|
-
async doctor() {
|
|
866
|
-
return await this.probeQueue.enqueue(this.probeAgent, () => this.runWithLaunchLease({
|
|
867
|
-
agent: this.probeAgent,
|
|
868
|
-
sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
|
|
869
|
-
command: this.probeCommand,
|
|
870
|
-
finalizeCompletedProbe: true,
|
|
871
|
-
run: () => this.delegate.doctor()
|
|
872
|
-
}));
|
|
873
|
-
}
|
|
874
|
-
async ensureSession(input) {
|
|
875
|
-
const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
|
|
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
|
-
}));
|
|
891
|
-
}
|
|
892
|
-
async ensureSessionUnlocked(logicalInput) {
|
|
893
|
-
assertSupportedRuntimeSessionMode(logicalInput.mode);
|
|
894
|
-
const command = resolveAgentCommand({
|
|
895
|
-
agentName: logicalInput.agent,
|
|
896
|
-
agentRegistry: this.agentRegistry
|
|
897
|
-
});
|
|
898
|
-
const delegate = this.resolveDelegateForSession({
|
|
899
|
-
command,
|
|
900
|
-
sessionKey: logicalInput.sessionKey,
|
|
901
|
-
agentId: logicalInput.agentId
|
|
902
|
-
});
|
|
903
|
-
const logicalTarget = {
|
|
904
|
-
sessionKey: logicalInput.sessionKey,
|
|
905
|
-
agentId: logicalInput.agentId
|
|
906
|
-
};
|
|
907
|
-
const input = {
|
|
908
|
-
...logicalInput,
|
|
909
|
-
sessionKey: resolveAcpxSessionResource(logicalInput)
|
|
910
|
-
};
|
|
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;
|
|
915
|
-
const claudeModelOverride = isClaudeAcpCommand(command) ? normalizeClaudeAcpModelOverride(input.model) : void 0;
|
|
916
|
-
const codexClassification = isCodexAcp ? classifyCodexAcpModelRequest(effectiveInput.model, effectiveInput.thinking) : void 0;
|
|
917
|
-
if (codexClassification?.kind === "unsupported" && input.modelExplicit) failUnsupportedCodexAcpModel(input.model ?? "");
|
|
918
|
-
const classifiedCodexOverride = codexClassification?.kind === "override" ? codexClassification.override : codexClassification?.thinkingOverride;
|
|
919
|
-
const codexModelOverride = classifiedCodexOverride && Object.keys(classifiedCodexOverride).length > 0 ? classifiedCodexOverride : void 0;
|
|
920
|
-
const requestedModel = effectiveInput.model?.trim();
|
|
921
|
-
const appliedModel = isCodexAcp && requestedModel ? codexModelOverride?.model ? {
|
|
922
|
-
kind: "applied",
|
|
923
|
-
model: requestedModel
|
|
924
|
-
} : { kind: "dropped" } : void 0;
|
|
925
|
-
const ensureInput = isCodexAcp ? withCodexSessionModel(effectiveInput, codexModelOverride) : claudeModelOverride ? {
|
|
926
|
-
...effectiveInput,
|
|
927
|
-
model: claudeModelOverride
|
|
928
|
-
} : effectiveInput;
|
|
929
|
-
const stableLaunchCommand = codexModelOverride && command ? appendCodexAcpConfigOverrides(command, codexModelOverride) : command;
|
|
930
|
-
const reusableCommand = await this.readReusablePersistentSessionCommand({
|
|
931
|
-
sessionKey: input.sessionKey,
|
|
932
|
-
mode: input.mode,
|
|
933
|
-
cwd: input.cwd,
|
|
934
|
-
command: stableLaunchCommand,
|
|
935
|
-
resumeSessionId: input.resumeSessionId
|
|
936
|
-
});
|
|
937
|
-
return {
|
|
938
|
-
...await this.runWithLaunchLease({
|
|
939
|
-
agent: ensureInput.agent,
|
|
940
|
-
sessionKey: ensureInput.sessionKey,
|
|
941
|
-
command: stableLaunchCommand,
|
|
942
|
-
reusableCommand,
|
|
943
|
-
run: () => this.withCodexWrapperDiagnostics({
|
|
944
|
-
command: stableLaunchCommand,
|
|
945
|
-
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
946
|
-
run: () => codexModelOverride ? delegate.ensureSession(withAcpxSessionOptions(ensureInput)) : ensureDelegateSessionWithModelFallback(delegate, ensureInput)
|
|
947
|
-
})
|
|
948
|
-
}),
|
|
949
|
-
...logicalTarget,
|
|
950
|
-
...appliedModel ? { appliedModel } : {},
|
|
951
|
-
...dropInheritedCodexMax ? { appliedThinking: { kind: "dropped" } } : {}
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
async *runTurn(input) {
|
|
955
|
-
const turn = this.startTurn(input);
|
|
956
|
-
turn.result.catch(() => {});
|
|
957
|
-
let completed = false;
|
|
958
|
-
try {
|
|
959
|
-
yield* turn.events;
|
|
960
|
-
const result = await turn.result;
|
|
961
|
-
completed = true;
|
|
962
|
-
yield result.status === "failed" ? {
|
|
963
|
-
type: "error",
|
|
964
|
-
...result.error
|
|
965
|
-
} : {
|
|
966
|
-
type: "done",
|
|
967
|
-
...result.stopReason ? { stopReason: result.stopReason } : {}
|
|
968
|
-
};
|
|
969
|
-
} finally {
|
|
970
|
-
if (!completed) {
|
|
971
|
-
await turn.cancel({ reason: "stream-closed" }).catch(() => {});
|
|
972
|
-
await turn.closeStream({ reason: "stream-closed" }).catch(() => {});
|
|
973
|
-
await turn.result.catch(() => {});
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
startTurn(input) {
|
|
978
|
-
const withTurnDiagnostics = (command, run) => this.withCodexWrapperDiagnostics({
|
|
979
|
-
command,
|
|
980
|
-
handle: input.handle,
|
|
981
|
-
fallbackCode: "ACP_TURN_FAILED",
|
|
982
|
-
run
|
|
983
|
-
});
|
|
984
|
-
const turnPromise = this.loadOperationSnapshotForHandle(input.handle).then((snapshot) => {
|
|
985
|
-
const { command, generation } = snapshot;
|
|
986
|
-
this.generationRegistry.assertCurrentGeneration(generation);
|
|
987
|
-
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
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
|
-
})));
|
|
1005
|
-
});
|
|
1006
|
-
return {
|
|
1007
|
-
requestId: input.requestId,
|
|
1008
|
-
get promptStarted() {
|
|
1009
|
-
return turnPromise.then(({ turn }) => turn.promptStarted);
|
|
1010
|
-
},
|
|
1011
|
-
events: { async *[Symbol.asyncIterator]() {
|
|
1012
|
-
const { command, turn } = await turnPromise;
|
|
1013
|
-
try {
|
|
1014
|
-
yield* turn.events;
|
|
1015
|
-
} catch (error) {
|
|
1016
|
-
if (!isGenericInternalAcpError(error)) throw error;
|
|
1017
|
-
await withTurnDiagnostics(command, () => Promise.reject(error));
|
|
1018
|
-
}
|
|
1019
|
-
} },
|
|
1020
|
-
result: turnPromise.then(({ command, turn }) => withTurnDiagnostics(command, async () => {
|
|
1021
|
-
const result = await turn.result;
|
|
1022
|
-
if (result.status !== "failed" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(result.error.message)) return result;
|
|
1023
|
-
const stderrTail = await this.readCodexTurnFailureStderr({ handle: input.handle });
|
|
1024
|
-
if (!stderrTail) return result;
|
|
1025
|
-
return {
|
|
1026
|
-
status: "failed",
|
|
1027
|
-
error: {
|
|
1028
|
-
...result.error,
|
|
1029
|
-
code: "ACP_TURN_FAILED",
|
|
1030
|
-
message: `Internal error: ${stderrTail}`
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
|
-
})),
|
|
1034
|
-
cancel(inputArgs) {
|
|
1035
|
-
return turnPromise.then(({ turn }) => turn.cancel(inputArgs));
|
|
1036
|
-
},
|
|
1037
|
-
closeStream(inputArgs) {
|
|
1038
|
-
return turnPromise.then(({ turn }) => turn.closeStream(inputArgs));
|
|
1039
|
-
}
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
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
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
async getStatus(input) {
|
|
1050
|
-
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1051
|
-
return this.runInGeneration(input.handle, {
|
|
1052
|
-
generation: snapshot.generation,
|
|
1053
|
-
recordId: snapshot.record?.acpxRecordId
|
|
1054
|
-
}, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).getStatus(toAcpxResourceInput(input)));
|
|
1055
|
-
}
|
|
1056
|
-
async setMode(input) {
|
|
1057
|
-
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1058
|
-
await this.runInGeneration(input.handle, {
|
|
1059
|
-
generation: snapshot.generation,
|
|
1060
|
-
recordId: snapshot.record?.acpxRecordId
|
|
1061
|
-
}, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).setMode(toAcpxResourceInput(input)));
|
|
1062
|
-
}
|
|
1063
|
-
async setConfigOption(input) {
|
|
1064
|
-
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1065
|
-
return await this.runInGeneration(input.handle, {
|
|
1066
|
-
generation: snapshot.generation,
|
|
1067
|
-
recordId: snapshot.record?.acpxRecordId
|
|
1068
|
-
}, () => this.setConfigOptionUnlocked(input, snapshot));
|
|
1069
|
-
}
|
|
1070
|
-
async setConfigOptionUnlocked(logicalInput, snapshot) {
|
|
1071
|
-
const { command } = snapshot;
|
|
1072
|
-
const delegate = this.resolveDelegateForOperationSnapshot(logicalInput.handle, snapshot);
|
|
1073
|
-
const input = toAcpxResourceInput(logicalInput);
|
|
1074
|
-
const key = input.key.trim().toLowerCase();
|
|
1075
|
-
const isCodexAcp = isCodexAcpCommand(command);
|
|
1076
|
-
if (WIRE_TIMEOUT_CONFIG_KEYS.has(key) && (isCodexAcp || isClaudeAcpCommand(command))) return;
|
|
1077
|
-
if (isCodexAcp) {
|
|
1078
|
-
if (key === "model") {
|
|
1079
|
-
const classification = classifyCodexAcpModelRequest(input.value);
|
|
1080
|
-
if (classification.kind === "unsupported") failUnsupportedCodexAcpModel(input.value);
|
|
1081
|
-
const { override } = classification;
|
|
1082
|
-
const modelResult = override.model ? await delegate.setConfigOption({
|
|
1083
|
-
...input,
|
|
1084
|
-
key: "model",
|
|
1085
|
-
value: override.model
|
|
1086
|
-
}) : void 0;
|
|
1087
|
-
this.generationRegistry.assertCurrentGeneration(snapshot.generation);
|
|
1088
|
-
if (override.reasoningEffort) return await delegate.setConfigOption({
|
|
1089
|
-
...input,
|
|
1090
|
-
key: "reasoning_effort",
|
|
1091
|
-
value: override.reasoningEffort
|
|
1092
|
-
});
|
|
1093
|
-
return modelResult;
|
|
1094
|
-
}
|
|
1095
|
-
if (key === "thinking" || key === "thought_level" || key === "reasoning_effort") {
|
|
1096
|
-
const classification = classifyCodexAcpModelRequest(void 0, input.value);
|
|
1097
|
-
const reasoningEffort = classification.kind === "override" ? classification.override.reasoningEffort : void 0;
|
|
1098
|
-
if (!reasoningEffort) throw new AcpRuntimeError("ACP_BACKEND_UNSUPPORTED_CONTROL", "Clearing Codex reasoning effort on an existing session is unsupported. Choose a supported explicit effort; the current effort is unchanged.");
|
|
1099
|
-
return await delegate.setConfigOption({
|
|
1100
|
-
...input,
|
|
1101
|
-
key: "reasoning_effort",
|
|
1102
|
-
value: reasoningEffort
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
if (isClaudeAcpCommand(command) && key === "model") return await delegate.setConfigOption({
|
|
1107
|
-
...input,
|
|
1108
|
-
value: normalizeClaudeAcpModelOverride(input.value) ?? input.value
|
|
1109
|
-
});
|
|
1110
|
-
return await delegate.setConfigOption(input);
|
|
1111
|
-
}
|
|
1112
|
-
async cancel(input) {
|
|
1113
|
-
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1114
|
-
await this.runInGeneration(input.handle, {
|
|
1115
|
-
generation: snapshot.generation,
|
|
1116
|
-
recordId: snapshot.record?.acpxRecordId
|
|
1117
|
-
}, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).cancel(toAcpxResourceInput(input)));
|
|
1118
|
-
}
|
|
1119
|
-
async prepareFreshSession(input) {
|
|
1120
|
-
const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
|
|
1121
|
-
this.generationRegistry.prepareFresh(resource);
|
|
1122
|
-
this.legacyBareSessionKeys.delete(resource);
|
|
1123
|
-
}
|
|
1124
|
-
async close(input) {
|
|
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);
|
|
1128
|
-
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
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;
|
|
1147
|
-
});
|
|
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;
|
|
1160
|
-
});
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1164
|
-
/** Test-only hooks for ACPX runtime behavior that is otherwise private. */
|
|
1165
|
-
const testing = {
|
|
1166
|
-
appendCodexAcpConfigOverrides,
|
|
1167
|
-
isClaudeAcpCommand,
|
|
1168
|
-
isCodexAcpCommand
|
|
1169
|
-
};
|
|
1170
|
-
//#endregion
|
|
1171
|
-
export { ACPX_BACKEND_ID, AcpxRuntime, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, testing };
|