@openclaw/acpx 2026.9.4 → 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.
@@ -1,1040 +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.js";
2
- import { AcpRuntimeError } from "./runtime-api.js";
3
- import { t as resolveAcpxSessionResource } from "./session-resource-UWe7qD3m.js";
4
- import { a as CODEX_ACP_PACKAGE, i as isOpenClawLeaseAwareAcpxProcessCommand, n as cleanupOpenClawOwnedAcpxPendingLease, o as OPENCLAW_CODEX_CONFIG_ARG, r as cleanupOpenClawOwnedAcpxProcessTree } from "./service-BAf6nuci.js";
5
- import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
6
- import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
7
- import path, { resolve } from "node:path";
8
- import fs from "node:fs/promises";
9
- import { randomUUID } from "node:crypto";
10
- import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-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/session-owner.ts
17
- function requireAcpxOwnerMigration(sessionKey) {
18
- 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" });
19
- }
20
- function assertAcpxSessionOwnerLocator(target, legacyBareSessionKeys) {
21
- const resource = resolveAcpxSessionResource(target);
22
- const qualified = resource === target.sessionKey.trim().toLowerCase();
23
- const persisted = target.persistedHandle;
24
- if (!qualified && (legacyBareSessionKeys?.has(target.sessionKey.trim().toLowerCase()) || legacyBareSessionKeys?.has(resource) && !persisted)) requireAcpxOwnerMigration(target.sessionKey);
25
- if (persisted) {
26
- const decoded = decodeAcpxRuntimeHandleState$1(persisted.runtimeSessionName);
27
- if (!qualified && !decoded || decoded && (decoded.name !== resource || persisted.acpxRecordId && decoded.acpxRecordId !== persisted.acpxRecordId)) requireAcpxOwnerMigration(target.sessionKey);
28
- }
29
- return resource;
30
- }
31
- /** Preserve physical oneshot record IDs and the upstream-encoded runtime handle. */
32
- function toAcpxResourceInput(input) {
33
- const sessionKey = assertAcpxSessionOwnerLocator({
34
- ...input.handle,
35
- persistedHandle: input.handle
36
- });
37
- return {
38
- ...input,
39
- handle: {
40
- ...input.handle,
41
- sessionKey
42
- }
43
- };
44
- }
45
- //#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";
54
- function withOpenClawLeaseSessionMetadata(record, lease) {
55
- return {
56
- ...record,
57
- openclawLeaseId: lease.leaseId,
58
- openclawGatewayInstanceId: lease.gatewayInstanceId
59
- };
60
- }
61
- const CODEX_WRAPPER_STDERR_LOG_PREFIX = "codex-acp-wrapper.stderr";
62
- function safeDiagnosticFilePart(value) {
63
- return value.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120) || "unknown";
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
- }
85
- }
86
- function readSessionRecordName(record) {
87
- if (typeof record !== "object" || record === null) return "";
88
- const { name } = record;
89
- return typeof name === "string" ? name.trim() : "";
90
- }
91
- function readRecordAgentCommand(record) {
92
- return record?.agentArgv ?? record?.agentCommand;
93
- }
94
- function readRecordCwd(record) {
95
- if (typeof record !== "object" || record === null) return;
96
- const { cwd } = record;
97
- return typeof cwd === "string" ? cwd.trim() || void 0 : void 0;
98
- }
99
- function readRecordResetOnNextEnsure(record) {
100
- if (typeof record !== "object" || record === null) return false;
101
- const { acpx } = record;
102
- if (typeof acpx !== "object" || acpx === null) return false;
103
- return acpx.reset_on_next_ensure === true;
104
- }
105
- function readRecordAgentPid(record) {
106
- if (typeof record !== "object" || record === null) return;
107
- const { pid, processId } = record;
108
- const rawPid = pid ?? processId;
109
- const numericPid = typeof rawPid === "number" ? rawPid : typeof rawPid === "string" ? parseStrictPositiveInteger(rawPid) : void 0;
110
- return numericPid && Number.isInteger(numericPid) && numericPid > 0 ? numericPid : void 0;
111
- }
112
- function readOpenClawLeaseIdFromRecord(record) {
113
- if (typeof record !== "object" || record === null) return;
114
- const { openclawLeaseId } = record;
115
- return typeof openclawLeaseId === "string" ? openclawLeaseId.trim() || void 0 : void 0;
116
- }
117
- function readOpenClawGatewayInstanceIdFromRecord(record) {
118
- if (typeof record !== "object" || record === null) return;
119
- const { openclawGatewayInstanceId } = record;
120
- return typeof openclawGatewayInstanceId === "string" ? openclawGatewayInstanceId.trim() || void 0 : void 0;
121
- }
122
- function extractGeneratedWrapperPath(command) {
123
- return splitCommandParts(command ?? "").find((part) => basename(part) === "codex-acp-wrapper.mjs" || basename(part) === "claude-agent-acp-wrapper.mjs") ?? "";
124
- }
125
- function selectCurrentSessionLease(params) {
126
- const sessionKeys = new Set(normalizeStringEntries(params.sessionKeys));
127
- const candidates = params.leases.filter((lease) => sessionKeys.has(lease.sessionKey));
128
- if (params.rootPid) return candidates.find((lease) => lease.rootPid === params.rootPid);
129
- let selected;
130
- for (const lease of candidates) if (!selected || lease.startedAt > selected.startedAt) selected = lease;
131
- return selected;
132
- }
133
- function createResetAwareSessionStore(baseStore, params) {
134
- const freshSessionKeys = /* @__PURE__ */ new Set();
135
- return {
136
- async load(sessionId) {
137
- const normalized = sessionId.trim();
138
- if (normalized && freshSessionKeys.has(normalized)) return;
139
- const record = await baseStore.load(sessionId);
140
- if (!record || !params?.leaseStore || !params.gatewayInstanceId) return record;
141
- const sessionName = readSessionRecordName(record) || normalized;
142
- const lease = selectCurrentSessionLease({
143
- leases: await params.leaseStore.listOpen(params.gatewayInstanceId),
144
- sessionKeys: [sessionName, normalized],
145
- rootPid: readRecordAgentPid(record)
146
- });
147
- if (!lease) return record;
148
- return withOpenClawLeaseSessionMetadata(record, lease);
149
- },
150
- async save(record) {
151
- let recordToSave = record;
152
- const launch = params?.launchScope?.getStore();
153
- const sessionName = readSessionRecordName(record);
154
- const agentCommand = readRecordAgentCommand(record);
155
- const leasedCommand = launch?.leasedCommand ?? agentCommand;
156
- const leaseIdentity = launch ?? readAcpxProcessLeaseIdentity(leasedCommand);
157
- if (params?.leaseStore && params.gatewayInstanceId && params.wrapperRoot && (!launch || sessionName === launch.sessionKey) && leasedCommand && leaseIdentity?.gatewayInstanceId === params.gatewayInstanceId && isOpenClawLeaseAwareAcpxProcessCommand({
158
- command: leasedCommand,
159
- wrapperRoot: params.wrapperRoot
160
- })) {
161
- const existing = await params.leaseStore.load(leaseIdentity.leaseId);
162
- if (!existing || existing.gatewayInstanceId === leaseIdentity.gatewayInstanceId && existing.sessionKey === sessionName && existing.wrapperRoot === params.wrapperRoot) {
163
- const adoptingLease = Boolean(launch && !isDeepStrictEqual(splitCommandParts(launch.resolvedCommand), splitCommandParts(launch.leasedCommand)));
164
- const persistedCommand = launch && !adoptingLease ? launch.resolvedCommand : leasedCommand;
165
- const lifecycleRecord = adoptingLease ? {
166
- ...record,
167
- pid: void 0,
168
- processId: void 0,
169
- agentStartedAt: void 0
170
- } : record;
171
- const rootPid = readRecordAgentPid(lifecycleRecord);
172
- if (rootPid) await params.leaseStore.save({
173
- leaseId: leaseIdentity.leaseId,
174
- gatewayInstanceId: leaseIdentity.gatewayInstanceId,
175
- sessionKey: sessionName,
176
- wrapperRoot: params.wrapperRoot,
177
- wrapperPath: extractGeneratedWrapperPath(leasedCommand),
178
- rootPid,
179
- ...existing?.rootPid === rootPid && existing.processGroupId ? { processGroupId: existing.processGroupId } : {},
180
- commandHash: hashAcpxProcessCommand(persistedCommand),
181
- startedAt: existing?.rootPid === rootPid ? existing.startedAt : Date.now(),
182
- state: "open"
183
- });
184
- recordToSave = withOpenClawLeaseSessionMetadata({
185
- ...lifecycleRecord,
186
- agentCommand: renderAgentCommand(persistedCommand),
187
- agentArgv: Array.isArray(persistedCommand) ? persistedCommand : void 0
188
- }, leaseIdentity);
189
- }
190
- }
191
- await baseStore.save(recordToSave);
192
- if (sessionName) freshSessionKeys.delete(sessionName);
193
- },
194
- markFresh(sessionKey) {
195
- const normalized = sessionKey.trim();
196
- if (normalized) freshSessionKeys.add(normalized);
197
- }
198
- };
199
- }
200
- const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
201
- const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
202
- const CODEX_ACP_AGENT_ID = "codex";
203
- const CODEX_ACP_OPENCLAW_PREFIX = "openai/";
204
- const CLAUDE_ACP_OPENCLAW_PREFIX = /^(?:anthropic|amazon-bedrock)\//i;
205
- const CODEX_ACP_THINKING_ALIASES = /* @__PURE__ */ new Map([
206
- ["off", void 0],
207
- ["minimal", "low"],
208
- ["low", "low"],
209
- ["medium", "medium"],
210
- ["high", "high"],
211
- ["x-high", "xhigh"],
212
- ["x_high", "xhigh"],
213
- ["extra-high", "xhigh"],
214
- ["extra_high", "xhigh"],
215
- ["extra high", "xhigh"],
216
- ["xhigh", "xhigh"]
217
- ]);
218
- function normalizeAgentName(value) {
219
- const normalized = value?.trim().toLowerCase();
220
- return normalized ? normalized : void 0;
221
- }
222
- function readAgentFromSessionKey(sessionKey) {
223
- const normalized = sessionKey?.trim();
224
- if (!normalized) return;
225
- return normalizeAgentName(/^agent:(?<agent>[^:]+):/i.exec(normalized)?.groups?.agent);
226
- }
227
- function readAgentFromHandle(handle) {
228
- return normalizeAgentName(decodeAcpxRuntimeHandleState(handle.runtimeSessionName)?.agent) ?? readAgentFromSessionKey(handle.sessionKey);
229
- }
230
- function basename(value) {
231
- return value.split(/[\\/]/).pop() ?? value;
232
- }
233
- function isEnvAssignment(value) {
234
- return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value);
235
- }
236
- function unwrapEnvCommand(parts) {
237
- const command = parts.at(0);
238
- if (!command || basename(command) !== "env") return parts;
239
- let index = 1;
240
- while (true) {
241
- const part = parts.at(index);
242
- if (!part || !isEnvAssignment(part)) break;
243
- index += 1;
244
- }
245
- return parts.slice(index);
246
- }
247
- function matchesExecutableName(value, executableName) {
248
- const normalized = basename(value).toLowerCase();
249
- return normalized === executableName || normalized === `${executableName}.exe`;
250
- }
251
- function matchesPackageSpec(value, packageName) {
252
- const normalized = value.trim().toLowerCase();
253
- return normalized === packageName || normalized.startsWith(`${packageName}@`);
254
- }
255
- function stripModuleExtension(value) {
256
- return value.replace(/\.[cm]?js$/i, "").toLowerCase();
257
- }
258
- function isAcpCommand(command, params) {
259
- if (!command) return false;
260
- const parts = unwrapEnvCommand(splitCommandParts(command));
261
- if (!parts.length) return false;
262
- if (parts.some((part) => matchesPackageSpec(part, params.packageName))) return true;
263
- const commandName = basename(parts[0] ?? "");
264
- if (matchesExecutableName(commandName, params.executableName)) return true;
265
- if (!matchesExecutableName(commandName, "node")) return false;
266
- const scriptName = stripModuleExtension(basename(parts[1] ?? ""));
267
- return scriptName === params.executableName || scriptName === `${params.executableName}-wrapper`;
268
- }
269
- function isOpenClawBridgeCommand(command) {
270
- if (!command) return false;
271
- const parts = unwrapEnvCommand(splitCommandParts(command));
272
- if (basename(parts[0] ?? "") === OPENCLAW_BRIDGE_EXECUTABLE) return parts[1] === OPENCLAW_BRIDGE_SUBCOMMAND;
273
- if (basename(parts[0] ?? "") !== "node") return false;
274
- const scriptName = basename(parts[1] ?? "");
275
- return /^openclaw(?:\.[cm]?js)?$/i.test(scriptName) && parts[2] === OPENCLAW_BRIDGE_SUBCOMMAND;
276
- }
277
- function isCodexAcpCommand(command) {
278
- return isAcpCommand(command, {
279
- packageName: CODEX_ACP_PACKAGE,
280
- executableName: "codex-acp"
281
- });
282
- }
283
- function isClaudeAcpCommand(command) {
284
- return isAcpCommand(command, {
285
- packageName: "@agentclientprotocol/claude-agent-acp",
286
- executableName: "claude-agent-acp"
287
- });
288
- }
289
- function failUnsupportedCodexAcpModel(rawModel) {
290
- throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Codex ACP model "${rawModel}" is not supported. Use openai/<model> or <model>/<reasoning-effort>.`);
291
- }
292
- const WIRE_TIMEOUT_CONFIG_KEYS = /* @__PURE__ */ new Set(["timeout", "timeout_seconds"]);
293
- function assertSupportedRuntimeSessionMode(mode) {
294
- if (mode === "persistent" || mode === "oneshot") return;
295
- throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Unsupported ACP runtime session mode ${JSON.stringify(mode)}. Expected one of: persistent, oneshot.`);
296
- }
297
- function failUnsupportedCodexAcpThinking(rawThinking) {
298
- throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Codex ACP thinking level "${rawThinking}" is not supported. Use off, minimal, low, medium, high, or xhigh.`);
299
- }
300
- function normalizeCodexAcpReasoningEffort(rawThinking) {
301
- const normalized = rawThinking?.trim().toLowerCase();
302
- if (!normalized) return;
303
- if (!CODEX_ACP_THINKING_ALIASES.has(normalized)) failUnsupportedCodexAcpThinking(rawThinking ?? "");
304
- return CODEX_ACP_THINKING_ALIASES.get(normalized);
305
- }
306
- function isCodexAcpReasoningEffortAlias(value) {
307
- const normalized = value?.trim().toLowerCase();
308
- return Boolean(normalized && CODEX_ACP_THINKING_ALIASES.has(normalized));
309
- }
310
- function classifyCodexAcpModelRequest(rawModel, rawThinking) {
311
- const raw = rawModel?.trim();
312
- const thinkingReasoningEffort = normalizeCodexAcpReasoningEffort(rawThinking);
313
- const thinkingOnlyOverride = thinkingReasoningEffort ? { reasoningEffort: thinkingReasoningEffort } : void 0;
314
- if (!raw) return {
315
- kind: "override",
316
- override: thinkingOnlyOverride ?? {}
317
- };
318
- let value = raw;
319
- let hadOpenAiQualifier = false;
320
- if (value.toLowerCase().startsWith(CODEX_ACP_OPENCLAW_PREFIX)) {
321
- value = value.slice(7);
322
- hadOpenAiQualifier = true;
323
- }
324
- let model = value.trim();
325
- let modelReasoningEffort;
326
- const slashIndex = value.lastIndexOf("/");
327
- if (slashIndex >= 0 && isCodexAcpReasoningEffortAlias(value.slice(slashIndex + 1))) {
328
- modelReasoningEffort = normalizeCodexAcpReasoningEffort(value.slice(slashIndex + 1));
329
- model = value.slice(0, slashIndex).trim();
330
- }
331
- if (hadOpenAiQualifier && (!model || model.includes("/"))) failUnsupportedCodexAcpModel(raw);
332
- if (!model || model.includes("/")) return thinkingOnlyOverride ? {
333
- kind: "unsupported",
334
- thinkingOverride: thinkingOnlyOverride
335
- } : { kind: "unsupported" };
336
- const reasoningEffort = rawThinking?.trim() ? thinkingReasoningEffort : modelReasoningEffort;
337
- return {
338
- kind: "override",
339
- override: {
340
- model,
341
- ...reasoningEffort ? { reasoningEffort } : {}
342
- }
343
- };
344
- }
345
- function withCodexSessionModel(input, override) {
346
- const next = { ...input };
347
- if (override?.model) next.model = override.model;
348
- else delete next.model;
349
- return next;
350
- }
351
- function normalizeClaudeAcpModelOverride(rawModel) {
352
- const raw = rawModel?.trim();
353
- if (!raw) return;
354
- const prefix = raw.match(CLAUDE_ACP_OPENCLAW_PREFIX);
355
- if (!prefix) return raw;
356
- return raw.slice(prefix[0].length).trim() || void 0;
357
- }
358
- function withAcpxSessionOptions(input) {
359
- const existingOptions = input.sessionOptions;
360
- const model = input.model?.trim() || existingOptions?.model;
361
- const sessionOptions = model ? {
362
- ...existingOptions,
363
- model
364
- } : existingOptions;
365
- const { modelExplicit: _modelExplicit, ...rest } = input;
366
- return {
367
- ...rest,
368
- ...sessionOptions ? { sessionOptions } : {}
369
- };
370
- }
371
- function isAcpModelCapabilityMissingError(error) {
372
- return isRequestedModelUnsupportedError(error) && error.reason === "missing-capability";
373
- }
374
- async function ensureDelegateSessionWithModelFallback(delegate, input) {
375
- try {
376
- return await delegate.ensureSession(withAcpxSessionOptions(input));
377
- } catch (error) {
378
- if (input.modelExplicit || !input.model || !isAcpModelCapabilityMissingError(error)) throw error;
379
- return {
380
- ...await delegate.ensureSession(withAcpxSessionOptions({
381
- ...input,
382
- model: void 0
383
- })),
384
- appliedModel: { kind: "dropped" }
385
- };
386
- }
387
- }
388
- function appendCodexAcpConfigOverrides(command, override) {
389
- const config = {
390
- ...override.model ? { model: override.model } : {},
391
- ...override.reasoningEffort ? { model_reasoning_effort: override.reasoningEffort } : {}
392
- };
393
- if (Object.keys(config).length === 0) return command;
394
- return [
395
- ...splitCommandParts(command),
396
- OPENCLAW_CODEX_CONFIG_ARG,
397
- JSON.stringify(config)
398
- ];
399
- }
400
- function resolveAgentCommand(params) {
401
- const normalizedAgentName = normalizeAgentName(params.agentName);
402
- if (!normalizedAgentName) return;
403
- return splitCommandParts(params.agentRegistry.resolve(normalizedAgentName));
404
- }
405
- function shouldUseDistinctBridgeDelegate(options) {
406
- const { mcpServers } = options;
407
- return Array.isArray(mcpServers) && mcpServers.length > 0;
408
- }
409
- function withManagedToolsMcpSessionEnv(params) {
410
- const sessionKey = params.sessionKey.trim();
411
- if (!params.pluginToolsEnabled && !params.openclawToolsEnabled || !sessionKey || !params.mcpServers?.length) return params.mcpServers;
412
- let changed = false;
413
- const nextServers = params.mcpServers.map((server) => {
414
- const isManagedPluginTools = params.pluginToolsEnabled && server.name === ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME;
415
- const isManagedOpenClawTools = params.openclawToolsEnabled && server.name === ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME;
416
- if (!isManagedPluginTools && !isManagedOpenClawTools || !("command" in server)) return server;
417
- changed = true;
418
- const env = [...server.env.filter((entry) => entry.name !== OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV), {
419
- name: OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV,
420
- value: sessionKey
421
- }];
422
- return {
423
- ...server,
424
- env,
425
- args: params.agentId ? [
426
- ...server.args,
427
- "--openclaw-agent-id",
428
- params.agentId
429
- ] : server.args
430
- };
431
- });
432
- return changed ? nextServers : params.mcpServers;
433
- }
434
- /** OpenClaw-managed ACP runtime implementation backed by the upstream acpx runtime. */
435
- var AcpxRuntime = class {
436
- constructor(options, testOptions) {
437
- this.ownerAwareSessions = 1;
438
- this.launchCommandScope = new AsyncLocalStorage();
439
- this.managedToolsSessionDelegates = /* @__PURE__ */ new Map();
440
- 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
- this.legacyBareSessionKeys = new Set(options.openclawLegacyBareSessionKeys);
446
- const { openclawProcessCleanup, ...delegateTestOptions } = testOptions ?? {};
447
- this.processCleanupDeps = openclawProcessCleanup;
448
- this.wrapperRoot = options.openclawWrapperRoot;
449
- this.gatewayInstanceId = options.openclawGatewayInstanceId;
450
- this.processLeaseStore = options.openclawProcessLeaseStore;
451
- this.pluginToolsMcpBridgeEnabled = options.pluginToolsMcpBridgeEnabled === true;
452
- this.openclawToolsMcpBridgeEnabled = options.openclawToolsMcpBridgeEnabled === true;
453
- this.managedToolsMcpBridgeEnabled = this.pluginToolsMcpBridgeEnabled || this.openclawToolsMcpBridgeEnabled;
454
- this.cwd = options.cwd;
455
- this.sessionStore = createResetAwareSessionStore(options.sessionStore, {
456
- gatewayInstanceId: this.gatewayInstanceId,
457
- leaseStore: this.processLeaseStore,
458
- launchScope: this.launchLeaseScope,
459
- wrapperRoot: this.wrapperRoot
460
- });
461
- this.agentRegistry = options.agentRegistry;
462
- this.scopedAgentRegistry = {
463
- resolve: (agentName) => {
464
- const launch = this.launchCommandScope.getStore();
465
- return launch && launch.agent === normalizeAgentName(agentName) && launch.command ? launch.command : this.agentRegistry.resolve(agentName);
466
- },
467
- list: () => this.agentRegistry.list()
468
- };
469
- const sharedOptions = {
470
- ...options,
471
- sessionStore: this.sessionStore,
472
- agentRegistry: this.scopedAgentRegistry
473
- };
474
- this.delegateOptions = sharedOptions;
475
- this.delegateTestOptions = delegateTestOptions;
476
- this.delegate = new AcpxRuntime$1(sharedOptions, this.delegateTestOptions);
477
- this.bridgeSafeDelegate = shouldUseDistinctBridgeDelegate(options) ? new AcpxRuntime$1({
478
- ...sharedOptions,
479
- mcpServers: []
480
- }, this.delegateTestOptions) : this.delegate;
481
- this.probeAgent = normalizeAgentName(options.probeAgent) ?? "codex";
482
- const probeCommand = resolveAgentCommand({
483
- agentName: this.probeAgent,
484
- agentRegistry: this.agentRegistry
485
- });
486
- this.probeCommand = probeCommand;
487
- const useBridgeSafeProbe = this.managedToolsMcpBridgeEnabled || isOpenClawBridgeCommand(probeCommand);
488
- this.probeDelegate = useBridgeSafeProbe ? this.bridgeSafeDelegate : this.delegate;
489
- }
490
- resolveDelegateForSession(params) {
491
- if (isOpenClawBridgeCommand(params.command)) return this.bridgeSafeDelegate;
492
- return this.resolveManagedToolsDelegateForSession(params);
493
- }
494
- resolveManagedToolsDelegateForSession(target) {
495
- if (!this.managedToolsMcpBridgeEnabled) return this.delegate;
496
- const normalizedSessionKey = resolveAcpxSessionResource(target);
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;
511
- }
512
- async loadOperationSnapshotForHandle(handle) {
513
- assertAcpxSessionOwnerLocator({
514
- ...handle,
515
- persistedHandle: handle
516
- }, this.legacyBareSessionKeys);
517
- const record = await this.sessionStore.load(handle.acpxRecordId ?? resolveAcpxSessionResource(handle));
518
- return {
519
- record,
520
- command: readRecordAgentCommand(record) ?? resolveAgentCommand({
521
- agentName: readAgentFromHandle(handle),
522
- agentRegistry: this.agentRegistry
523
- })
524
- };
525
- }
526
- resolveDelegateForOperationSnapshot(handle, snapshot) {
527
- return this.resolveDelegateForSession({
528
- command: snapshot.command,
529
- sessionKey: handle.sessionKey,
530
- agentId: handle.agentId
531
- });
532
- }
533
- async readReusablePersistentSessionCommand(params) {
534
- if (params.mode !== "persistent" || !params.command) return;
535
- const existing = await this.sessionStore.load(params.sessionKey);
536
- if (!existing || readRecordResetOnNextEnsure(existing)) return;
537
- const recordCwd = readRecordCwd(existing);
538
- if (!recordCwd || resolve(recordCwd) !== resolve(params.cwd?.trim() || this.cwd)) return;
539
- const recordCommand = readRecordAgentCommand(existing);
540
- if (!recordCommand) return;
541
- const leaseIdentity = readAcpxProcessLeaseIdentity(recordCommand);
542
- if (leaseIdentity && leaseIdentity.gatewayInstanceId !== this.gatewayInstanceId) return;
543
- const stableRecordCommand = leaseIdentity ? withAcpxLeaseArgs({
544
- command: params.command,
545
- leaseId: leaseIdentity.leaseId,
546
- gatewayInstanceId: leaseIdentity.gatewayInstanceId
547
- }) : params.command;
548
- if (!isDeepStrictEqual(splitCommandParts(recordCommand), splitCommandParts(stableRecordCommand))) return;
549
- return !params.resumeSessionId || existing.acpSessionId === params.resumeSessionId ? recordCommand : void 0;
550
- }
551
- async runWithLaunchLease(params) {
552
- if (!params.command || !this.wrapperRoot || !this.gatewayInstanceId || !this.processLeaseStore || !isOpenClawLeaseAwareAcpxProcessCommand({
553
- command: params.command,
554
- wrapperRoot: this.wrapperRoot
555
- })) return await this.launchCommandScope.run({
556
- agent: normalizeAgentName(params.agent) ?? params.agent,
557
- command: params.reusableCommand ?? params.command
558
- }, params.run);
559
- const processLeaseStore = this.processLeaseStore;
560
- const reusableIdentity = readAcpxProcessLeaseIdentity(params.reusableCommand);
561
- const canReuseLeaseIdentity = reusableIdentity?.gatewayInstanceId === this.gatewayInstanceId;
562
- const leaseId = canReuseLeaseIdentity ? reusableIdentity.leaseId : params.finalizeCompletedProbe ? `probe-${hashAcpxProcessCommand(`${this.gatewayInstanceId}\0${extractGeneratedWrapperPath(params.command)}`)}` : randomUUID();
563
- const leasedCommand = withAcpxLeaseArgs({
564
- command: params.command,
565
- leaseId,
566
- gatewayInstanceId: this.gatewayInstanceId
567
- });
568
- const launch = {
569
- leaseId,
570
- gatewayInstanceId: this.gatewayInstanceId,
571
- sessionKey: params.sessionKey,
572
- wrapperRoot: this.wrapperRoot,
573
- resolvedCommand: params.reusableCommand ?? leasedCommand,
574
- leasedCommand
575
- };
576
- await this.retainProcessLeaseOperation(launch, async () => {
577
- const reusableLease = canReuseLeaseIdentity ? await processLeaseStore.load(launch.leaseId) : void 0;
578
- if (reusableLease && (reusableLease.gatewayInstanceId !== launch.gatewayInstanceId || reusableLease.sessionKey !== launch.sessionKey || reusableLease.wrapperRoot !== launch.wrapperRoot)) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", `ACPX process lease ${launch.leaseId} belongs to another session`);
579
- if (!(!reusableLease && (!params.reusableCommand || isDeepStrictEqual(splitCommandParts(params.reusableCommand), splitCommandParts(leasedCommand))))) return;
580
- await processLeaseStore.save({
581
- leaseId: launch.leaseId,
582
- gatewayInstanceId: launch.gatewayInstanceId,
583
- sessionKey: launch.sessionKey,
584
- wrapperRoot: launch.wrapperRoot,
585
- wrapperPath: extractGeneratedWrapperPath(leasedCommand),
586
- rootPid: 0,
587
- commandHash: hashAcpxProcessCommand(leasedCommand),
588
- startedAt: Date.now(),
589
- state: "open"
590
- });
591
- });
592
- try {
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
- }
604
- }
605
- async prepareProcessLeaseForOperation(handle, record) {
606
- if (!this.processLeaseStore || !this.gatewayInstanceId || !this.wrapperRoot) return;
607
- const processLeaseStore = this.processLeaseStore;
608
- const wrapperRoot = this.wrapperRoot;
609
- const recordPid = readRecordAgentPid(record);
610
- const command = readRecordAgentCommand(record);
611
- const identity = readAcpxProcessLeaseIdentity(command);
612
- if (!command || !isOpenClawLeaseAwareAcpxProcessCommand({
613
- command,
614
- wrapperRoot
615
- })) return;
616
- if (!identity) return;
617
- if (identity.gatewayInstanceId !== this.gatewayInstanceId) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another gateway`);
618
- await this.retainProcessLeaseOperation(identity, async () => {
619
- const existing = await processLeaseStore.load(identity.leaseId);
620
- if (!existing) {
621
- await processLeaseStore.save({
622
- leaseId: identity.leaseId,
623
- gatewayInstanceId: identity.gatewayInstanceId,
624
- sessionKey: resolveAcpxSessionResource(handle),
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);
702
- });
703
- }
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
- async withCodexWrapperDiagnostics(params) {
720
- try {
721
- return await params.run();
722
- } catch (error) {
723
- if (!isCodexAcpCommand(params.command) || !isGenericInternalAcpError(error)) throw error;
724
- const stderrTail = params.handle ? await this.readCodexTurnFailureStderr({ handle: params.handle }) : await readCodexWrapperStderrTail({
725
- wrapperRoot: this.wrapperRoot,
726
- leaseId: this.launchLeaseScope.getStore()?.leaseId
727
- });
728
- if (!stderrTail) throw error;
729
- throw new AcpRuntimeError(params.fallbackCode, `Internal error: ${stderrTail}`, { cause: error });
730
- }
731
- }
732
- async readCodexTurnFailureStderr(params) {
733
- const record = await this.sessionStore.load(params.handle.acpxRecordId ?? resolveAcpxSessionResource(params.handle));
734
- return readCodexWrapperStderrTail({
735
- wrapperRoot: this.wrapperRoot,
736
- leaseId: readOpenClawLeaseIdFromRecord(record)
737
- });
738
- }
739
- async cleanupProcessTreeForRecord(handle, record) {
740
- const leaseId = readOpenClawLeaseIdFromRecord(record);
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
- });
777
- }
778
- isHealthy() {
779
- return this.probeDelegate.isHealthy();
780
- }
781
- async probeAvailability() {
782
- await this.runWithLaunchLease({
783
- agent: this.probeAgent,
784
- sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
785
- command: this.probeCommand,
786
- finalizeCompletedProbe: true,
787
- run: () => this.probeDelegate.probeAvailability()
788
- });
789
- }
790
- async doctor() {
791
- return await this.runWithLaunchLease({
792
- agent: this.probeAgent,
793
- sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
794
- command: this.probeCommand,
795
- finalizeCompletedProbe: true,
796
- run: () => this.probeDelegate.doctor()
797
- });
798
- }
799
- async ensureSession(input) {
800
- const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
801
- return await this.sessionEnsureQueue.enqueue(resource.trim() || resource, () => this.ensureSessionUnlocked(input));
802
- }
803
- async ensureSessionUnlocked(logicalInput) {
804
- assertSupportedRuntimeSessionMode(logicalInput.mode);
805
- const command = resolveAgentCommand({
806
- agentName: logicalInput.agent,
807
- agentRegistry: this.agentRegistry
808
- });
809
- const delegate = this.resolveDelegateForSession({
810
- command,
811
- sessionKey: logicalInput.sessionKey,
812
- agentId: logicalInput.agentId
813
- });
814
- const logicalTarget = {
815
- sessionKey: logicalInput.sessionKey,
816
- agentId: logicalInput.agentId
817
- };
818
- const input = {
819
- ...logicalInput,
820
- sessionKey: resolveAcpxSessionResource(logicalInput)
821
- };
822
- const isCodexAcp = normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
823
- const claudeModelOverride = isClaudeAcpCommand(command) ? normalizeClaudeAcpModelOverride(input.model) : void 0;
824
- const codexClassification = isCodexAcp ? classifyCodexAcpModelRequest(input.model, input.thinking) : void 0;
825
- if (codexClassification?.kind === "unsupported" && input.modelExplicit) failUnsupportedCodexAcpModel(input.model ?? "");
826
- const classifiedCodexOverride = codexClassification?.kind === "override" ? codexClassification.override : codexClassification?.thinkingOverride;
827
- const codexModelOverride = classifiedCodexOverride && Object.keys(classifiedCodexOverride).length > 0 ? classifiedCodexOverride : void 0;
828
- const requestedModel = input.model?.trim();
829
- const appliedModel = isCodexAcp && requestedModel ? codexModelOverride?.model ? {
830
- kind: "applied",
831
- model: requestedModel
832
- } : { kind: "dropped" } : void 0;
833
- const ensureInput = isCodexAcp ? withCodexSessionModel(input, codexModelOverride) : claudeModelOverride ? {
834
- ...input,
835
- model: claudeModelOverride
836
- } : input;
837
- const stableLaunchCommand = codexModelOverride && command ? appendCodexAcpConfigOverrides(command, codexModelOverride) : command;
838
- const reusableCommand = await this.readReusablePersistentSessionCommand({
839
- sessionKey: input.sessionKey,
840
- mode: input.mode,
841
- cwd: input.cwd,
842
- command: stableLaunchCommand,
843
- resumeSessionId: input.resumeSessionId
844
- });
845
- return {
846
- ...await this.runWithLaunchLease({
847
- agent: ensureInput.agent,
848
- sessionKey: ensureInput.sessionKey,
849
- command: stableLaunchCommand,
850
- reusableCommand,
851
- run: () => this.withCodexWrapperDiagnostics({
852
- command: stableLaunchCommand,
853
- fallbackCode: "ACP_SESSION_INIT_FAILED",
854
- run: () => codexModelOverride ? delegate.ensureSession(withAcpxSessionOptions(ensureInput)) : ensureDelegateSessionWithModelFallback(delegate, ensureInput)
855
- })
856
- }),
857
- ...logicalTarget,
858
- ...appliedModel ? { appliedModel } : {}
859
- };
860
- }
861
- async *runTurn(input) {
862
- const turn = this.startTurn(input);
863
- turn.result.catch(() => {});
864
- let completed = false;
865
- try {
866
- yield* turn.events;
867
- const result = await turn.result;
868
- completed = true;
869
- yield result.status === "failed" ? {
870
- type: "error",
871
- ...result.error
872
- } : {
873
- type: "done",
874
- ...result.stopReason ? { stopReason: result.stopReason } : {}
875
- };
876
- } finally {
877
- if (!completed) {
878
- await turn.cancel({ reason: "stream-closed" }).catch(() => {});
879
- await turn.closeStream({ reason: "stream-closed" }).catch(() => {});
880
- await turn.result.catch(() => {});
881
- }
882
- }
883
- }
884
- startTurn(input) {
885
- const withTurnDiagnostics = (command, run) => this.withCodexWrapperDiagnostics({
886
- command,
887
- handle: input.handle,
888
- fallbackCode: "ACP_TURN_FAILED",
889
- run
890
- });
891
- const snapshotPromise = this.loadOperationSnapshotForHandle(input.handle);
892
- const turnLeasePromise = snapshotPromise.then(({ record }) => this.prepareProcessLeaseForOperation(input.handle, record));
893
- const turnPromise = Promise.all([snapshotPromise, turnLeasePromise]).then(([snapshot]) => {
894
- const { command } = snapshot;
895
- const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
896
- return withTurnDiagnostics(command, async () => ({
897
- command,
898
- turn: delegate.startTurn({
899
- ...toAcpxResourceInput(input),
900
- timeoutMs: 0
901
- })
902
- }));
903
- });
904
- return {
905
- requestId: input.requestId,
906
- get promptStarted() {
907
- return turnPromise.then(({ turn }) => turn.promptStarted);
908
- },
909
- events: { async *[Symbol.asyncIterator]() {
910
- const { command, turn } = await turnPromise;
911
- try {
912
- yield* turn.events;
913
- } catch (error) {
914
- if (!isGenericInternalAcpError(error)) throw error;
915
- await withTurnDiagnostics(command, () => Promise.reject(error));
916
- }
917
- } },
918
- result: this.finalizeProcessLeaseAfter(input.handle, turnLeasePromise, turnPromise.then(({ command, turn }) => withTurnDiagnostics(command, async () => {
919
- const result = await turn.result;
920
- if (result.status !== "failed" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(result.error.message)) return result;
921
- const stderrTail = await this.readCodexTurnFailureStderr({ handle: input.handle });
922
- if (!stderrTail) return result;
923
- return {
924
- status: "failed",
925
- error: {
926
- ...result.error,
927
- code: "ACP_TURN_FAILED",
928
- message: `Internal error: ${stderrTail}`
929
- }
930
- };
931
- }))),
932
- cancel(inputArgs) {
933
- return turnPromise.then(({ turn }) => turn.cancel(inputArgs));
934
- },
935
- closeStream(inputArgs) {
936
- return turnPromise.then(({ turn }) => turn.closeStream(inputArgs));
937
- }
938
- };
939
- }
940
- getCapabilities(input) {
941
- return this.delegate.getCapabilities(input?.handle ? toAcpxResourceInput({ handle: input.handle }) : input);
942
- }
943
- async getStatus(input) {
944
- const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
945
- return this.resolveDelegateForOperationSnapshot(input.handle, snapshot).getStatus(toAcpxResourceInput(input));
946
- }
947
- async setMode(input) {
948
- const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
949
- await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).setMode(toAcpxResourceInput(input)));
950
- }
951
- async setConfigOption(input) {
952
- const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
953
- return await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.setConfigOptionUnlocked(input, snapshot));
954
- }
955
- async setConfigOptionUnlocked(logicalInput, snapshot) {
956
- const { command } = snapshot;
957
- const delegate = this.resolveDelegateForOperationSnapshot(logicalInput.handle, snapshot);
958
- const input = toAcpxResourceInput(logicalInput);
959
- const key = input.key.trim().toLowerCase();
960
- const isCodexAcp = isCodexAcpCommand(command);
961
- if (WIRE_TIMEOUT_CONFIG_KEYS.has(key) && (isCodexAcp || isClaudeAcpCommand(command))) return;
962
- if (isCodexAcp) {
963
- if (key === "model") {
964
- const classification = classifyCodexAcpModelRequest(input.value);
965
- if (classification.kind === "unsupported") failUnsupportedCodexAcpModel(input.value);
966
- const { override } = classification;
967
- const modelResult = override.model ? await delegate.setConfigOption({
968
- ...input,
969
- key: "model",
970
- value: override.model
971
- }) : void 0;
972
- if (override.reasoningEffort) return await delegate.setConfigOption({
973
- ...input,
974
- key: "reasoning_effort",
975
- value: override.reasoningEffort
976
- });
977
- return modelResult;
978
- }
979
- if (key === "thinking" || key === "thought_level" || key === "reasoning_effort") {
980
- const classification = classifyCodexAcpModelRequest(void 0, input.value);
981
- const reasoningEffort = classification.kind === "override" ? classification.override.reasoningEffort : void 0;
982
- 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.");
983
- return await delegate.setConfigOption({
984
- ...input,
985
- key: "reasoning_effort",
986
- value: reasoningEffort
987
- });
988
- }
989
- }
990
- if (isClaudeAcpCommand(command) && key === "model") return await delegate.setConfigOption({
991
- ...input,
992
- value: normalizeClaudeAcpModelOverride(input.value) ?? input.value
993
- });
994
- return await delegate.setConfigOption(input);
995
- }
996
- async cancel(input) {
997
- const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
998
- await this.resolveDelegateForOperationSnapshot(input.handle, snapshot).cancel(toAcpxResourceInput(input));
999
- }
1000
- async prepareFreshSession(input) {
1001
- const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
1002
- this.sessionStore.markFresh(resource);
1003
- this.legacyBareSessionKeys.delete(resource);
1004
- }
1005
- async close(input) {
1006
- const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
1007
- const closeLease = await this.prepareProcessLeaseForOperation(input.handle, snapshot.record);
1008
- let cleanupSucceeded = false;
1009
- try {
1010
- const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
1011
- const handle = toAcpxResourceInput(input).handle;
1012
- try {
1013
- await delegate.close({
1014
- handle,
1015
- reason: input.reason,
1016
- discardPersistentState: input.discardPersistentState
1017
- });
1018
- if (this.managedToolsSessionDelegates.get(handle.sessionKey) === delegate) this.managedToolsSessionDelegates.delete(handle.sessionKey);
1019
- } finally {
1020
- await this.cleanupProcessTreeForRecord(input.handle, snapshot.record);
1021
- cleanupSucceeded = true;
1022
- }
1023
- if (input.discardPersistentState) await this.prepareFreshSession({
1024
- ...input.handle,
1025
- persistedHandle: input.handle
1026
- });
1027
- } finally {
1028
- if (cleanupSucceeded) await this.finalizeProcessLeaseForOperation(input.handle, closeLease);
1029
- else await this.releaseProcessLeaseAfterUncertainFailure(closeLease);
1030
- }
1031
- }
1032
- };
1033
- /** Test-only hooks for ACPX runtime behavior that is otherwise private. */
1034
- const testing = {
1035
- appendCodexAcpConfigOverrides,
1036
- isClaudeAcpCommand,
1037
- isCodexAcpCommand
1038
- };
1039
- //#endregion
1040
- export { ACPX_BACKEND_ID, AcpxRuntime, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, testing };