@openclaw/acpx 2026.7.2-beta.5 → 2026.8.1-beta.2
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/dist/{config-schema-lrk5nlcV.js → config-schema-DN_uAi4R.js} +0 -2
- package/dist/doctor-contract-api.js +36 -4
- package/dist/index.js +21 -1248
- package/dist/pi-session-catalog-runtime-BiTQtT7s.js +1220 -0
- package/dist/pi-session-paths-DgYt8LUg.js +81 -0
- package/dist/{process-lease-CU1cFI4I.js → process-lease-Cwvj7WGe.js} +66 -2
- package/dist/{process-reaper-BIOtp_Ek.js → process-reaper-DzVuCxl3.js} +68 -62
- package/dist/{register.runtime-Dajn8myc.js → register.runtime-C29AY8LI.js} +75 -30
- package/dist/register.runtime.js +1 -1
- package/dist/{runtime-CN4JQkdV.js → runtime-BmzHUTK8.js} +329 -107
- package/dist/{service-DOsKw_R1.js → service-LTeZyc7q.js} +38 -45
- package/openclaw.plugin.json +5 -17
- package/package.json +7 -7
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as withAcpxLeaseEnvironment, i as createAcpxProcessLeaseId, o as hashAcpxProcessCommand, t as ACPX_PROBE_LEASE_SESSION_KEY, u as readAcpxProcessLeaseIdentity, v as splitCommandParts } from "./process-lease-Cwvj7WGe.js";
|
|
2
2
|
import { AcpRuntimeError } from "./runtime-api.js";
|
|
3
|
-
import {
|
|
3
|
+
import { d as OPENCLAW_CODEX_CONFIG_ARG, l as CODEX_ACP_PACKAGE, n as cleanupOpenClawOwnedAcpxProcessTree, r as isOpenClawLeaseAwareAcpxProcessCommand, t as cleanupOpenClawOwnedAcpxPendingLease } from "./process-reaper-DzVuCxl3.js";
|
|
4
4
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
5
5
|
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
-
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
7
|
-
import fs from "node:fs/promises";
|
|
8
6
|
import path, { resolve } from "node:path";
|
|
7
|
+
import fs from "node:fs/promises";
|
|
8
|
+
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
9
9
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
10
|
import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
|
|
11
11
|
import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
@@ -128,29 +128,42 @@ function createResetAwareSessionStore(baseStore, params) {
|
|
|
128
128
|
let recordToSave = record;
|
|
129
129
|
const launch = params?.launchScope?.getStore();
|
|
130
130
|
const sessionName = readSessionRecordName(record);
|
|
131
|
-
const rootPid = readRecordAgentPid(record);
|
|
132
131
|
const agentCommand = readRecordAgentCommand(record);
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
132
|
+
const leasedCommand = launch?.leasedCommand ?? agentCommand;
|
|
133
|
+
const leaseIdentity = launch ?? readAcpxProcessLeaseIdentity(leasedCommand);
|
|
134
|
+
if (params?.leaseStore && params.gatewayInstanceId && params.wrapperRoot && (!launch || sessionName === launch.sessionKey) && leasedCommand && leaseIdentity?.gatewayInstanceId === params.gatewayInstanceId && isOpenClawLeaseAwareAcpxProcessCommand({
|
|
135
|
+
command: leasedCommand,
|
|
136
|
+
wrapperRoot: params.wrapperRoot
|
|
137
|
+
})) {
|
|
138
|
+
const existing = await params.leaseStore.load(leaseIdentity.leaseId);
|
|
139
|
+
if (!existing || existing.gatewayInstanceId === leaseIdentity.gatewayInstanceId && existing.sessionKey === sessionName && existing.wrapperRoot === params.wrapperRoot) {
|
|
140
|
+
const lifecycleRecord = Boolean(launch && launch.resolvedCommand !== launch.leasedCommand) ? {
|
|
141
|
+
...record,
|
|
142
|
+
pid: void 0,
|
|
143
|
+
processId: void 0,
|
|
144
|
+
agentStartedAt: void 0
|
|
145
|
+
} : record;
|
|
146
|
+
const rootPid = readRecordAgentPid(lifecycleRecord);
|
|
147
|
+
if (rootPid) await params.leaseStore.save({
|
|
148
|
+
leaseId: leaseIdentity.leaseId,
|
|
149
|
+
gatewayInstanceId: leaseIdentity.gatewayInstanceId,
|
|
150
|
+
sessionKey: sessionName,
|
|
151
|
+
wrapperRoot: params.wrapperRoot,
|
|
152
|
+
wrapperPath: extractGeneratedWrapperPath(leasedCommand),
|
|
153
|
+
rootPid,
|
|
154
|
+
...existing?.rootPid === rootPid && existing.processGroupId ? { processGroupId: existing.processGroupId } : {},
|
|
155
|
+
commandHash: hashAcpxProcessCommand(leasedCommand),
|
|
156
|
+
startedAt: existing?.rootPid === rootPid ? existing.startedAt : Date.now(),
|
|
157
|
+
state: "open"
|
|
158
|
+
});
|
|
159
|
+
recordToSave = withOpenClawLeaseSessionMetadata({
|
|
160
|
+
...lifecycleRecord,
|
|
161
|
+
agentCommand: leasedCommand
|
|
162
|
+
}, {
|
|
163
|
+
openclawLeaseId: leaseIdentity.leaseId,
|
|
164
|
+
openclawGatewayInstanceId: leaseIdentity.gatewayInstanceId
|
|
165
|
+
});
|
|
166
|
+
}
|
|
154
167
|
}
|
|
155
168
|
await baseStore.save(recordToSave);
|
|
156
169
|
if (sessionName) freshSessionKeys.delete(sessionName);
|
|
@@ -362,6 +375,9 @@ function quoteShellArg(value) {
|
|
|
362
375
|
if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) return value;
|
|
363
376
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
364
377
|
}
|
|
378
|
+
function normalizeAgentCommand(command) {
|
|
379
|
+
return (Array.isArray(command) ? command.map((part) => quoteShellArg(part)).join(" ") : command).trim() || void 0;
|
|
380
|
+
}
|
|
365
381
|
function appendCodexAcpConfigOverrides(command, override) {
|
|
366
382
|
const config = {
|
|
367
383
|
...override.model ? { model: override.model } : {},
|
|
@@ -373,7 +389,7 @@ function appendCodexAcpConfigOverrides(command, override) {
|
|
|
373
389
|
function createModelScopedAgentRegistry(params) {
|
|
374
390
|
return {
|
|
375
391
|
resolve(agentName) {
|
|
376
|
-
const command = params.agentRegistry.resolve(agentName);
|
|
392
|
+
const command = normalizeAgentCommand(params.agentRegistry.resolve(agentName)) ?? "";
|
|
377
393
|
const override = params.scope.getStore();
|
|
378
394
|
if (!override || normalizeAgentName(agentName) !== CODEX_ACP_AGENT_ID || !isCodexAcpCommand(command)) return params.leaseCommand(command);
|
|
379
395
|
return params.leaseCommand(appendCodexAcpConfigOverrides(command, override));
|
|
@@ -386,8 +402,7 @@ function createModelScopedAgentRegistry(params) {
|
|
|
386
402
|
function resolveAgentCommand(params) {
|
|
387
403
|
const normalizedAgentName = normalizeAgentName(params.agentName);
|
|
388
404
|
if (!normalizedAgentName) return;
|
|
389
|
-
|
|
390
|
-
return typeof resolvedCommand === "string" ? resolvedCommand.trim() || void 0 : void 0;
|
|
405
|
+
return normalizeAgentCommand(params.agentRegistry.resolve(normalizedAgentName));
|
|
391
406
|
}
|
|
392
407
|
function shouldUseBridgeSafeDelegateForCommand(command) {
|
|
393
408
|
return isOpenClawBridgeCommand(command);
|
|
@@ -422,6 +437,10 @@ var AcpxRuntime = class {
|
|
|
422
437
|
this.codexAcpModelOverrideScope = new AsyncLocalStorage();
|
|
423
438
|
this.managedToolsSessionDelegates = /* @__PURE__ */ new Map();
|
|
424
439
|
this.launchLeaseScope = new AsyncLocalStorage();
|
|
440
|
+
this.ensureSessionTails = /* @__PURE__ */ new Map();
|
|
441
|
+
this.processLeaseTransitionTails = /* @__PURE__ */ new Map();
|
|
442
|
+
this.processLeaseOperationCounts = /* @__PURE__ */ new Map();
|
|
443
|
+
this.uncertainProcessLeaseIds = /* @__PURE__ */ new Set();
|
|
425
444
|
const { openclawProcessCleanup, ...delegateTestOptions } = testOptions ?? {};
|
|
426
445
|
this.processCleanupDeps = openclawProcessCleanup;
|
|
427
446
|
this.wrapperRoot = options.openclawWrapperRoot;
|
|
@@ -434,7 +453,8 @@ var AcpxRuntime = class {
|
|
|
434
453
|
this.sessionStore = createResetAwareSessionStore(options.sessionStore, {
|
|
435
454
|
gatewayInstanceId: this.gatewayInstanceId,
|
|
436
455
|
leaseStore: this.processLeaseStore,
|
|
437
|
-
launchScope: this.launchLeaseScope
|
|
456
|
+
launchScope: this.launchLeaseScope,
|
|
457
|
+
wrapperRoot: this.wrapperRoot
|
|
438
458
|
});
|
|
439
459
|
this.agentRegistry = options.agentRegistry;
|
|
440
460
|
this.scopedAgentRegistry = createModelScopedAgentRegistry({
|
|
@@ -458,6 +478,7 @@ var AcpxRuntime = class {
|
|
|
458
478
|
agentName: normalizeAgentName(options.probeAgent) ?? "codex",
|
|
459
479
|
agentRegistry: this.agentRegistry
|
|
460
480
|
});
|
|
481
|
+
this.probeCommand = probeCommand;
|
|
461
482
|
const useBridgeSafeProbe = this.managedToolsMcpBridgeEnabled || shouldUseBridgeSafeDelegateForCommand(probeCommand);
|
|
462
483
|
this.probeDelegate = useBridgeSafeProbe ? this.bridgeSafeDelegate : this.delegate;
|
|
463
484
|
}
|
|
@@ -489,77 +510,244 @@ var AcpxRuntime = class {
|
|
|
489
510
|
if (!normalizedSessionKey) return;
|
|
490
511
|
this.managedToolsSessionDelegates.delete(normalizedSessionKey);
|
|
491
512
|
}
|
|
492
|
-
async
|
|
513
|
+
async loadOperationSnapshotForHandle(handle) {
|
|
493
514
|
const record = await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey);
|
|
494
|
-
return
|
|
515
|
+
return {
|
|
516
|
+
record,
|
|
517
|
+
command: readAgentCommandFromRecord(record) ?? resolveAgentCommand({
|
|
518
|
+
agentName: readAgentFromHandle(handle),
|
|
519
|
+
agentRegistry: this.agentRegistry
|
|
520
|
+
})
|
|
521
|
+
};
|
|
495
522
|
}
|
|
496
|
-
|
|
497
|
-
const recordCommand = readAgentCommandFromRecord(record);
|
|
498
|
-
if (recordCommand) return this.resolveDelegateForSession({
|
|
499
|
-
command: recordCommand,
|
|
500
|
-
sessionKey: handle.sessionKey
|
|
501
|
-
});
|
|
502
|
-
const command = resolveAgentCommand({
|
|
503
|
-
agentName: readAgentFromHandle(handle),
|
|
504
|
-
agentRegistry: this.agentRegistry
|
|
505
|
-
});
|
|
523
|
+
resolveDelegateForOperationSnapshot(handle, snapshot) {
|
|
506
524
|
return this.resolveDelegateForSession({
|
|
507
|
-
command,
|
|
525
|
+
command: snapshot.command,
|
|
508
526
|
sessionKey: handle.sessionKey
|
|
509
527
|
});
|
|
510
528
|
}
|
|
511
|
-
async resolveCommandForHandle(handle) {
|
|
512
|
-
const recordCommand = readAgentCommandFromRecord(await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey));
|
|
513
|
-
if (recordCommand) return recordCommand;
|
|
514
|
-
return resolveAgentCommand({
|
|
515
|
-
agentName: readAgentFromHandle(handle),
|
|
516
|
-
agentRegistry: this.agentRegistry
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
529
|
commandWithLaunchLease(command) {
|
|
520
530
|
const launch = this.launchLeaseScope.getStore();
|
|
521
531
|
if (!launch) return command;
|
|
522
|
-
launch.stableCommand
|
|
532
|
+
if (command === launch.stableCommand) return launch.resolvedCommand;
|
|
523
533
|
return withAcpxLeaseEnvironment({
|
|
524
534
|
command,
|
|
525
535
|
leaseId: launch.leaseId,
|
|
526
536
|
gatewayInstanceId: launch.gatewayInstanceId
|
|
527
537
|
});
|
|
528
538
|
}
|
|
529
|
-
async
|
|
530
|
-
if (params.mode !== "persistent" || !params.command) return
|
|
539
|
+
async readReusablePersistentSessionCommand(params) {
|
|
540
|
+
if (params.mode !== "persistent" || !params.command) return;
|
|
531
541
|
const existing = await this.sessionStore.load(params.sessionKey);
|
|
532
|
-
if (!existing || readRecordResetOnNextEnsure(existing)) return
|
|
542
|
+
if (!existing || readRecordResetOnNextEnsure(existing)) return;
|
|
533
543
|
const recordCwd = readRecordCwd(existing);
|
|
534
|
-
if (!recordCwd || resolve(recordCwd) !== resolve(params.cwd?.trim() || this.cwd)) return
|
|
535
|
-
|
|
544
|
+
if (!recordCwd || resolve(recordCwd) !== resolve(params.cwd?.trim() || this.cwd)) return;
|
|
545
|
+
const recordCommand = readRecordAgentCommand(existing);
|
|
546
|
+
if (!recordCommand) return;
|
|
547
|
+
const leaseIdentity = readAcpxProcessLeaseIdentity(recordCommand);
|
|
548
|
+
if (leaseIdentity && leaseIdentity.gatewayInstanceId !== this.gatewayInstanceId) return;
|
|
549
|
+
if (recordCommand !== (leaseIdentity ? withAcpxLeaseEnvironment({
|
|
550
|
+
command: params.command,
|
|
551
|
+
leaseId: leaseIdentity.leaseId,
|
|
552
|
+
gatewayInstanceId: leaseIdentity.gatewayInstanceId
|
|
553
|
+
}) : params.command)) return;
|
|
536
554
|
const existingSessionId = typeof existing === "object" && existing !== null ? existing.acpSessionId : void 0;
|
|
537
|
-
return !params.resumeSessionId || existingSessionId === params.resumeSessionId;
|
|
555
|
+
return !params.resumeSessionId || existingSessionId === params.resumeSessionId ? recordCommand : void 0;
|
|
538
556
|
}
|
|
539
557
|
async runWithLaunchLease(params) {
|
|
540
|
-
if (
|
|
558
|
+
if (!params.command || !this.wrapperRoot || !this.gatewayInstanceId || !this.processLeaseStore || !isOpenClawLeaseAwareAcpxProcessCommand({
|
|
541
559
|
command: params.command,
|
|
542
560
|
wrapperRoot: this.wrapperRoot
|
|
543
561
|
})) return await params.run();
|
|
562
|
+
const processLeaseStore = this.processLeaseStore;
|
|
563
|
+
const reusableIdentity = readAcpxProcessLeaseIdentity(params.reusableCommand);
|
|
564
|
+
const canReuseLeaseIdentity = reusableIdentity?.gatewayInstanceId === this.gatewayInstanceId;
|
|
565
|
+
const leaseId = canReuseLeaseIdentity ? reusableIdentity.leaseId : params.finalizeCompletedProbe ? `probe-${hashAcpxProcessCommand(`${this.gatewayInstanceId}\0${extractGeneratedWrapperPath(params.command)}`)}` : createAcpxProcessLeaseId();
|
|
566
|
+
const leasedCommand = withAcpxLeaseEnvironment({
|
|
567
|
+
command: params.command,
|
|
568
|
+
leaseId,
|
|
569
|
+
gatewayInstanceId: this.gatewayInstanceId
|
|
570
|
+
});
|
|
544
571
|
const launch = {
|
|
545
|
-
leaseId
|
|
572
|
+
leaseId,
|
|
546
573
|
gatewayInstanceId: this.gatewayInstanceId,
|
|
547
574
|
sessionKey: params.sessionKey,
|
|
548
575
|
wrapperRoot: this.wrapperRoot,
|
|
549
|
-
stableCommand: params.command
|
|
576
|
+
stableCommand: params.command,
|
|
577
|
+
resolvedCommand: params.reusableCommand ?? leasedCommand,
|
|
578
|
+
leasedCommand
|
|
550
579
|
};
|
|
551
|
-
await this.
|
|
552
|
-
|
|
553
|
-
gatewayInstanceId
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
580
|
+
await this.retainProcessLeaseOperation(launch, async () => {
|
|
581
|
+
const reusableLease = canReuseLeaseIdentity ? await processLeaseStore.load(launch.leaseId) : void 0;
|
|
582
|
+
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`);
|
|
583
|
+
if (!(!reusableLease && (!params.reusableCommand || params.reusableCommand === leasedCommand))) return;
|
|
584
|
+
await processLeaseStore.save({
|
|
585
|
+
leaseId: launch.leaseId,
|
|
586
|
+
gatewayInstanceId: launch.gatewayInstanceId,
|
|
587
|
+
sessionKey: launch.sessionKey,
|
|
588
|
+
wrapperRoot: launch.wrapperRoot,
|
|
589
|
+
wrapperPath: extractGeneratedWrapperPath(leasedCommand),
|
|
590
|
+
rootPid: 0,
|
|
591
|
+
commandHash: hashAcpxProcessCommand(leasedCommand),
|
|
592
|
+
startedAt: Date.now(),
|
|
593
|
+
state: "open"
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
try {
|
|
597
|
+
const result = await this.launchLeaseScope.run(launch, params.run);
|
|
598
|
+
if (params.finalizeCompletedProbe) await this.finalizeCompletedProbeLease(launch);
|
|
599
|
+
else await this.finalizeProcessLeaseForSession(params.sessionKey, launch);
|
|
600
|
+
return result;
|
|
601
|
+
} catch (error) {
|
|
602
|
+
await this.releaseProcessLeaseAfterUncertainFailure(launch);
|
|
603
|
+
throw error;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async prepareProcessLeaseForOperation(handle, record) {
|
|
607
|
+
if (!this.processLeaseStore || !this.gatewayInstanceId || !this.wrapperRoot) return;
|
|
608
|
+
const processLeaseStore = this.processLeaseStore;
|
|
609
|
+
const wrapperRoot = this.wrapperRoot;
|
|
610
|
+
const recordPid = readRecordAgentPid(record);
|
|
611
|
+
const command = readAgentCommandFromRecord(record);
|
|
612
|
+
const identity = readAcpxProcessLeaseIdentity(command);
|
|
613
|
+
if (!command || !isOpenClawLeaseAwareAcpxProcessCommand({
|
|
614
|
+
command,
|
|
615
|
+
wrapperRoot
|
|
616
|
+
})) return;
|
|
617
|
+
if (!identity) return;
|
|
618
|
+
if (identity.gatewayInstanceId !== this.gatewayInstanceId) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another gateway`);
|
|
619
|
+
await this.retainProcessLeaseOperation(identity, async () => {
|
|
620
|
+
const existing = await processLeaseStore.load(identity.leaseId);
|
|
621
|
+
if (!existing) {
|
|
622
|
+
await processLeaseStore.save({
|
|
623
|
+
leaseId: identity.leaseId,
|
|
624
|
+
gatewayInstanceId: identity.gatewayInstanceId,
|
|
625
|
+
sessionKey: handle.sessionKey,
|
|
626
|
+
wrapperRoot,
|
|
627
|
+
wrapperPath: extractGeneratedWrapperPath(command),
|
|
628
|
+
rootPid: recordPid ?? 0,
|
|
629
|
+
commandHash: hashAcpxProcessCommand(command),
|
|
630
|
+
startedAt: Date.now(),
|
|
631
|
+
state: "open"
|
|
632
|
+
});
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (existing.gatewayInstanceId !== identity.gatewayInstanceId || existing.sessionKey !== handle.sessionKey || existing.wrapperRoot !== wrapperRoot) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another session`);
|
|
636
|
+
});
|
|
637
|
+
return identity;
|
|
638
|
+
}
|
|
639
|
+
async runSerializedProcessLeaseTransition(leaseId, run) {
|
|
640
|
+
const previous = this.processLeaseTransitionTails.get(leaseId) ?? Promise.resolve();
|
|
641
|
+
let release;
|
|
642
|
+
const current = new Promise((resolve) => {
|
|
643
|
+
release = resolve;
|
|
644
|
+
});
|
|
645
|
+
const tail = previous.then(() => current);
|
|
646
|
+
this.processLeaseTransitionTails.set(leaseId, tail);
|
|
647
|
+
await previous;
|
|
648
|
+
try {
|
|
649
|
+
return await run();
|
|
650
|
+
} finally {
|
|
651
|
+
release();
|
|
652
|
+
if (this.processLeaseTransitionTails.get(leaseId) === tail) this.processLeaseTransitionTails.delete(leaseId);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
async retainProcessLeaseOperation(identity, prepare) {
|
|
656
|
+
await this.runSerializedProcessLeaseTransition(identity.leaseId, async () => {
|
|
657
|
+
await prepare();
|
|
658
|
+
this.processLeaseOperationCounts.set(identity.leaseId, (this.processLeaseOperationCounts.get(identity.leaseId) ?? 0) + 1);
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async releaseProcessLeaseOperation(identity, finalize) {
|
|
662
|
+
if (!identity) return;
|
|
663
|
+
await this.runSerializedProcessLeaseTransition(identity.leaseId, async () => {
|
|
664
|
+
const count = this.processLeaseOperationCounts.get(identity.leaseId) ?? 0;
|
|
665
|
+
if (count > 1) {
|
|
666
|
+
this.processLeaseOperationCounts.set(identity.leaseId, count - 1);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (count === 0) return;
|
|
670
|
+
this.processLeaseOperationCounts.delete(identity.leaseId);
|
|
671
|
+
await finalize();
|
|
561
672
|
});
|
|
562
|
-
|
|
673
|
+
}
|
|
674
|
+
async finalizeProcessLeaseForOperation(handle, identity) {
|
|
675
|
+
await this.finalizeProcessLeaseForSession(handle.acpxRecordId ?? handle.sessionKey, identity);
|
|
676
|
+
}
|
|
677
|
+
async finalizeProcessLeaseForSession(sessionId, identity) {
|
|
678
|
+
if (!identity || !this.processLeaseStore) return;
|
|
679
|
+
const processLeaseStore = this.processLeaseStore;
|
|
680
|
+
await this.releaseProcessLeaseOperation(identity, async () => {
|
|
681
|
+
const lease = await processLeaseStore.load(identity.leaseId);
|
|
682
|
+
if (!lease || lease.gatewayInstanceId !== identity.gatewayInstanceId) return;
|
|
683
|
+
if (lease.rootPid <= 0) {
|
|
684
|
+
if (this.uncertainProcessLeaseIds.has(identity.leaseId)) return;
|
|
685
|
+
await processLeaseStore.markState(identity.leaseId, "lost");
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
this.uncertainProcessLeaseIds.delete(identity.leaseId);
|
|
689
|
+
try {
|
|
690
|
+
const recordIdentity = readAcpxProcessLeaseIdentity(readAgentCommandFromRecord(await this.sessionStore.load(sessionId)));
|
|
691
|
+
if (recordIdentity?.leaseId !== identity.leaseId || recordIdentity.gatewayInstanceId !== identity.gatewayInstanceId) await processLeaseStore.markState(identity.leaseId, "lost");
|
|
692
|
+
} catch {}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
async finalizeCompletedProbeLease(identity) {
|
|
696
|
+
if (!this.processLeaseStore) return;
|
|
697
|
+
const processLeaseStore = this.processLeaseStore;
|
|
698
|
+
await this.releaseProcessLeaseOperation(identity, async () => {
|
|
699
|
+
const lease = await processLeaseStore.load(identity.leaseId);
|
|
700
|
+
if (!lease || lease.gatewayInstanceId !== identity.gatewayInstanceId) return;
|
|
701
|
+
if (lease.rootPid > 0) return;
|
|
702
|
+
await cleanupOpenClawOwnedAcpxPendingLease({
|
|
703
|
+
leaseId: lease.leaseId,
|
|
704
|
+
gatewayInstanceId: lease.gatewayInstanceId,
|
|
705
|
+
wrapperRoot: lease.wrapperRoot,
|
|
706
|
+
wrapperPath: lease.wrapperPath,
|
|
707
|
+
deps: this.processCleanupDeps
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
async releaseProcessLeaseAfterUncertainFailure(identity) {
|
|
712
|
+
if (!identity || !this.processLeaseStore) return;
|
|
713
|
+
this.uncertainProcessLeaseIds.add(identity.leaseId);
|
|
714
|
+
const processLeaseStore = this.processLeaseStore;
|
|
715
|
+
await this.releaseProcessLeaseOperation(identity, async () => {
|
|
716
|
+
const lease = await processLeaseStore.load(identity.leaseId);
|
|
717
|
+
if (!lease || lease.gatewayInstanceId !== identity.gatewayInstanceId || lease.rootPid > 0) this.uncertainProcessLeaseIds.delete(identity.leaseId);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
async runWithProcessLeaseForHandle(handle, record, run) {
|
|
721
|
+
const identity = await this.prepareProcessLeaseForOperation(handle, record);
|
|
722
|
+
try {
|
|
723
|
+
return await run();
|
|
724
|
+
} finally {
|
|
725
|
+
await this.finalizeProcessLeaseForOperation(handle, identity);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
async finalizeProcessLeaseAfter(handle, identityPromise, resultPromise) {
|
|
729
|
+
try {
|
|
730
|
+
return await resultPromise;
|
|
731
|
+
} finally {
|
|
732
|
+
await this.finalizeProcessLeaseForOperation(handle, await identityPromise);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
async runSerializedSessionEnsure(sessionKey, run) {
|
|
736
|
+
const key = sessionKey.trim() || sessionKey;
|
|
737
|
+
const previous = this.ensureSessionTails.get(key) ?? Promise.resolve();
|
|
738
|
+
let release;
|
|
739
|
+
const current = new Promise((resolve) => {
|
|
740
|
+
release = resolve;
|
|
741
|
+
});
|
|
742
|
+
const tail = previous.then(() => current);
|
|
743
|
+
this.ensureSessionTails.set(key, tail);
|
|
744
|
+
await previous;
|
|
745
|
+
try {
|
|
746
|
+
return await run();
|
|
747
|
+
} finally {
|
|
748
|
+
release();
|
|
749
|
+
if (this.ensureSessionTails.get(key) === tail) this.ensureSessionTails.delete(key);
|
|
750
|
+
}
|
|
563
751
|
}
|
|
564
752
|
async withCodexWrapperDiagnostics(params) {
|
|
565
753
|
try {
|
|
@@ -602,7 +790,7 @@ var AcpxRuntime = class {
|
|
|
602
790
|
wrapperRoot: lease.wrapperRoot,
|
|
603
791
|
deps: this.processCleanupDeps
|
|
604
792
|
});
|
|
605
|
-
await this.processLeaseStore?.markState(lease.leaseId, result.terminatedPids.length > 0 || result.skippedReason === "missing-root" ? "closed" : "lost");
|
|
793
|
+
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");
|
|
606
794
|
return;
|
|
607
795
|
}
|
|
608
796
|
const rootCommand = readAgentCommandFromRecord(record) ?? resolveAgentCommand({
|
|
@@ -623,13 +811,26 @@ var AcpxRuntime = class {
|
|
|
623
811
|
isHealthy() {
|
|
624
812
|
return this.probeDelegate.isHealthy();
|
|
625
813
|
}
|
|
626
|
-
probeAvailability() {
|
|
627
|
-
|
|
814
|
+
async probeAvailability() {
|
|
815
|
+
await this.runWithLaunchLease({
|
|
816
|
+
sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
|
|
817
|
+
command: this.probeCommand,
|
|
818
|
+
finalizeCompletedProbe: true,
|
|
819
|
+
run: () => this.probeDelegate.probeAvailability()
|
|
820
|
+
});
|
|
628
821
|
}
|
|
629
|
-
doctor() {
|
|
630
|
-
return this.
|
|
822
|
+
async doctor() {
|
|
823
|
+
return await this.runWithLaunchLease({
|
|
824
|
+
sessionKey: ACPX_PROBE_LEASE_SESSION_KEY,
|
|
825
|
+
command: this.probeCommand,
|
|
826
|
+
finalizeCompletedProbe: true,
|
|
827
|
+
run: () => this.probeDelegate.doctor()
|
|
828
|
+
});
|
|
631
829
|
}
|
|
632
830
|
async ensureSession(input) {
|
|
831
|
+
return await this.runSerializedSessionEnsure(input.sessionKey, () => this.ensureSessionUnlocked(input));
|
|
832
|
+
}
|
|
833
|
+
async ensureSessionUnlocked(input) {
|
|
633
834
|
assertSupportedRuntimeSessionMode(input.mode);
|
|
634
835
|
const command = resolveAgentCommand({
|
|
635
836
|
agentName: input.agent,
|
|
@@ -655,7 +856,7 @@ var AcpxRuntime = class {
|
|
|
655
856
|
model: claudeModelOverride
|
|
656
857
|
} : input;
|
|
657
858
|
const stableLaunchCommand = codexModelOverride && command ? appendCodexAcpConfigOverrides(command, codexModelOverride) : command;
|
|
658
|
-
const
|
|
859
|
+
const reusableCommand = await this.readReusablePersistentSessionCommand({
|
|
659
860
|
sessionKey: input.sessionKey,
|
|
660
861
|
mode: input.mode,
|
|
661
862
|
cwd: input.cwd,
|
|
@@ -665,7 +866,7 @@ var AcpxRuntime = class {
|
|
|
665
866
|
const handle = !codexModelOverride ? await this.runWithLaunchLease({
|
|
666
867
|
sessionKey: ensureInput.sessionKey,
|
|
667
868
|
command: stableLaunchCommand,
|
|
668
|
-
|
|
869
|
+
reusableCommand,
|
|
669
870
|
run: () => this.withCodexWrapperDiagnostics({
|
|
670
871
|
command: stableLaunchCommand,
|
|
671
872
|
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
@@ -674,7 +875,7 @@ var AcpxRuntime = class {
|
|
|
674
875
|
}) : await this.runWithLaunchLease({
|
|
675
876
|
sessionKey: input.sessionKey,
|
|
676
877
|
command: stableLaunchCommand,
|
|
677
|
-
|
|
878
|
+
reusableCommand,
|
|
678
879
|
run: () => this.codexAcpModelOverrideScope.run(codexModelOverride, () => this.withCodexWrapperDiagnostics({
|
|
679
880
|
command: stableLaunchCommand,
|
|
680
881
|
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
@@ -687,9 +888,12 @@ var AcpxRuntime = class {
|
|
|
687
888
|
} : handle;
|
|
688
889
|
}
|
|
689
890
|
async *runTurn(input) {
|
|
690
|
-
const
|
|
691
|
-
const
|
|
891
|
+
const record = await this.sessionStore.load(input.handle.acpxRecordId ?? input.handle.sessionKey);
|
|
892
|
+
const turnLease = await this.prepareProcessLeaseForOperation(input.handle, record);
|
|
893
|
+
let command;
|
|
692
894
|
try {
|
|
895
|
+
command = (await this.loadOperationSnapshotForHandle(input.handle)).command;
|
|
896
|
+
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, await this.loadOperationSnapshotForHandle(input.handle));
|
|
693
897
|
for await (const event of delegate.runTurn(withOpenClawManagedTurnTimeout(input))) {
|
|
694
898
|
if (event.type !== "error" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(event.message)) {
|
|
695
899
|
yield event;
|
|
@@ -711,11 +915,17 @@ var AcpxRuntime = class {
|
|
|
711
915
|
const stderrTail = await this.readCodexTurnFailureStderr({ handle: input.handle });
|
|
712
916
|
if (!stderrTail) throw error;
|
|
713
917
|
throw new AcpRuntimeError("ACP_TURN_FAILED", `Internal error: ${stderrTail}`, { cause: error });
|
|
918
|
+
} finally {
|
|
919
|
+
await this.finalizeProcessLeaseForOperation(input.handle, turnLease);
|
|
714
920
|
}
|
|
715
921
|
}
|
|
716
922
|
startTurn(input) {
|
|
717
923
|
const readCodexTurnFailureStderr = () => this.readCodexTurnFailureStderr({ handle: input.handle });
|
|
718
|
-
const
|
|
924
|
+
const snapshotPromise = this.loadOperationSnapshotForHandle(input.handle);
|
|
925
|
+
const turnLeasePromise = snapshotPromise.then(({ record }) => this.prepareProcessLeaseForOperation(input.handle, record));
|
|
926
|
+
const turnPromise = Promise.all([snapshotPromise, turnLeasePromise]).then(async ([snapshot]) => {
|
|
927
|
+
const { command } = snapshot;
|
|
928
|
+
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
719
929
|
try {
|
|
720
930
|
return {
|
|
721
931
|
command,
|
|
@@ -756,7 +966,7 @@ var AcpxRuntime = class {
|
|
|
756
966
|
throw new AcpRuntimeError("ACP_TURN_FAILED", `Internal error: ${stderrTail}`, { cause: error });
|
|
757
967
|
}
|
|
758
968
|
} },
|
|
759
|
-
result: turnPromise.then(async ({ command, turn }) => {
|
|
969
|
+
result: this.finalizeProcessLeaseAfter(input.handle, turnLeasePromise, turnPromise.then(async ({ command, turn }) => {
|
|
760
970
|
try {
|
|
761
971
|
const result = await turn.result;
|
|
762
972
|
if (result.status !== "failed" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(result.error.message)) return result;
|
|
@@ -776,7 +986,7 @@ var AcpxRuntime = class {
|
|
|
776
986
|
if (!stderrTail) throw error;
|
|
777
987
|
throw new AcpRuntimeError("ACP_TURN_FAILED", `Internal error: ${stderrTail}`, { cause: error });
|
|
778
988
|
}
|
|
779
|
-
}),
|
|
989
|
+
})),
|
|
780
990
|
cancel(inputArgs) {
|
|
781
991
|
return turnPromise.then(({ turn }) => turn.cancel(inputArgs));
|
|
782
992
|
},
|
|
@@ -789,14 +999,20 @@ var AcpxRuntime = class {
|
|
|
789
999
|
return this.delegate.getCapabilities(input);
|
|
790
1000
|
}
|
|
791
1001
|
async getStatus(input) {
|
|
792
|
-
|
|
1002
|
+
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1003
|
+
return this.resolveDelegateForOperationSnapshot(input.handle, snapshot).getStatus(input);
|
|
793
1004
|
}
|
|
794
1005
|
async setMode(input) {
|
|
795
|
-
|
|
1006
|
+
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1007
|
+
await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).setMode(input));
|
|
796
1008
|
}
|
|
797
1009
|
async setConfigOption(input) {
|
|
798
|
-
const
|
|
799
|
-
|
|
1010
|
+
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1011
|
+
await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.setConfigOptionUnlocked(input, snapshot));
|
|
1012
|
+
}
|
|
1013
|
+
async setConfigOptionUnlocked(input, snapshot) {
|
|
1014
|
+
const { command } = snapshot;
|
|
1015
|
+
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
800
1016
|
const key = input.key.trim().toLowerCase();
|
|
801
1017
|
const isCodexAcp = isCodexAcpCommand(command);
|
|
802
1018
|
if (WIRE_TIMEOUT_CONFIG_KEYS.has(key) && (isCodexAcp || isClaudeAcpCommand(command))) return;
|
|
@@ -839,38 +1055,44 @@ var AcpxRuntime = class {
|
|
|
839
1055
|
await delegate.setConfigOption(input);
|
|
840
1056
|
}
|
|
841
1057
|
async cancel(input) {
|
|
842
|
-
const
|
|
843
|
-
await this.
|
|
1058
|
+
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1059
|
+
await this.resolveDelegateForOperationSnapshot(input.handle, snapshot).cancel(input);
|
|
844
1060
|
}
|
|
845
1061
|
async prepareFreshSession(input) {
|
|
846
1062
|
this.sessionStore.markFresh(input.sessionKey);
|
|
847
1063
|
}
|
|
848
1064
|
async close(input) {
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
1065
|
+
const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
|
|
1066
|
+
const closeLease = await this.prepareProcessLeaseForOperation(input.handle, snapshot.record);
|
|
1067
|
+
let cleanupSucceeded = false;
|
|
852
1068
|
try {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1069
|
+
const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
|
|
1070
|
+
let closeSucceeded;
|
|
1071
|
+
try {
|
|
1072
|
+
await delegate.close({
|
|
1073
|
+
handle: input.handle,
|
|
1074
|
+
reason: input.reason,
|
|
1075
|
+
discardPersistentState: input.discardPersistentState
|
|
1076
|
+
});
|
|
1077
|
+
closeSucceeded = true;
|
|
1078
|
+
} finally {
|
|
1079
|
+
await this.cleanupProcessTreeForRecord(input.handle, snapshot.record);
|
|
1080
|
+
cleanupSucceeded = true;
|
|
1081
|
+
}
|
|
1082
|
+
if (closeSucceeded) this.releaseManagedToolsDelegateForSession(input.handle.sessionKey);
|
|
1083
|
+
if (closeSucceeded && input.discardPersistentState) this.sessionStore.markFresh(input.handle.sessionKey);
|
|
859
1084
|
} finally {
|
|
860
|
-
await this.
|
|
1085
|
+
if (cleanupSucceeded) await this.finalizeProcessLeaseForOperation(input.handle, closeLease);
|
|
1086
|
+
else await this.releaseProcessLeaseAfterUncertainFailure(closeLease);
|
|
861
1087
|
}
|
|
862
|
-
if (closeSucceeded) this.releaseManagedToolsDelegateForSession(input.handle.sessionKey);
|
|
863
|
-
if (closeSucceeded && input.discardPersistentState) this.sessionStore.markFresh(input.handle.sessionKey);
|
|
864
1088
|
}
|
|
865
1089
|
};
|
|
866
1090
|
/** Test-only hooks for ACPX runtime behavior that is otherwise private. */
|
|
867
1091
|
const testing = {
|
|
868
1092
|
appendCodexAcpConfigOverrides,
|
|
869
|
-
assertSupportedRuntimeSessionMode,
|
|
870
|
-
classifyCodexAcpModelRequest,
|
|
871
1093
|
isClaudeAcpCommand,
|
|
872
1094
|
isCodexAcpCommand,
|
|
873
|
-
|
|
1095
|
+
normalizeAgentCommand
|
|
874
1096
|
};
|
|
875
1097
|
//#endregion
|
|
876
|
-
export { ACPX_BACKEND_ID, AcpxRuntime,
|
|
1098
|
+
export { ACPX_BACKEND_ID, AcpxRuntime, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, testing };
|