acpx 0.17.0 → 0.18.0
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/{agent-registry-DU6mhZBL.js → agent-registry-DdKB-zia.js} +20 -17
- package/dist/agent-registry-DdKB-zia.js.map +1 -0
- package/dist/agent-registry.d.ts.map +1 -1
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-CiyfEC7L.js → cli-8mVHeUXh.js} +5 -5
- package/dist/{cli-CiyfEC7L.js.map → cli-8mVHeUXh.js.map} +1 -1
- package/dist/cli.js +122 -94
- package/dist/cli.js.map +1 -1
- package/dist/{client-C6NUFqr-.d.ts → client-DeNKZZcu.d.ts} +19 -13
- package/dist/client-DeNKZZcu.d.ts.map +1 -0
- package/dist/{flows-CTOdIk-m.js → flows-WOQNzCMC.js} +5 -5
- package/dist/{flows-CTOdIk-m.js.map → flows-WOQNzCMC.js.map} +1 -1
- package/dist/flows.d.ts +1 -1
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-DbCcOAzM.js → invocation-options-B6d7TI-y.js} +7 -6
- package/dist/{invocation-options-DbCcOAzM.js.map → invocation-options-B6d7TI-y.js.map} +1 -1
- package/dist/{ipc-D3akHMEq.js → ipc-BJrvfNXr.js} +307 -90
- package/dist/ipc-BJrvfNXr.js.map +1 -0
- package/dist/{output-BPPjvJTI.js → output-CL18YmsG.js} +9 -6
- package/dist/output-CL18YmsG.js.map +1 -0
- package/dist/{queue-owner-runtime--IbbndGz.js → queue-owner-runtime-Cr0dWJuL.js} +1008 -463
- package/dist/queue-owner-runtime-Cr0dWJuL.js.map +1 -0
- package/dist/runtime.d.ts +129 -39
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +378 -176
- package/dist/runtime.js.map +1 -1
- package/dist/{session-watch--UX5dh00.js → session-watch-B8Q_TsGf.js} +4 -4
- package/dist/{session-watch--UX5dh00.js.map → session-watch-B8Q_TsGf.js.map} +1 -1
- package/dist/{watch-BbBxO-kb.js → watch-Dp73ph8h.js} +2 -2
- package/dist/{watch-BbBxO-kb.js.map → watch-Dp73ph8h.js.map} +1 -1
- package/package.json +3 -3
- package/skills/acpx/SKILL.md +27 -7
- package/dist/agent-registry-DU6mhZBL.js.map +0 -1
- package/dist/client-C6NUFqr-.d.ts.map +0 -1
- package/dist/ipc-D3akHMEq.js.map +0 -1
- package/dist/output-BPPjvJTI.js.map +0 -1
- package/dist/queue-owner-runtime--IbbndGz.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY } from "./agent-registry-
|
|
1
|
+
import { n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY } from "./agent-registry-DdKB-zia.js";
|
|
2
2
|
import { statSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import fs$1 from "node:fs/promises";
|
|
@@ -9,6 +9,7 @@ import { root } from "@openclaw/fs-safe";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { createHash, randomInt, randomUUID } from "node:crypto";
|
|
11
11
|
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
12
|
+
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
12
13
|
import net from "node:net";
|
|
13
14
|
//#region src/errors.ts
|
|
14
15
|
var AcpxOperationalError = class extends Error {
|
|
@@ -179,8 +180,8 @@ var QueueConnectionError = class extends AcpxOperationalError {};
|
|
|
179
180
|
var QueueProtocolError = class extends AcpxOperationalError {};
|
|
180
181
|
var PermissionDeniedError = class extends AcpxOperationalError {};
|
|
181
182
|
var PermissionPromptUnavailableError = class extends AcpxOperationalError {
|
|
182
|
-
constructor() {
|
|
183
|
-
super(
|
|
183
|
+
constructor(message = "Permission prompt unavailable in non-interactive mode") {
|
|
184
|
+
super(message);
|
|
184
185
|
}
|
|
185
186
|
};
|
|
186
187
|
//#endregion
|
|
@@ -310,6 +311,15 @@ function isProcessAlive(pid) {
|
|
|
310
311
|
return false;
|
|
311
312
|
}
|
|
312
313
|
}
|
|
314
|
+
function isProcessDefinitelyDead(pid) {
|
|
315
|
+
if (!pid || !Number.isSafeInteger(pid) || pid <= 0 || pid === process.pid) return false;
|
|
316
|
+
try {
|
|
317
|
+
process.kill(pid, 0);
|
|
318
|
+
return false;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return error.code === "ESRCH";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
313
323
|
//#endregion
|
|
314
324
|
//#region \0@oxc-project+runtime@0.149.0/helpers/esm/usingCtx.js
|
|
315
325
|
function _usingCtx() {
|
|
@@ -1871,11 +1881,12 @@ function closedAtOrLastUsedAt(record) {
|
|
|
1871
1881
|
return record.closedAt ?? record.lastUsedAt;
|
|
1872
1882
|
}
|
|
1873
1883
|
function isSessionStreamFile(fileName, safeId) {
|
|
1874
|
-
|
|
1884
|
+
const segmentPrefix = `${safeId}.stream.`;
|
|
1885
|
+
return fileName === `${safeId}.stream.ndjson` || fileName === `${safeId}.stream.lock` || fileName.startsWith(segmentPrefix) && /^\d+\.ndjson$/.test(fileName.slice(segmentPrefix.length));
|
|
1875
1886
|
}
|
|
1876
1887
|
async function pruneSessions(options = {}) {
|
|
1877
1888
|
await ensureSessionDir();
|
|
1878
|
-
const records = await loadPrunableRecords(
|
|
1889
|
+
const records = await loadPrunableRecords((await loadSessionIndexEntries()).filter((entry) => isPruneCandidate(entry, options.agentCommand)), options.before ?? (options.olderThanMs != null ? new Date(Date.now() - options.olderThanMs) : void 0), options.agentCommand);
|
|
1879
1890
|
if (options.dryRun) return {
|
|
1880
1891
|
pruned: records,
|
|
1881
1892
|
bytesFreed: 0,
|
|
@@ -1895,15 +1906,15 @@ async function pruneSessions(options = {}) {
|
|
|
1895
1906
|
dryRun: false
|
|
1896
1907
|
};
|
|
1897
1908
|
}
|
|
1898
|
-
function
|
|
1899
|
-
return
|
|
1909
|
+
function isPruneCandidate(record, agentCommand) {
|
|
1910
|
+
return record.closed === true && (!agentCommand || record.agentCommand === agentCommand);
|
|
1900
1911
|
}
|
|
1901
|
-
async function loadPrunableRecords(entries, cutoff) {
|
|
1912
|
+
async function loadPrunableRecords(entries, cutoff, agentCommand) {
|
|
1902
1913
|
const records = [];
|
|
1903
1914
|
const cutoffIso = cutoff?.toISOString();
|
|
1904
1915
|
for (const entry of entries) {
|
|
1905
1916
|
const record = await loadRecordFromIndexEntry(entry);
|
|
1906
|
-
if (record && isBeforeCutoff(record, cutoffIso)) records.push(record);
|
|
1917
|
+
if (record && isPruneCandidate(record, agentCommand) && isBeforeCutoff(record, cutoffIso)) records.push(record);
|
|
1907
1918
|
}
|
|
1908
1919
|
return records;
|
|
1909
1920
|
}
|
|
@@ -2124,6 +2135,156 @@ function queueSocketPath(sessionId, homeDir = os.homedir()) {
|
|
|
2124
2135
|
return path.join(queueSocketBaseDir(homeDir) ?? "/tmp", `${key}.sock`);
|
|
2125
2136
|
}
|
|
2126
2137
|
//#endregion
|
|
2138
|
+
//#region src/session/queue/lease-mutation.ts
|
|
2139
|
+
const GUARD_WAIT_MS = 2e3;
|
|
2140
|
+
const localMutations = /* @__PURE__ */ new Map();
|
|
2141
|
+
var QueueLeaseGuardSettlementError = class extends QueueConnectionError {
|
|
2142
|
+
constructor(errors) {
|
|
2143
|
+
super("Queue lease guard settlement failed; cleanup remains retryable", {
|
|
2144
|
+
detailCode: "QUEUE_LEASE_GUARD_SETTLEMENT_FAILED",
|
|
2145
|
+
origin: "queue",
|
|
2146
|
+
retryable: false,
|
|
2147
|
+
cause: new AggregateError(errors, "Queue lease mutation and settlement errors")
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
async function canonicalLeasePath(sessionId) {
|
|
2152
|
+
const requested = queueLockFilePath(sessionId);
|
|
2153
|
+
try {
|
|
2154
|
+
return path.join(await fs$1.realpath(path.dirname(requested)), path.basename(requested));
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
if (error.code === "ENOENT") return path.resolve(requested);
|
|
2157
|
+
throw error;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
function guardOwnerHasExited({ payload }) {
|
|
2161
|
+
return payload !== null && typeof payload === "object" && "pid" in payload && typeof payload.pid === "number" && isProcessDefinitelyDead(payload.pid);
|
|
2162
|
+
}
|
|
2163
|
+
async function acquireGuard(lockPath) {
|
|
2164
|
+
return await acquireFileLock(lockPath, {
|
|
2165
|
+
managerKey: "acpx.queue-lease-mutation",
|
|
2166
|
+
lockPath: `${lockPath}.guard`,
|
|
2167
|
+
staleMs: Infinity,
|
|
2168
|
+
timeoutMs: GUARD_WAIT_MS,
|
|
2169
|
+
retry: {
|
|
2170
|
+
minTimeout: 5,
|
|
2171
|
+
maxTimeout: 15,
|
|
2172
|
+
factor: 1,
|
|
2173
|
+
randomize: false
|
|
2174
|
+
},
|
|
2175
|
+
staleRecovery: "remove-if-unchanged",
|
|
2176
|
+
payload: () => ({ pid: process.pid }),
|
|
2177
|
+
shouldReclaim: guardOwnerHasExited,
|
|
2178
|
+
shouldRemoveStaleLock: guardOwnerHasExited
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
function matchesReservation(value, expected) {
|
|
2182
|
+
if (!value || typeof value !== "object") return false;
|
|
2183
|
+
const record = value;
|
|
2184
|
+
return record.pid === expected.pid && record.sessionId === expected.sessionId && record.ownerGeneration === expected.ownerGeneration;
|
|
2185
|
+
}
|
|
2186
|
+
async function rollbackReservation(lockPath, expected) {
|
|
2187
|
+
try {
|
|
2188
|
+
if (!(await fs$1.lstat(lockPath)).isFile()) return;
|
|
2189
|
+
const raw = await fs$1.readFile(lockPath, "utf8");
|
|
2190
|
+
let value;
|
|
2191
|
+
try {
|
|
2192
|
+
value = JSON.parse(raw);
|
|
2193
|
+
} catch {
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
if (matchesReservation(value, expected)) await fs$1.unlink(lockPath);
|
|
2197
|
+
} catch (error) {
|
|
2198
|
+
if (error.code !== "ENOENT") throw error;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
async function settleReceipt(lockPath, receipt) {
|
|
2202
|
+
if (receipt.reservation) {
|
|
2203
|
+
if (!await receipt.guard.verifyStillHeld()) {
|
|
2204
|
+
await receipt.guard.release();
|
|
2205
|
+
receipt.guard = await acquireGuard(lockPath);
|
|
2206
|
+
}
|
|
2207
|
+
await rollbackReservation(lockPath, receipt.reservation);
|
|
2208
|
+
receipt.reservation = void 0;
|
|
2209
|
+
}
|
|
2210
|
+
await receipt.guard.release();
|
|
2211
|
+
}
|
|
2212
|
+
async function settleFailedGuard(lockPath, state) {
|
|
2213
|
+
const failed = state.failed;
|
|
2214
|
+
if (!failed) return;
|
|
2215
|
+
try {
|
|
2216
|
+
await settleReceipt(lockPath, failed);
|
|
2217
|
+
if (state.failed === failed) state.failed = void 0;
|
|
2218
|
+
} catch (error) {
|
|
2219
|
+
throw new QueueLeaseGuardSettlementError([error]);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
async function locallySerialized(lockPath, run) {
|
|
2223
|
+
let state = localMutations.get(lockPath);
|
|
2224
|
+
if (!state) {
|
|
2225
|
+
state = { tail: Promise.resolve() };
|
|
2226
|
+
localMutations.set(lockPath, state);
|
|
2227
|
+
}
|
|
2228
|
+
const current = state;
|
|
2229
|
+
const operation = current.tail.then(async () => await run(current));
|
|
2230
|
+
const tail = operation.then(() => {}, () => {});
|
|
2231
|
+
current.tail = tail;
|
|
2232
|
+
try {
|
|
2233
|
+
return await operation;
|
|
2234
|
+
} finally {
|
|
2235
|
+
if (current.tail === tail && !current.failed && localMutations.get(lockPath) === current) localMutations.delete(lockPath);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
async function settlePendingQueueLeaseGuard(sessionId) {
|
|
2239
|
+
if (localMutations.size === 0) return;
|
|
2240
|
+
const lockPath = await canonicalLeasePath(sessionId);
|
|
2241
|
+
if (!localMutations.has(lockPath)) return;
|
|
2242
|
+
await locallySerialized(lockPath, async (state) => await settleFailedGuard(lockPath, state));
|
|
2243
|
+
}
|
|
2244
|
+
async function withQueueLeaseMutation(sessionId, mutate, reservation) {
|
|
2245
|
+
await fs$1.mkdir(path.dirname(queueLockFilePath(sessionId)), {
|
|
2246
|
+
recursive: true,
|
|
2247
|
+
mode: 448
|
|
2248
|
+
});
|
|
2249
|
+
const lockPath = await canonicalLeasePath(sessionId);
|
|
2250
|
+
const expected = reservation ? {
|
|
2251
|
+
pid: reservation.pid,
|
|
2252
|
+
sessionId: reservation.sessionId,
|
|
2253
|
+
ownerGeneration: reservation.ownerGeneration
|
|
2254
|
+
} : void 0;
|
|
2255
|
+
const publishedReservation = () => reservation?.published ? expected : void 0;
|
|
2256
|
+
return await locallySerialized(lockPath, async (state) => {
|
|
2257
|
+
await settleFailedGuard(lockPath, state);
|
|
2258
|
+
const guard = await acquireGuard(lockPath);
|
|
2259
|
+
let outcome;
|
|
2260
|
+
try {
|
|
2261
|
+
outcome = {
|
|
2262
|
+
ok: true,
|
|
2263
|
+
value: await mutate()
|
|
2264
|
+
};
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
outcome = {
|
|
2267
|
+
ok: false,
|
|
2268
|
+
error
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
const published = publishedReservation();
|
|
2272
|
+
const receipt = {
|
|
2273
|
+
guard,
|
|
2274
|
+
reservation: outcome.ok ? void 0 : published
|
|
2275
|
+
};
|
|
2276
|
+
try {
|
|
2277
|
+
await settleReceipt(lockPath, receipt);
|
|
2278
|
+
} catch (error) {
|
|
2279
|
+
if (outcome.ok) receipt.reservation = published;
|
|
2280
|
+
state.failed = receipt;
|
|
2281
|
+
throw new QueueLeaseGuardSettlementError(outcome.ok ? [error] : [outcome.error, error]);
|
|
2282
|
+
}
|
|
2283
|
+
if (!outcome.ok) throw outcome.error;
|
|
2284
|
+
return outcome.value;
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
//#endregion
|
|
2127
2288
|
//#region src/session/queue/lease-store.ts
|
|
2128
2289
|
const PROCESS_SIGTERM_GRACE_MS = process.platform === "win32" ? 4e3 : 12e3;
|
|
2129
2290
|
const PROCESS_SIGKILL_GRACE_MS = 1500;
|
|
@@ -2141,12 +2302,18 @@ function parseQueueOwnerRecord(raw) {
|
|
|
2141
2302
|
heartbeatAt: record.heartbeatAt,
|
|
2142
2303
|
ownerGeneration: record.ownerGeneration,
|
|
2143
2304
|
queueDepth: record.queueDepth,
|
|
2144
|
-
...record
|
|
2145
|
-
...record.sessionWatch === true ? { sessionWatch: true } : {},
|
|
2305
|
+
...parseQueueOwnerCapabilities(record),
|
|
2146
2306
|
...typeof record.mcpConfigPath === "string" ? { mcpConfigPath: record.mcpConfigPath } : {},
|
|
2147
2307
|
...typeof record.mcpConfigFingerprint === "string" ? { mcpConfigFingerprint: record.mcpConfigFingerprint } : {}
|
|
2148
2308
|
};
|
|
2149
2309
|
}
|
|
2310
|
+
function parseQueueOwnerCapabilities(record) {
|
|
2311
|
+
return {
|
|
2312
|
+
...record.sharedRuntime === true ? { sharedRuntime: true } : {},
|
|
2313
|
+
...record.sessionWatch === true ? { sessionWatch: true } : {},
|
|
2314
|
+
...record.persistsControlState === true ? { persistsControlState: true } : {}
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2150
2317
|
function hasValidQueueOwnerRecordFields(record) {
|
|
2151
2318
|
return !Array.isArray(record) && isPositiveInteger(record.pid) && typeof record.sessionId === "string" && typeof record.socketPath === "string" && typeof record.createdAt === "string" && typeof record.heartbeatAt === "string" && isPositiveInteger(record.ownerGeneration) && isNonNegativeInteger(record.queueDepth);
|
|
2152
2319
|
}
|
|
@@ -2199,15 +2366,20 @@ async function removeSocketFile(socketPath) {
|
|
|
2199
2366
|
if (error.code !== "ENOENT") throw error;
|
|
2200
2367
|
}
|
|
2201
2368
|
}
|
|
2202
|
-
async function waitForProcessExit(pid, timeoutMs) {
|
|
2369
|
+
async function waitForProcessExit(pid, timeoutMs, hasExited) {
|
|
2203
2370
|
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
2204
2371
|
while (Date.now() <= deadline) {
|
|
2205
|
-
if (
|
|
2372
|
+
if (hasExited(pid)) return true;
|
|
2206
2373
|
await waitMs(PROCESS_POLL_MS);
|
|
2207
2374
|
}
|
|
2208
|
-
return
|
|
2375
|
+
return hasExited(pid);
|
|
2209
2376
|
}
|
|
2210
2377
|
async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent) {
|
|
2378
|
+
await withQueueLeaseMutation(sessionId, async () => {
|
|
2379
|
+
await cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent);
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
async function cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent) {
|
|
2211
2383
|
if (!await isCurrent()) return;
|
|
2212
2384
|
await removeSocketFile(socketPath).catch(() => {});
|
|
2213
2385
|
if (!await isCurrent()) return;
|
|
@@ -2217,7 +2389,10 @@ async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent) {
|
|
|
2217
2389
|
}
|
|
2218
2390
|
async function ownsQueueLease(owner, requireStale = false) {
|
|
2219
2391
|
const current = await readQueueOwnerRecord(owner.sessionId);
|
|
2220
|
-
return current
|
|
2392
|
+
return matchesQueueOwner(current, owner) && (!requireStale || isQueueOwnerHeartbeatStale(current));
|
|
2393
|
+
}
|
|
2394
|
+
function matchesQueueOwner(current, expected) {
|
|
2395
|
+
return current?.sessionId === expected.sessionId && current.pid === expected.pid && current.ownerGeneration === expected.ownerGeneration;
|
|
2221
2396
|
}
|
|
2222
2397
|
function ownerIsAlive(owner) {
|
|
2223
2398
|
return owner.pid === process.pid || isProcessAlive(owner.pid);
|
|
@@ -2234,37 +2409,34 @@ async function readQueueOwnerRecord(sessionId) {
|
|
|
2234
2409
|
}
|
|
2235
2410
|
async function terminateProcess(pid, beforeSignal) {
|
|
2236
2411
|
if (!isProcessAlive(pid)) return false;
|
|
2237
|
-
|
|
2412
|
+
return await terminateWithDispatch(pid, async (signal) => {
|
|
2238
2413
|
if (beforeSignal && !await beforeSignal(signal)) return false;
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2414
|
+
return dispatchSignal(pid, signal);
|
|
2415
|
+
}, (target) => !isProcessAlive(target));
|
|
2416
|
+
}
|
|
2417
|
+
function dispatchSignal(pid, signal) {
|
|
2418
|
+
try {
|
|
2419
|
+
process.kill(pid, signal);
|
|
2420
|
+
return true;
|
|
2421
|
+
} catch {
|
|
2422
|
+
return false;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
async function terminateWithDispatch(pid, dispatch, hasExited) {
|
|
2426
|
+
for (const [signal, graceMs] of [["SIGTERM", PROCESS_SIGTERM_GRACE_MS], ["SIGKILL", PROCESS_SIGKILL_GRACE_MS]]) {
|
|
2427
|
+
if (!await dispatch(signal)) return false;
|
|
2428
|
+
if (await waitForProcessExit(pid, graceMs, hasExited)) return true;
|
|
2245
2429
|
}
|
|
2246
2430
|
return false;
|
|
2247
2431
|
}
|
|
2248
2432
|
async function resolveUsableQueueOwner(sessionId, owner) {
|
|
2249
|
-
|
|
2250
|
-
await terminateQueueOwnerForSession(sessionId, owner, true);
|
|
2251
|
-
const current = await readQueueOwnerRecord(sessionId);
|
|
2252
|
-
return current?.pid === owner.pid && current.ownerGeneration === owner.ownerGeneration && ownerIsAlive(current) && !isQueueOwnerHeartbeatStale(current) ? current : void 0;
|
|
2253
|
-
}
|
|
2254
|
-
async function readQueueOwnerStatus(sessionId) {
|
|
2433
|
+
await settlePendingQueueLeaseGuard(sessionId);
|
|
2255
2434
|
const observed = await readQueueOwnerRecord(sessionId);
|
|
2256
|
-
if (!observed) return;
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
socketPath: owner.socketPath,
|
|
2262
|
-
heartbeatAt: owner.heartbeatAt,
|
|
2263
|
-
ownerGeneration: owner.ownerGeneration,
|
|
2264
|
-
queueDepth: owner.queueDepth,
|
|
2265
|
-
alive: true,
|
|
2266
|
-
stale: isQueueOwnerHeartbeatStale(owner)
|
|
2267
|
-
};
|
|
2435
|
+
if (!matchesQueueOwner(observed, owner)) return;
|
|
2436
|
+
if (ownerIsAlive(observed) && !isQueueOwnerHeartbeatStale(observed)) return observed;
|
|
2437
|
+
await terminateQueueOwnerForSession(sessionId, observed, true);
|
|
2438
|
+
const current = await readQueueOwnerRecord(sessionId);
|
|
2439
|
+
return matchesQueueOwner(current, observed) && ownerIsAlive(current) && !isQueueOwnerHeartbeatStale(current) ? current : void 0;
|
|
2268
2440
|
}
|
|
2269
2441
|
async function tryAcquireQueueOwnerLease(sessionId, mcpConfigOrNowIsoFactory, nowIsoFactory = nowIso) {
|
|
2270
2442
|
const { mcpConfigPath, clock } = resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory);
|
|
@@ -2285,21 +2457,30 @@ async function tryAcquireQueueOwnerLease(sessionId, mcpConfigOrNowIsoFactory, no
|
|
|
2285
2457
|
updates: Promise.resolve(),
|
|
2286
2458
|
released: false
|
|
2287
2459
|
};
|
|
2460
|
+
const reservation = {
|
|
2461
|
+
pid: lease.pid,
|
|
2462
|
+
sessionId,
|
|
2463
|
+
ownerGeneration,
|
|
2464
|
+
published: false
|
|
2465
|
+
};
|
|
2288
2466
|
try {
|
|
2289
|
-
await
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2467
|
+
return await withQueueLeaseMutation(sessionId, async () => {
|
|
2468
|
+
await stageQueueOwnerRecord(lease, 0, clock, async (tempPath, payload) => {
|
|
2469
|
+
try {
|
|
2470
|
+
await fs$1.link(tempPath, lockPath);
|
|
2471
|
+
} catch (error) {
|
|
2472
|
+
if (!isHardlinkFallbackError(error)) throw error;
|
|
2473
|
+
await fs$1.writeFile(lockPath, payload, {
|
|
2474
|
+
encoding: "utf8",
|
|
2475
|
+
flag: "wx",
|
|
2476
|
+
mode: 384
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
reservation.published = true;
|
|
2480
|
+
});
|
|
2481
|
+
await removeSocketFile(socketPath).catch(() => {});
|
|
2482
|
+
return lease;
|
|
2483
|
+
}, reservation);
|
|
2303
2484
|
} catch (error) {
|
|
2304
2485
|
return await handleLeaseCollision(sessionId, error);
|
|
2305
2486
|
}
|
|
@@ -2324,18 +2505,20 @@ async function handleLeaseCollision(sessionId, error) {
|
|
|
2324
2505
|
}
|
|
2325
2506
|
async function cleanupAbandonedReservation(sessionId) {
|
|
2326
2507
|
const lockPath = queueLockFilePath(sessionId);
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2508
|
+
await withQueueLeaseMutation(sessionId, async () => {
|
|
2509
|
+
try {
|
|
2510
|
+
const observed = await fs$1.lstat(lockPath, { bigint: true });
|
|
2511
|
+
if (!observed.isFile() || Date.now() - Number(observed.mtimeMs) <= QUEUE_OWNER_STALE_HEARTBEAT_MS) return;
|
|
2512
|
+
const raw = await fs$1.readFile(lockPath, "utf8");
|
|
2513
|
+
if (await readQueueOwnerRecord(sessionId)) return;
|
|
2514
|
+
await cleanupGuardedQueueOwnerFiles(sessionId, queueSocketPath(sessionId), async () => {
|
|
2515
|
+
const current = await fs$1.lstat(lockPath, { bigint: true });
|
|
2516
|
+
return current.isFile() && current.dev === observed.dev && current.ino === observed.ino && current.mtimeNs === observed.mtimeNs && await fs$1.readFile(lockPath, "utf8") === raw;
|
|
2517
|
+
});
|
|
2518
|
+
} catch (error) {
|
|
2519
|
+
if (error.code !== "ENOENT") throw error;
|
|
2520
|
+
}
|
|
2521
|
+
});
|
|
2339
2522
|
}
|
|
2340
2523
|
function resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory) {
|
|
2341
2524
|
if (typeof mcpConfigOrNowIsoFactory === "string") return {
|
|
@@ -2358,8 +2541,11 @@ function resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory) {
|
|
|
2358
2541
|
function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso) {
|
|
2359
2542
|
if (lease.released) return Promise.resolve();
|
|
2360
2543
|
const update = lease.updates.then(async () => {
|
|
2361
|
-
await
|
|
2362
|
-
if (await ownsQueueLease(lease))
|
|
2544
|
+
await withQueueLeaseMutation(lease.sessionId, async () => {
|
|
2545
|
+
if (!await ownsQueueLease(lease)) return;
|
|
2546
|
+
await stageQueueOwnerRecord(lease, options.queueDepth, nowIsoFactory, async (tempPath) => {
|
|
2547
|
+
await fs$1.rename(tempPath, lease.lockPath);
|
|
2548
|
+
});
|
|
2363
2549
|
});
|
|
2364
2550
|
});
|
|
2365
2551
|
lease.updates = update.catch(() => {});
|
|
@@ -2376,6 +2562,7 @@ async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
|
|
|
2376
2562
|
queueDepth: Math.max(0, Math.round(queueDepth)),
|
|
2377
2563
|
sharedRuntime: true,
|
|
2378
2564
|
sessionWatch: true,
|
|
2565
|
+
persistsControlState: true,
|
|
2379
2566
|
...lease.mcpConfigPath ? { mcpConfigPath: lease.mcpConfigPath } : {},
|
|
2380
2567
|
...lease.mcpConfigFingerprint ? { mcpConfigFingerprint: lease.mcpConfigFingerprint } : {}
|
|
2381
2568
|
}, null, 2);
|
|
@@ -2398,11 +2585,15 @@ async function releaseQueueOwnerLease(lease) {
|
|
|
2398
2585
|
await cleanupQueueOwnerFiles(lease.sessionId, lease.socketPath, () => ownsQueueLease(lease));
|
|
2399
2586
|
}
|
|
2400
2587
|
async function terminateQueueOwnerForSession(sessionId, expectedOwner, requireStale = false) {
|
|
2588
|
+
await settlePendingQueueLeaseGuard(sessionId);
|
|
2401
2589
|
const owner = expectedOwner ?? await readQueueOwnerRecord(sessionId);
|
|
2402
2590
|
if (!owner || owner.sessionId !== sessionId) return;
|
|
2403
|
-
if (
|
|
2404
|
-
|
|
2405
|
-
|
|
2591
|
+
if (owner.pid !== process.pid && isProcessAlive(owner.pid)) await terminateWithDispatch(owner.pid, (signal) => withQueueLeaseMutation(sessionId, async () => {
|
|
2592
|
+
if (!await ownsQueueLease(owner, requireStale && signal === "SIGTERM")) return false;
|
|
2593
|
+
return dispatchSignal(owner.pid, signal);
|
|
2594
|
+
}), isProcessDefinitelyDead);
|
|
2595
|
+
if (!isProcessDefinitelyDead(owner.pid)) return;
|
|
2596
|
+
await cleanupQueueOwnerFiles(sessionId, owner.socketPath, async () => isProcessDefinitelyDead(owner.pid) && await ownsQueueLease(owner));
|
|
2406
2597
|
}
|
|
2407
2598
|
async function waitMs(ms) {
|
|
2408
2599
|
await new Promise((resolve) => {
|
|
@@ -2456,7 +2647,12 @@ async function connectToQueueOwner(owner, maxAttempts = QUEUE_CONNECT_ATTEMPTS)
|
|
|
2456
2647
|
}
|
|
2457
2648
|
//#endregion
|
|
2458
2649
|
//#region src/session/queue/ipc-health.ts
|
|
2650
|
+
async function isStillCurrent(owner) {
|
|
2651
|
+
const current = await readQueueOwnerRecord(owner.sessionId);
|
|
2652
|
+
return current?.pid === owner.pid && current.ownerGeneration === owner.ownerGeneration;
|
|
2653
|
+
}
|
|
2459
2654
|
async function probeQueueOwnerHealth(sessionId) {
|
|
2655
|
+
await settlePendingQueueLeaseGuard(sessionId);
|
|
2460
2656
|
const ownerRecord = await readQueueOwnerRecord(sessionId);
|
|
2461
2657
|
if (!ownerRecord) return {
|
|
2462
2658
|
sessionId,
|
|
@@ -2465,7 +2661,7 @@ async function probeQueueOwnerHealth(sessionId) {
|
|
|
2465
2661
|
socketReachable: false,
|
|
2466
2662
|
pidAlive: false
|
|
2467
2663
|
};
|
|
2468
|
-
const owner = await
|
|
2664
|
+
const owner = await resolveUsableQueueOwner(sessionId, ownerRecord);
|
|
2469
2665
|
if (!owner) return {
|
|
2470
2666
|
sessionId,
|
|
2471
2667
|
hasLease: false,
|
|
@@ -2473,10 +2669,9 @@ async function probeQueueOwnerHealth(sessionId) {
|
|
|
2473
2669
|
socketReachable: false,
|
|
2474
2670
|
pidAlive: false
|
|
2475
2671
|
};
|
|
2476
|
-
const pidAlive = owner.alive;
|
|
2477
2672
|
let socketReachable = false;
|
|
2478
2673
|
try {
|
|
2479
|
-
const socket = await connectToQueueOwner(
|
|
2674
|
+
const socket = await connectToQueueOwner(owner, 2);
|
|
2480
2675
|
if (socket) {
|
|
2481
2676
|
socketReachable = true;
|
|
2482
2677
|
if (!socket.destroyed) socket.end();
|
|
@@ -2484,12 +2679,19 @@ async function probeQueueOwnerHealth(sessionId) {
|
|
|
2484
2679
|
} catch {
|
|
2485
2680
|
socketReachable = false;
|
|
2486
2681
|
}
|
|
2682
|
+
if (!await isStillCurrent(owner)) return {
|
|
2683
|
+
sessionId,
|
|
2684
|
+
hasLease: false,
|
|
2685
|
+
healthy: false,
|
|
2686
|
+
socketReachable: false,
|
|
2687
|
+
pidAlive: false
|
|
2688
|
+
};
|
|
2487
2689
|
return {
|
|
2488
2690
|
sessionId,
|
|
2489
2691
|
hasLease: true,
|
|
2490
2692
|
healthy: socketReachable,
|
|
2491
2693
|
socketReachable,
|
|
2492
|
-
pidAlive,
|
|
2694
|
+
pidAlive: true,
|
|
2493
2695
|
pid: owner.pid,
|
|
2494
2696
|
socketPath: owner.socketPath,
|
|
2495
2697
|
ownerGeneration: owner.ownerGeneration,
|
|
@@ -3051,6 +3253,12 @@ async function runQueueOwnerRequest(options) {
|
|
|
3051
3253
|
onAbort();
|
|
3052
3254
|
return;
|
|
3053
3255
|
}
|
|
3256
|
+
try {
|
|
3257
|
+
options.assertDispatch?.();
|
|
3258
|
+
} catch (error) {
|
|
3259
|
+
finishReject(error);
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3054
3262
|
try {
|
|
3055
3263
|
socket.write(`${requestLine}\n`);
|
|
3056
3264
|
} catch (error) {
|
|
@@ -3164,10 +3372,11 @@ async function submitToQueueOwner(owner, options) {
|
|
|
3164
3372
|
}
|
|
3165
3373
|
});
|
|
3166
3374
|
}
|
|
3167
|
-
async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
3375
|
+
async function submitControlToQueueOwner(owner, request, isExpectedResponse, assertDispatch) {
|
|
3168
3376
|
return await runQueueOwnerRequest({
|
|
3169
3377
|
owner,
|
|
3170
3378
|
request,
|
|
3379
|
+
assertDispatch,
|
|
3171
3380
|
onMessage: (message, { state, resolve, reject }) => {
|
|
3172
3381
|
if (message.type === "error") {
|
|
3173
3382
|
reject(queueConnectionErrorFromOwner(message, false));
|
|
@@ -3209,25 +3418,25 @@ async function submitCancelToQueueOwner(owner, targetRequestId) {
|
|
|
3209
3418
|
...targetRequestId !== void 0 ? { targetRequestId } : {}
|
|
3210
3419
|
}, (message) => message.type === "cancel_result"))?.cancelled;
|
|
3211
3420
|
}
|
|
3212
|
-
async function submitSetModeToQueueOwner(owner, modeId, timeoutMs) {
|
|
3421
|
+
async function submitSetModeToQueueOwner(owner, modeId, timeoutMs, assertDispatch) {
|
|
3213
3422
|
return await submitControlToQueueOwner(owner, {
|
|
3214
3423
|
type: "set_mode",
|
|
3215
3424
|
requestId: randomUUID(),
|
|
3216
3425
|
ownerGeneration: owner.ownerGeneration,
|
|
3217
3426
|
modeId,
|
|
3218
3427
|
timeoutMs
|
|
3219
|
-
}, (message) => message.type === "set_mode_result") ? true : void 0;
|
|
3428
|
+
}, (message) => message.type === "set_mode_result", assertDispatch) ? true : void 0;
|
|
3220
3429
|
}
|
|
3221
|
-
async function submitSetModelToQueueOwner(owner, modelId, timeoutMs) {
|
|
3430
|
+
async function submitSetModelToQueueOwner(owner, modelId, timeoutMs, assertDispatch) {
|
|
3222
3431
|
return await submitControlToQueueOwner(owner, {
|
|
3223
3432
|
type: "set_model",
|
|
3224
3433
|
requestId: randomUUID(),
|
|
3225
3434
|
ownerGeneration: owner.ownerGeneration,
|
|
3226
3435
|
modelId,
|
|
3227
3436
|
timeoutMs
|
|
3228
|
-
}, (message) => message.type === "set_model_result");
|
|
3437
|
+
}, (message) => message.type === "set_model_result", assertDispatch);
|
|
3229
3438
|
}
|
|
3230
|
-
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
|
|
3439
|
+
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs, assertDispatch) {
|
|
3231
3440
|
return (await submitControlToQueueOwner(owner, {
|
|
3232
3441
|
type: "set_config_option",
|
|
3233
3442
|
requestId: randomUUID(),
|
|
@@ -3235,7 +3444,7 @@ async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeout
|
|
|
3235
3444
|
configId,
|
|
3236
3445
|
value,
|
|
3237
3446
|
timeoutMs
|
|
3238
|
-
}, (message) => message.type === "set_config_option_result"))?.response;
|
|
3447
|
+
}, (message) => message.type === "set_config_option_result", assertDispatch))?.response;
|
|
3239
3448
|
}
|
|
3240
3449
|
async function submitCloseSessionToQueueOwner(owner, timeoutMs) {
|
|
3241
3450
|
return (await submitControlToQueueOwner(owner, {
|
|
@@ -3317,34 +3526,42 @@ async function tryCancelOnRunningOwner(options) {
|
|
|
3317
3526
|
submit: (owner) => submitCancelToQueueOwner(owner, options.targetRequestId)
|
|
3318
3527
|
});
|
|
3319
3528
|
}
|
|
3320
|
-
async function
|
|
3529
|
+
async function withOwnerControlPersistence(owner, operation) {
|
|
3530
|
+
const persistsControlState = owner.persistsControlState === true;
|
|
3531
|
+
const value = await operation;
|
|
3532
|
+
return value === void 0 ? void 0 : {
|
|
3533
|
+
value,
|
|
3534
|
+
persistsControlState
|
|
3535
|
+
};
|
|
3536
|
+
}
|
|
3537
|
+
async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose, assertDispatch) {
|
|
3321
3538
|
return await tryControlOnRunningOwner({
|
|
3322
3539
|
sessionId,
|
|
3323
3540
|
verbose,
|
|
3324
3541
|
requestName: "set_mode",
|
|
3325
3542
|
logPrefix: "[acpx] requested session/set_mode on owner pid",
|
|
3326
|
-
submit: (owner) => submitSetModeToQueueOwner(owner, modeId, timeoutMs)
|
|
3543
|
+
submit: (owner) => withOwnerControlPersistence(owner, submitSetModeToQueueOwner(owner, modeId, timeoutMs, assertDispatch))
|
|
3327
3544
|
});
|
|
3328
3545
|
}
|
|
3329
|
-
async function trySetModelOnRunningOwner(sessionId, modelId, timeoutMs, verbose) {
|
|
3546
|
+
async function trySetModelOnRunningOwner(sessionId, modelId, timeoutMs, verbose, assertDispatch) {
|
|
3330
3547
|
return await tryControlOnRunningOwner({
|
|
3331
3548
|
sessionId,
|
|
3332
3549
|
verbose,
|
|
3333
3550
|
requestName: "set_model",
|
|
3334
3551
|
logPrefix: "[acpx] requested a model config update on owner pid",
|
|
3335
|
-
submit: (owner) => submitSetModelToQueueOwner(owner, modelId, timeoutMs)
|
|
3552
|
+
submit: (owner) => withOwnerControlPersistence(owner, submitSetModelToQueueOwner(owner, modelId, timeoutMs, assertDispatch))
|
|
3336
3553
|
});
|
|
3337
3554
|
}
|
|
3338
|
-
async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
|
|
3555
|
+
async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose, assertDispatch) {
|
|
3339
3556
|
return await tryControlOnRunningOwner({
|
|
3340
3557
|
sessionId,
|
|
3341
3558
|
verbose,
|
|
3342
3559
|
requestName: "set_config_option",
|
|
3343
3560
|
logPrefix: "[acpx] requested session/set_config_option on owner pid",
|
|
3344
|
-
submit: (owner) => submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs)
|
|
3561
|
+
submit: (owner) => withOwnerControlPersistence(owner, submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs, assertDispatch))
|
|
3345
3562
|
});
|
|
3346
3563
|
}
|
|
3347
3564
|
//#endregion
|
|
3348
|
-
export {
|
|
3565
|
+
export { setPerfGauge as $, isoNow as A, AcpxOperationalError as At, serializeSessionRecordForDisk as B, QueueConnectionError as Bt, promptToDisplayText as C, NON_INTERACTIVE_PERMISSION_POLICIES as Ct, findGitRepositoryRoot as D, PERMISSION_MODES as Dt, absolutePath as E, OUTPUT_FORMATS as Et, readSessionRecord as F, ClaudeAcpSessionCreateTimeoutError as Ft, SessionWatchError as G, SessionResumeRequiredError as Gt, normalizeAgentSessionId as H, SessionModeReplayError as Ht, resolveSessionRecord as I, CopilotAcpUnsupportedError as It, getPerfMetricsSnapshot as J, watchSession as K, UnsupportedPromptContentError as Kt, writeSessionRecord as L, GeminiAcpStartupTimeoutError as Lt, listSessionsForAgent as M, AgentSpawnError as Mt, normalizeName as N, AgentStartupError as Nt, findSession as O, PERMISSION_POLICY_ACTIONS as Ot, pruneSessions as P, AuthPolicyError as Pt, resetPerfMetrics as Q, assertPersistedKeyPolicy as R, PermissionDeniedError as Rt, parsePromptSource as S, EXIT_CODES as St, DEFAULT_HISTORY_LIMIT as T, OUTPUT_ERROR_ORIGINS as Tt, SESSION_JOURNAL_SCHEMA as U, SessionModelReplayError as Ut, extractAgentSessionId as V, SessionConfigOptionReplayError as Vt, SessionJournalReader as W, SessionNotFoundError as Wt, measurePerf as X, incrementPerfCounter as Y, recordPerfDuration as Z, waitMs as _, isProcessDefinitelyDead as _t, trySetModelOnRunningOwner as a, parseJsonRpcErrorMessage as at, getUnsupportedPromptContentMessage as b, isAcpResourceNotFoundError as bt, queueRequestExceedsLimit as c, DEFAULT_EVENT_SEGMENT_MAX_BYTES as ct, readQueueOwnerRecord as d, sessionEventActivePath as dt, startPerfTimer as et, refreshQueueOwnerLease as f, sessionEventLockPath as ft, tryAcquireQueueOwnerLease as g, isProcessAlive as gt, terminateQueueOwnerForSession as h, writePrivateJsonFile as ht, trySetModeOnRunningOwner as i, isSessionUpdateNotification as it, listSessions as j, AgentDisconnectedError as jt, findSessionByDirectoryWalk as k, SESSION_RECORD_SCHEMA as kt, parseQueueRequest as l, defaultSessionEventLog as lt, terminateProcess as m, writePrivateFile as mt, tryCloseSessionOnRunningOwner as n, isAcpJsonRpcMessage as nt, trySubmitToRunningOwner as o, parsePermissionNotice as ot, releaseQueueOwnerLease as p, sessionEventSegmentPath as pt, formatPerfMetric as q, trySetConfigOptionOnRunningOwner as r, isAcpMessageObject as rt, queueRequestByteLimit as s, parsePromptStopReason as st, tryCancelOnRunningOwner as t, extractSessionUpdateNotification as tt, probeQueueOwnerHealth as u, sessionBaseDir as ut, QueueLeaseGuardSettlementError as v, extractAcpError as vt, textPrompt as w, OUTPUT_ERROR_CODES as wt, mergePromptSourceWithText as x, AUTH_POLICIES as xt, PromptInputValidationError as y, formatUnknownErrorMessage as yt, parseSessionRecord as z, PermissionPromptUnavailableError as zt };
|
|
3349
3566
|
|
|
3350
|
-
//# sourceMappingURL=ipc-
|
|
3567
|
+
//# sourceMappingURL=ipc-BJrvfNXr.js.map
|