acpx 0.19.0 → 0.19.1

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.
Files changed (38) hide show
  1. package/dist/{agent-registry-Ct2yWPW7.js → agent-registry-DWlx5BQ4.js} +3 -2
  2. package/dist/agent-registry-DWlx5BQ4.js.map +1 -0
  3. package/dist/agent-registry.js +1 -1
  4. package/dist/{cli-Bmki9cj5.js → cli-BerngYEz.js} +5 -5
  5. package/dist/{cli-Bmki9cj5.js.map → cli-BerngYEz.js.map} +1 -1
  6. package/dist/cli.js +31 -15
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{client-DzaUKazo.d.ts → client-BchvQRpz.d.ts} +8 -1
  9. package/dist/client-BchvQRpz.d.ts.map +1 -0
  10. package/dist/{flows--Ewxn_fQ.js → flows-DimX0it5.js} +33 -20
  11. package/dist/flows-DimX0it5.js.map +1 -0
  12. package/dist/flows.d.ts +2 -1
  13. package/dist/flows.d.ts.map +1 -1
  14. package/dist/flows.js +1 -1
  15. package/dist/{invocation-options-DRzxHZmr.js → invocation-options-vobVApfO.js} +5 -5
  16. package/dist/{invocation-options-DRzxHZmr.js.map → invocation-options-vobVApfO.js.map} +1 -1
  17. package/dist/{ipc-fMgj5vrO.js → ipc-DAApJ5mi.js} +536 -57
  18. package/dist/ipc-DAApJ5mi.js.map +1 -0
  19. package/dist/{output-D6kDs8BN.js → output-Cnuji8Bo.js} +20 -12
  20. package/dist/output-Cnuji8Bo.js.map +1 -0
  21. package/dist/{queue-owner-runtime-CLwjMYNL.js → queue-owner-runtime-C4OOosbo.js} +253 -240
  22. package/dist/queue-owner-runtime-C4OOosbo.js.map +1 -0
  23. package/dist/runtime.d.ts +1 -1
  24. package/dist/runtime.d.ts.map +1 -1
  25. package/dist/runtime.js +5 -5
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/{session-watch-BfdIo-n4.js → session-watch-Bj9LoYUq.js} +4 -4
  28. package/dist/{session-watch-BfdIo-n4.js.map → session-watch-Bj9LoYUq.js.map} +1 -1
  29. package/dist/{watch-DH-zXQH2.js → watch-Cz6jbrRs.js} +2 -2
  30. package/dist/{watch-DH-zXQH2.js.map → watch-Cz6jbrRs.js.map} +1 -1
  31. package/package.json +1 -1
  32. package/skills/acpx/SKILL.md +2 -0
  33. package/dist/agent-registry-Ct2yWPW7.js.map +0 -1
  34. package/dist/client-DzaUKazo.d.ts.map +0 -1
  35. package/dist/flows--Ewxn_fQ.js.map +0 -1
  36. package/dist/ipc-fMgj5vrO.js.map +0 -1
  37. package/dist/output-D6kDs8BN.js.map +0 -1
  38. package/dist/queue-owner-runtime-CLwjMYNL.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { M as runTimedExecFile, n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY } from "./agent-registry-Ct2yWPW7.js";
2
- import { statSync } from "node:fs";
1
+ import { C as PROCESS_HELPER_TIMEOUT_MS, E as isChildProcessRunning, N as runTimedExecFile, S as PROCESS_HELPER_MAX_BUFFER_BYTES, n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY } from "./agent-registry-DWlx5BQ4.js";
2
+ import fs, { statSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import fs$1 from "node:fs/promises";
5
5
  import os from "node:os";
@@ -1727,17 +1727,18 @@ async function readSessionRecord(sessionId) {
1727
1727
  return;
1728
1728
  }
1729
1729
  }
1730
- function hasGitDirectory(dir) {
1730
+ function hasGitMarker(dir) {
1731
1731
  const gitPath = path.join(dir, ".git");
1732
1732
  try {
1733
- return statSync(gitPath).isDirectory();
1733
+ const marker = statSync(gitPath);
1734
+ return marker.isDirectory() || marker.isFile();
1734
1735
  } catch {
1735
1736
  return false;
1736
1737
  }
1737
1738
  }
1738
1739
  function isWithinBoundary(boundary, target) {
1739
1740
  const relative = path.relative(boundary, target);
1740
- return relative.length === 0 || !relative.startsWith("..") && !path.isAbsolute(relative);
1741
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
1741
1742
  }
1742
1743
  function absolutePath(value) {
1743
1744
  return path.resolve(value);
@@ -1746,7 +1747,7 @@ function findGitRepositoryRoot(startDir) {
1746
1747
  let current = absolutePath(startDir);
1747
1748
  const root = path.parse(current).root;
1748
1749
  for (;;) {
1749
- if (hasGitDirectory(current)) return current;
1750
+ if (hasGitMarker(current)) return current;
1750
1751
  if (current === root) return;
1751
1752
  const parent = path.dirname(current);
1752
1753
  if (parent === current) return;
@@ -2052,6 +2053,39 @@ const LINUX_PROCESS_QUERY = `
2052
2053
  if (error.code === 'ENOENT') return 'unsupported';
2053
2054
  throw error;
2054
2055
  });
2056
+ const prefix = async (name, size) => {
2057
+ const file = await fs.open(name, 'r');
2058
+ try {
2059
+ const buffer = Buffer.alloc(size);
2060
+ const { bytesRead } = await file.read(buffer, 0, size, 0);
2061
+ return buffer.subarray(0, bytesRead).toString('base64');
2062
+ } finally { await file.close(); }
2063
+ };
2064
+ const targets = async () => {
2065
+ if (targetPid !== 'all') {
2066
+ return { targetStat: await fs.readFile('/proc/' + targetPid + '/stat', 'utf8').catch(() => null) };
2067
+ }
2068
+ const required = new Set(JSON.parse(process.argv[3] || '[]'));
2069
+ const pids = new Set((await fs.readdir('/proc')).filter(pid => /^[1-9][0-9]*$/.test(pid)));
2070
+ for (const pid of required) pids.add(String(pid));
2071
+ const processStats = [];
2072
+ let bytes = 0;
2073
+ // Sequential reads keep proc I/O and retained memory bounded within this
2074
+ // single killable helper; never launch one helper or pending read per PID.
2075
+ for (const pid of [...pids].sort((a, b) => Number(a) - Number(b))) {
2076
+ let stat;
2077
+ try { stat = await fs.readFile('/proc/' + pid + '/stat', 'utf8'); }
2078
+ catch (error) {
2079
+ if (error.code === 'ENOENT' || error.code === 'ESRCH') continue;
2080
+ if (!required.has(Number(pid)) && (error.code === 'EACCES' || error.code === 'EPERM')) continue;
2081
+ throw error;
2082
+ }
2083
+ bytes += Buffer.byteLength(JSON.stringify(stat)) + 1;
2084
+ if (bytes > ${PROCESS_HELPER_MAX_BUFFER_BYTES} - 8192) throw new Error('Process table exceeds its byte limit');
2085
+ processStats.push(stat);
2086
+ }
2087
+ return { processStats, auxv: await prefix('/proc/self/auxv', 4096), elf: await prefix('/proc/self/exe', 6) };
2088
+ };
2055
2089
  Promise.all([
2056
2090
  fs.readFile('/proc/sys/kernel/random/boot_id', 'utf8'),
2057
2091
  fs.readlink('/proc/self/ns/pid'),
@@ -2059,12 +2093,12 @@ const LINUX_PROCESS_QUERY = `
2059
2093
  fs.readlink('/proc/' + observerPid + '/ns/pid'),
2060
2094
  timeNamespace(observerPid),
2061
2095
  fs.readFile('/proc/self/stat', 'utf8'),
2062
- fs.readFile('/proc/' + targetPid + '/stat', 'utf8').catch(() => null),
2096
+ targets(),
2063
2097
  ]).then(([bootId, pidNamespace, timeNamespace, observerPidNamespace,
2064
- observerTimeNamespace, selfStat, targetStat]) => {
2098
+ observerTimeNamespace, selfStat, target]) => {
2065
2099
  process.stdout.write(JSON.stringify({bootId: bootId.trim(), pidNamespace,
2066
2100
  timeNamespace, observerPidNamespace, observerTimeNamespace,
2067
- helperPid: process.pid, selfStat, targetStat}));
2101
+ helperPid: process.pid, selfStat, ...target}));
2068
2102
  }).catch(() => { process.exitCode = 1; });
2069
2103
  `;
2070
2104
  const POSIX_MONTHS = [
@@ -2146,31 +2180,48 @@ function parseLinuxStat(raw) {
2146
2180
  const pid = Number(prefix[1]);
2147
2181
  const fields = raw.slice(close + 2).trim().split(/\s+/u);
2148
2182
  const startTicks = fields[19];
2149
- if (!isPositivePid(pid) || !isStartTicks(startTicks) || !/^[RSDTtKWPI]$/.test(fields[0] ?? "")) return;
2150
- return {
2183
+ if (!isPositivePid(pid) || !isStartTicks(startTicks) || !/^[RSDTtKWPI]$/.test(fields[0])) return;
2184
+ const parentPid = Number(fields[1]);
2185
+ const groupPid = Number(fields[2]);
2186
+ return validProcessLinks(parentPid, groupPid) ? {
2151
2187
  pid,
2188
+ parentPid,
2189
+ groupPid,
2152
2190
  startTicks
2153
- };
2191
+ } : void 0;
2154
2192
  }
2155
- async function readLinuxObservation(pid, timeoutMs) {
2156
- const output = await runTimedExecFile(process.execPath, [
2193
+ function validProcessLinks(parentPid, groupPid) {
2194
+ return Number.isSafeInteger(parentPid) && parentPid >= 0 && Number.isSafeInteger(groupPid) && groupPid >= 0;
2195
+ }
2196
+ async function readLinuxQuery(pid, timeoutMs, requiredPids = []) {
2197
+ const args = [
2157
2198
  "--input-type=commonjs",
2158
2199
  "-e",
2159
2200
  LINUX_PROCESS_QUERY,
2160
2201
  String(pid),
2161
2202
  String(process.pid)
2162
- ], {
2203
+ ];
2204
+ if (pid === "all") args.push(JSON.stringify(requiredPids));
2205
+ const output = await runTimedExecFile(process.execPath, args, {
2163
2206
  timeoutMs,
2164
- maxBufferBytes: 8192,
2207
+ maxBufferBytes: pid === "all" ? PROCESS_HELPER_MAX_BUFFER_BYTES : 8192,
2165
2208
  env: {
2166
2209
  ...process.env,
2167
2210
  NODE_OPTIONS: ""
2168
2211
  }
2169
2212
  });
2170
- const raw = JSON.parse(output);
2213
+ return JSON.parse(output);
2214
+ }
2215
+ function verifiedLinuxScope(raw) {
2171
2216
  const scope = parseLinuxScope(raw);
2172
2217
  const self = parseLinuxStat(raw.selfStat);
2173
2218
  if (!scope || !self || self.pid !== raw.helperPid || scope.pidNamespace !== raw.observerPidNamespace || scope.timeNamespace !== raw.observerTimeNamespace) return;
2219
+ return scope;
2220
+ }
2221
+ async function readLinuxObservation(pid, timeoutMs) {
2222
+ const raw = await readLinuxQuery(pid, timeoutMs);
2223
+ const scope = verifiedLinuxScope(raw);
2224
+ if (!scope) return;
2174
2225
  const target = parseLinuxStat(raw.targetStat);
2175
2226
  return {
2176
2227
  scope,
@@ -2181,6 +2232,80 @@ async function readLinuxObservation(pid, timeoutMs) {
2181
2232
  } } : {}
2182
2233
  };
2183
2234
  }
2235
+ function linuxAuxv(raw) {
2236
+ if (typeof raw.elf !== "string" || typeof raw.auxv !== "string") throw new Error("Linux process clock is unavailable");
2237
+ const elf = Buffer.from(raw.elf, "base64");
2238
+ const auxv = Buffer.from(raw.auxv, "base64");
2239
+ if (elf.subarray(0, 4).toString("hex") !== "7f454c46" || ![1, 2].includes(elf[4]) || ![1, 2].includes(elf[5])) throw new Error("Linux process clock has an invalid ELF format");
2240
+ return {
2241
+ auxv,
2242
+ size: elf[4] === 1 ? 4 : 8,
2243
+ littleEndian: elf[5] === 1
2244
+ };
2245
+ }
2246
+ function linuxClockTicks(raw) {
2247
+ const { auxv, size, littleEndian } = linuxAuxv(raw);
2248
+ const word = (offset) => readAuxvWord(auxv, offset, size, littleEndian);
2249
+ for (let offset = 0; offset + size * 2 <= auxv.length; offset += size * 2) {
2250
+ const key = word(offset);
2251
+ if (key === 0n) break;
2252
+ if (key === 17n && word(offset + size) > 0n) return word(offset + size);
2253
+ }
2254
+ throw new Error("Linux process clock has no AT_CLKTCK");
2255
+ }
2256
+ function readAuxvWord(buffer, offset, size, littleEndian) {
2257
+ if (size === 8) return littleEndian ? buffer.readBigUInt64LE(offset) : buffer.readBigUInt64BE(offset);
2258
+ return BigInt(littleEndian ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset));
2259
+ }
2260
+ async function readLinuxProcessTable(timeoutMs, requiredPids, pid) {
2261
+ const raw = await readLinuxQuery("all", timeoutMs, requiredPids);
2262
+ const scope = verifiedLinuxScope(raw);
2263
+ if (!scope || !Array.isArray(raw.processStats)) throw new Error("Linux process table scope is unverified");
2264
+ const clockTicksPerSecond = linuxClockTicks(raw);
2265
+ const table = /* @__PURE__ */ new Map();
2266
+ for (const value of raw.processStats) {
2267
+ const stat = parseLinuxStat(value);
2268
+ if (stat && (pid === void 0 || stat.pid === pid)) table.set(stat.pid, {
2269
+ pid: stat.pid,
2270
+ parentPid: stat.parentPid,
2271
+ groupPid: stat.groupPid,
2272
+ birth: {
2273
+ kind: "linux-proc",
2274
+ ...scope,
2275
+ startTicks: stat.startTicks
2276
+ },
2277
+ clockTicksPerSecond
2278
+ });
2279
+ }
2280
+ return table;
2281
+ }
2282
+ function readLinuxExitClock() {
2283
+ let descriptor;
2284
+ try {
2285
+ descriptor = fs.openSync("/proc/uptime", "r");
2286
+ const buffer = Buffer.alloc(64);
2287
+ const bytes = fs.readSync(descriptor, buffer, 0, buffer.length, 0);
2288
+ const match = /^(\d+)\.(\d{2})\s/u.exec(buffer.toString("ascii", 0, bytes));
2289
+ return match ? {
2290
+ centiseconds: BigInt(match[1]) * 100n + BigInt(match[2]),
2291
+ timeNamespace: readOwnTimeNamespace()
2292
+ } : void 0;
2293
+ } catch {
2294
+ return;
2295
+ } finally {
2296
+ if (descriptor !== void 0) try {
2297
+ fs.closeSync(descriptor);
2298
+ } catch {}
2299
+ }
2300
+ }
2301
+ function readOwnTimeNamespace() {
2302
+ try {
2303
+ return fs.readlinkSync("/proc/self/ns/time");
2304
+ } catch (error) {
2305
+ if (error.code === "ENOENT") return "unsupported";
2306
+ throw error;
2307
+ }
2308
+ }
2184
2309
  function isPositivePid(pid) {
2185
2310
  return Number.isSafeInteger(pid) && pid > 0;
2186
2311
  }
@@ -2218,7 +2343,7 @@ function parseProcessTable(output) {
2218
2343
  pid,
2219
2344
  parentPid: Number(match[2]),
2220
2345
  groupPid: Number(match[3]),
2221
- birth
2346
+ birth: processBirthIdentity(birth)
2222
2347
  });
2223
2348
  }
2224
2349
  return table;
@@ -2227,7 +2352,7 @@ function processTableBirth(value) {
2227
2352
  if (process.platform !== "win32") return posixBirth(value);
2228
2353
  return isCanonicalBirth(value, "windows-creation") ? value : void 0;
2229
2354
  }
2230
- async function readProcessTable(timeoutMs, pid) {
2355
+ async function readProcessTable(timeoutMs, pid, requiredPids = []) {
2231
2356
  if (pid !== void 0 && (!Number.isSafeInteger(pid) || pid <= 0)) throw new Error("Process identity requires a positive PID");
2232
2357
  if (process.platform === "win32") return parseProcessTable(await runTimedExecFile(windowsPowerShellPath(), [
2233
2358
  "-NoLogo",
@@ -2239,6 +2364,7 @@ async function readProcessTable(timeoutMs, pid) {
2239
2364
  timeoutMs,
2240
2365
  windowsHide: true
2241
2366
  }));
2367
+ if (process.platform === "linux") return await readLinuxProcessTable(timeoutMs, requiredPids, pid);
2242
2368
  return parseProcessTable(await runTimedExecFile("ps", [
2243
2369
  ...pid === void 0 ? ["-e"] : ["-p", String(pid)],
2244
2370
  "-o",
@@ -2266,8 +2392,7 @@ async function probeProcessIdentity(pid, timeoutMs = IDENTITY_QUERY_TIMEOUT_MS$1
2266
2392
  }
2267
2393
  async function readNativeIdentity(pid, timeoutMs) {
2268
2394
  if (process.platform === "linux") return (await readLinuxObservation(pid, timeoutMs))?.identity;
2269
- const entry = (await readProcessTable(timeoutMs, pid)).get(pid);
2270
- return entry ? processBirthIdentity(entry.birth) : void 0;
2395
+ return (await readProcessTable(timeoutMs, pid)).get(pid)?.birth;
2271
2396
  }
2272
2397
  function processBirthIdentity(value) {
2273
2398
  return {
@@ -2314,6 +2439,138 @@ async function getOwnProcessIdentity() {
2314
2439
  return await ownIdentityPending;
2315
2440
  }
2316
2441
  //#endregion
2442
+ //#region src/acp/process-descendants.ts
2443
+ function hasStaleWindowsParent(identity, parent) {
2444
+ return identity.birth.kind === "windows-creation" && (parent?.birth.kind !== "windows-creation" || parent.birth.value > identity.birth.value);
2445
+ }
2446
+ function includeProcessDescendants(table, owned) {
2447
+ let expanded;
2448
+ do {
2449
+ expanded = false;
2450
+ for (const identity of table.values()) {
2451
+ if (hasStaleWindowsParent(identity, table.get(identity.parentPid))) continue;
2452
+ if (owned.has(identity.parentPid) && !owned.has(identity.pid)) {
2453
+ owned.add(identity.pid);
2454
+ expanded = true;
2455
+ }
2456
+ }
2457
+ } while (expanded);
2458
+ }
2459
+ /** Best-effort cleanup for descendants witnessed during this child launch. */
2460
+ var ProcessDescendants = class {
2461
+ child;
2462
+ identities = /* @__PURE__ */ new Map();
2463
+ rootBirth;
2464
+ pending;
2465
+ retired = false;
2466
+ ownProcessGroup;
2467
+ captureGroupAfterExit;
2468
+ groupExitedAt;
2469
+ onRootExit = () => {
2470
+ this.groupExitedAt = process.platform === "linux" ? readLinuxExitClock() ?? null : Date.now();
2471
+ };
2472
+ constructor(child, options = {}) {
2473
+ this.child = child;
2474
+ this.ownProcessGroup = process.platform !== "win32" && Boolean(options.ownProcessGroup);
2475
+ this.captureGroupAfterExit = this.ownProcessGroup && isChildProcessRunning(child);
2476
+ if (this.ownProcessGroup) child.once("exit", this.onRootExit);
2477
+ }
2478
+ capture(timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
2479
+ if (this.retired) return Promise.resolve(true);
2480
+ this.pending ??= this.readSnapshot(timeoutMs).finally(() => {
2481
+ this.pending = void 0;
2482
+ });
2483
+ return this.pending;
2484
+ }
2485
+ async readSnapshot(timeoutMs) {
2486
+ const deadline = performance.now() + timeoutMs;
2487
+ const rootWasRunning = isChildProcessRunning(this.child);
2488
+ const captureRootGroup = rootWasRunning || this.captureGroupAfterExit;
2489
+ if (!rootWasRunning) this.captureGroupAfterExit = false;
2490
+ try {
2491
+ const table = await readProcessTable(timeoutMs, void 0, this.requiredSnapshotPids(rootWasRunning));
2492
+ if (!this.retired) {
2493
+ table.delete(1);
2494
+ table.delete(process.pid);
2495
+ this.refresh(table, captureRootGroup);
2496
+ if (this.captureGroupAfterExit && !isChildProcessRunning(this.child)) return await this.readSnapshot(Math.max(1, deadline - performance.now()));
2497
+ }
2498
+ return true;
2499
+ } catch {
2500
+ if (!isChildProcessRunning(this.child)) this.captureGroupAfterExit = false;
2501
+ return false;
2502
+ }
2503
+ }
2504
+ requiredSnapshotPids(rootWasRunning) {
2505
+ return [...this.identities.keys(), ...rootWasRunning && this.child.pid ? [this.child.pid] : []];
2506
+ }
2507
+ refresh(table, rootWasRunning) {
2508
+ this.verifyScope(table);
2509
+ const owned = /* @__PURE__ */ new Set();
2510
+ for (const [pid, identity] of this.identities) {
2511
+ const observed = table.get(pid);
2512
+ if (observed && compareProcessBirthIdentity(identity.birth, observed.birth) === "matching") owned.add(pid);
2513
+ }
2514
+ this.includeRoot(table, owned);
2515
+ if (this.ownProcessGroup) this.includeProcessGroup(table, owned, rootWasRunning);
2516
+ includeProcessDescendants(table, owned);
2517
+ owned.delete(this.child.pid ?? 0);
2518
+ this.identities = new Map([...table].filter(([pid]) => owned.has(pid)));
2519
+ }
2520
+ verifyScope(table) {
2521
+ const expected = this.rootBirth ?? this.identities.values().next().value?.birth;
2522
+ const observation = table.values().next().value?.birth;
2523
+ if (expected && observation && compareProcessBirthIdentity(expected, observation) === "unknown") throw new Error("Descendant process scope changed");
2524
+ }
2525
+ includeRoot(table, owned) {
2526
+ const root = this.child.pid;
2527
+ const rootIdentity = root && table.get(root);
2528
+ if (rootIdentity && isChildProcessRunning(this.child)) {
2529
+ this.rootBirth ??= rootIdentity.birth;
2530
+ if (compareProcessBirthIdentity(this.rootBirth, rootIdentity.birth) === "matching") owned.add(rootIdentity.pid);
2531
+ }
2532
+ }
2533
+ includeProcessGroup(table, owned, rootWasRunning) {
2534
+ const root = this.child.pid;
2535
+ if (root && (rootWasRunning || [...owned].some((pid) => table.get(pid)?.groupPid === root))) {
2536
+ for (const identity of table.values()) if (identity.groupPid === root && this.startedBeforeRootExit(identity)) owned.add(identity.pid);
2537
+ }
2538
+ }
2539
+ startedBeforeRootExit(identity) {
2540
+ const cutoff = this.groupExitedAt;
2541
+ if (cutoff === void 0) return true;
2542
+ if (cutoff === null) return false;
2543
+ if (identity.birth.kind !== "linux-proc") return typeof cutoff === "number" && Date.parse(identity.birth.value) <= cutoff;
2544
+ if (typeof cutoff === "number" || identity.birth.timeNamespace !== cutoff.timeNamespace || !identity.clockTicksPerSecond) return false;
2545
+ return BigInt(identity.birth.startTicks) * 100n < (cutoff.centiseconds + 1n) * identity.clockTicksPerSecond;
2546
+ }
2547
+ async signal(signal, timeoutMs) {
2548
+ if (!await this.capture(timeoutMs) || this.retired) return;
2549
+ for (const pid of this.identities.keys()) try {
2550
+ process.kill(pid, signal);
2551
+ } catch {
2552
+ this.identities.delete(pid);
2553
+ }
2554
+ }
2555
+ hasTrackedProcesses() {
2556
+ return this.identities.size > 0;
2557
+ }
2558
+ async waitForExit(timeoutMs) {
2559
+ const deadline = performance.now() + timeoutMs;
2560
+ do {
2561
+ if (!await this.capture(Math.max(1, deadline - performance.now()))) return false;
2562
+ if (this.identities.size === 0) return true;
2563
+ await setTimeout$1(Math.min(100, Math.max(0, deadline - performance.now())));
2564
+ } while (performance.now() < deadline);
2565
+ return false;
2566
+ }
2567
+ retire() {
2568
+ this.retired = true;
2569
+ this.child.off("exit", this.onRootExit);
2570
+ this.identities.clear();
2571
+ }
2572
+ };
2573
+ //#endregion
2317
2574
  //#region src/session/lock-owner.ts
2318
2575
  const OBSERVATION_REUSE_MS = 1e3;
2319
2576
  const IDENTITY_QUERY_TIMEOUT_MS = 1e3;
@@ -2377,6 +2634,127 @@ function queueSocketPath(sessionId, homeDir = os.homedir()) {
2377
2634
  return path.join(queueSocketBaseDir(homeDir) ?? "/tmp", `${key}.sock`);
2378
2635
  }
2379
2636
  //#endregion
2637
+ //#region src/session/queue/retirement-receipt.ts
2638
+ const MAX_RETIREMENT_MEMBERS = 4096;
2639
+ const MAX_RETIREMENT_BYTES = 1048576;
2640
+ function hasQueueRetirementReceipt(value) {
2641
+ return value !== null && typeof value === "object" && Object.hasOwn(value, "retirement");
2642
+ }
2643
+ function parseWitness(raw) {
2644
+ if (!raw || typeof raw !== "object") return;
2645
+ const value = raw;
2646
+ const processIdentity = parseProcessBirthIdentity(value.processIdentity);
2647
+ if (typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 1 || processIdentity?.kind !== "windows-creation") return;
2648
+ return {
2649
+ pid: value.pid,
2650
+ processIdentity
2651
+ };
2652
+ }
2653
+ function parseQueueRetirementReceipt(raw, owner) {
2654
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
2655
+ const value = raw;
2656
+ const root = parseReceiptRoot(value, owner);
2657
+ if (!root) return null;
2658
+ const descendants = parseDescendants(value.descendants, root.pid);
2659
+ if (!descendants) return null;
2660
+ try {
2661
+ if (Buffer.byteLength(JSON.stringify(raw)) > MAX_RETIREMENT_BYTES) return null;
2662
+ } catch {
2663
+ return null;
2664
+ }
2665
+ return {
2666
+ ownerGeneration: owner.ownerGeneration,
2667
+ root,
2668
+ descendants
2669
+ };
2670
+ }
2671
+ function parseReceiptRoot(value, owner) {
2672
+ const root = parseWitness(value.root);
2673
+ if (!root || root.pid !== owner.pid || value.ownerGeneration !== owner.ownerGeneration || compareProcessBirthIdentity(owner.processIdentity, root.processIdentity) !== "matching") return;
2674
+ return root;
2675
+ }
2676
+ function parseDescendants(raw, rootPid) {
2677
+ if (!Array.isArray(raw) || raw.length >= MAX_RETIREMENT_MEMBERS) return;
2678
+ const pids = /* @__PURE__ */ new Set([rootPid]);
2679
+ const witnesses = [];
2680
+ for (const value of raw) {
2681
+ const witness = parseWitness(value);
2682
+ if (!witness || pids.has(witness.pid)) return;
2683
+ pids.add(witness.pid);
2684
+ witnesses.push(witness);
2685
+ }
2686
+ return witnesses;
2687
+ }
2688
+ function queueRetirementIncomplete() {
2689
+ return new QueueConnectionError("Queue-owner descendant cleanup is incomplete or unverified. Its lease was retained. Retry cleanup with updated clients and process-query access; do not remove the lease while recorded processes may remain.", {
2690
+ detailCode: "QUEUE_OWNER_RETIREMENT_INCOMPLETE",
2691
+ origin: "queue",
2692
+ retryable: true
2693
+ });
2694
+ }
2695
+ function retirementTimeRemaining(deadline, maximumMs = 2e3) {
2696
+ const remaining = deadline - performance.now();
2697
+ if (remaining <= 0) throw queueRetirementIncomplete();
2698
+ return Math.min(maximumMs, remaining);
2699
+ }
2700
+ async function captureQueueRetirementReceipt(owner, previous, rootRunning, deadline) {
2701
+ const root = parseWitness({
2702
+ pid: owner.pid,
2703
+ processIdentity: owner.processIdentity
2704
+ });
2705
+ if (!root || process.platform !== "win32") throw queueRetirementIncomplete();
2706
+ const table = await readProcessTable(retirementTimeRemaining(deadline)).catch(() => {
2707
+ throw queueRetirementIncomplete();
2708
+ });
2709
+ const saved = unsettledSnapshotWitnesses(table, previous ? previous.descendants : []);
2710
+ const owned = matchingSnapshotPids(table, rootRunning ? [root, ...saved] : saved);
2711
+ if (rootRunning && !owned.has(root.pid)) throw queueRetirementIncomplete();
2712
+ table.delete(1);
2713
+ table.delete(process.pid);
2714
+ includeProcessDescendants(table, owned);
2715
+ owned.delete(root.pid);
2716
+ const receipt = {
2717
+ ownerGeneration: owner.ownerGeneration,
2718
+ root,
2719
+ descendants: mergeSnapshotDescendants(table, owned, saved)
2720
+ };
2721
+ if (!parseQueueRetirementReceipt(receipt, owner)) throw queueRetirementIncomplete();
2722
+ return receipt;
2723
+ }
2724
+ function unsettledSnapshotWitnesses(table, witnesses) {
2725
+ return witnesses.filter((witness) => {
2726
+ const observed = table.get(witness.pid);
2727
+ return !observed || compareProcessBirthIdentity(witness.processIdentity, observed.birth) !== "different";
2728
+ });
2729
+ }
2730
+ function matchingSnapshotPids(table, witnesses) {
2731
+ const owned = /* @__PURE__ */ new Set();
2732
+ for (const witness of witnesses) {
2733
+ const observed = table.get(witness.pid);
2734
+ if (observed && compareProcessBirthIdentity(witness.processIdentity, observed.birth) === "matching") owned.add(witness.pid);
2735
+ }
2736
+ return owned;
2737
+ }
2738
+ function mergeSnapshotDescendants(table, owned, saved) {
2739
+ const descendants = new Map(saved.map((witness) => [witness.pid, witness]));
2740
+ for (const pid of owned) {
2741
+ const observed = table.get(pid);
2742
+ if (observed) descendants.set(pid, {
2743
+ pid,
2744
+ processIdentity: observed.birth
2745
+ });
2746
+ }
2747
+ return [...descendants.values()];
2748
+ }
2749
+ async function observeRetirementWitness(witness, deadline) {
2750
+ if (witness.pid === process.pid) throw queueRetirementIncomplete();
2751
+ return await observeProcessIncarnation(witness.pid, witness.processIdentity, retirementTimeRemaining(deadline));
2752
+ }
2753
+ async function assertQueueRetirementComplete(receipt, deadline) {
2754
+ if (receipt === null) throw queueRetirementIncomplete();
2755
+ for (const witness of receipt?.descendants ?? []) if (await observeRetirementWitness(witness, deadline) !== "gone") throw queueRetirementIncomplete();
2756
+ }
2757
+ //#endregion
2380
2758
  //#region src/session/queue/lease-mutation.ts
2381
2759
  const GUARD_WAIT_MS = 2e3;
2382
2760
  const localMutations = /* @__PURE__ */ new Map();
@@ -2432,7 +2810,7 @@ async function rollbackReservation(lockPath, expected) {
2432
2810
  } catch {
2433
2811
  return;
2434
2812
  }
2435
- if (matchesReservation(value, expected)) await fs$1.unlink(lockPath);
2813
+ if (matchesReservation(value, expected) && !hasQueueRetirementReceipt(value)) await fs$1.unlink(lockPath);
2436
2814
  } catch (error) {
2437
2815
  if (error.code !== "ENOENT") throw error;
2438
2816
  }
@@ -2537,7 +2915,7 @@ function parseQueueOwnerRecord(raw) {
2537
2915
  const record = raw;
2538
2916
  if (!hasValidQueueOwnerRecordFields(record)) return null;
2539
2917
  const processIdentity = parseProcessBirthIdentity(record.processIdentity);
2540
- return {
2918
+ const owner = {
2541
2919
  pid: record.pid,
2542
2920
  sessionId: record.sessionId,
2543
2921
  socketPath: record.socketPath,
@@ -2550,6 +2928,8 @@ function parseQueueOwnerRecord(raw) {
2550
2928
  ...typeof record.mcpConfigPath === "string" ? { mcpConfigPath: record.mcpConfigPath } : {},
2551
2929
  ...typeof record.mcpConfigFingerprint === "string" ? { mcpConfigFingerprint: record.mcpConfigFingerprint } : {}
2552
2930
  };
2931
+ if (hasQueueRetirementReceipt(record)) owner.retirement = parseQueueRetirementReceipt(record.retirement, owner);
2932
+ return owner;
2553
2933
  }
2554
2934
  function parseQueueOwnerCapabilities(record) {
2555
2935
  return {
@@ -2611,20 +2991,21 @@ async function removeSocketFile(socketPath) {
2611
2991
  }
2612
2992
  }
2613
2993
  async function waitForProcessExit(pid, timeoutMs, hasExited) {
2614
- const deadline = Date.now() + Math.max(0, timeoutMs);
2615
- while (Date.now() <= deadline) {
2994
+ const deadline = performance.now() + Math.max(0, timeoutMs);
2995
+ while (performance.now() <= deadline) {
2616
2996
  if (hasExited(pid)) return true;
2617
2997
  await waitMs(PROCESS_POLL_MS);
2618
2998
  }
2619
2999
  return hasExited(pid);
2620
3000
  }
2621
- async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent, capturedIdentity) {
3001
+ async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent, capturedIdentity, deadline = retirementDeadline()) {
2622
3002
  await withQueueLeaseMutation(sessionId, async () => {
2623
- await cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent);
3003
+ await cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent, deadline);
2624
3004
  }, { capturedIdentity });
2625
3005
  }
2626
- async function cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent) {
3006
+ async function cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent, deadline = retirementDeadline()) {
2627
3007
  if (!await isCurrent()) return;
3008
+ await assertQueueRetirementComplete((await readQueueOwnerRecord(sessionId))?.retirement, deadline);
2628
3009
  await removeSocketFile(socketPath).catch(() => {});
2629
3010
  if (!await isCurrent()) return;
2630
3011
  await fs$1.unlink(queueLockFilePath(sessionId)).catch((error) => {
@@ -2640,6 +3021,9 @@ function matchesQueueOwner(current, expected) {
2640
3021
  function ownerIsAlive(owner) {
2641
3022
  return owner.pid === process.pid || isProcessAlive(owner.pid);
2642
3023
  }
3024
+ function ownerAcceptsWork(owner) {
3025
+ return owner.retirement === void 0 && ownerIsAlive(owner) && !isQueueOwnerHeartbeatStale(owner);
3026
+ }
2643
3027
  async function readQueueOwnerRecord(sessionId) {
2644
3028
  const lockPath = queueLockFilePath(sessionId);
2645
3029
  try {
@@ -2665,7 +3049,7 @@ function dispatchSignal(pid, signal) {
2665
3049
  return false;
2666
3050
  }
2667
3051
  }
2668
- async function dispatchQueueOwnerSignal(pid, signal) {
3052
+ async function dispatchQueueOwnerSignal(pid, signal, deadline) {
2669
3053
  if (process.platform !== "win32") return dispatchSignal(pid, signal);
2670
3054
  const systemRoot = process.env.SystemRoot;
2671
3055
  if (!systemRoot || !path.win32.isAbsolute(systemRoot)) throw new Error("Windows queue-owner cleanup requires an absolute SystemRoot directory");
@@ -2676,15 +3060,15 @@ async function dispatchQueueOwnerSignal(pid, signal) {
2676
3060
  "/T",
2677
3061
  "/F"
2678
3062
  ], {
2679
- timeoutMs: PROCESS_SIGTERM_GRACE_MS,
3063
+ timeoutMs: retirementTimeRemaining(deadline, PROCESS_SIGTERM_GRACE_MS),
2680
3064
  windowsHide: true
2681
3065
  });
2682
3066
  return true;
2683
3067
  }
2684
- async function terminateWithDispatch(pid, dispatch, hasExited) {
3068
+ async function terminateWithDispatch(pid, dispatch, hasExited, deadline = Infinity) {
2685
3069
  for (const [signal, graceMs] of [["SIGTERM", PROCESS_SIGTERM_GRACE_MS], ["SIGKILL", PROCESS_SIGKILL_GRACE_MS]]) {
2686
3070
  if (!await dispatch(signal)) return false;
2687
- if (await waitForProcessExit(pid, graceMs, hasExited)) return true;
3071
+ if (await waitForProcessExit(pid, Math.min(graceMs, deadline - performance.now()), hasExited)) return true;
2688
3072
  }
2689
3073
  return false;
2690
3074
  }
@@ -2692,10 +3076,10 @@ async function resolveUsableQueueOwner(sessionId, owner) {
2692
3076
  await settlePendingQueueLeaseGuard(sessionId);
2693
3077
  const observed = await readQueueOwnerRecord(sessionId);
2694
3078
  if (!matchesQueueOwner(observed, owner)) return;
2695
- if (ownerIsAlive(observed) && !isQueueOwnerHeartbeatStale(observed)) return observed;
3079
+ if (ownerAcceptsWork(observed)) return observed;
2696
3080
  await terminateQueueOwnerForSession(sessionId, observed, true);
2697
3081
  const current = await readQueueOwnerRecord(sessionId);
2698
- return matchesQueueOwner(current, observed) && ownerIsAlive(current) && !isQueueOwnerHeartbeatStale(current) ? current : void 0;
3082
+ return matchesQueueOwner(current, observed) && ownerAcceptsWork(current) ? current : void 0;
2699
3083
  }
2700
3084
  async function tryAcquireQueueOwnerLease(sessionId, mcpConfigOrNowIsoFactory, nowIsoFactory = nowIso) {
2701
3085
  const { mcpConfigPath, clock } = resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory);
@@ -2806,9 +3190,13 @@ function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso) {
2806
3190
  if (lease.released) return Promise.resolve();
2807
3191
  const update = lease.updates.then(async () => {
2808
3192
  await withQueueLeaseMutation(lease.sessionId, async () => {
2809
- if (!await ownsQueueLease(lease)) return;
2810
- await stageQueueOwnerRecord(lease, options.queueDepth, nowIsoFactory, async (tempPath) => {
2811
- await fs$1.rename(tempPath, lease.lockPath);
3193
+ const current = await readQueueOwnerRecord(lease.sessionId);
3194
+ if (!matchesQueueOwner(current, lease)) return;
3195
+ if (current.retirement === null) throw queueRetirementIncomplete();
3196
+ await replaceQueueOwnerRecord({
3197
+ ...current,
3198
+ heartbeatAt: nowIsoFactory(),
3199
+ queueDepth: Math.max(0, Math.round(options.queueDepth))
2812
3200
  });
2813
3201
  }, { capturedIdentity: lease });
2814
3202
  });
@@ -2816,8 +3204,8 @@ function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso) {
2816
3204
  return update;
2817
3205
  }
2818
3206
  async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
2819
- const payload = JSON.stringify({
2820
- pid: process.pid,
3207
+ await stagePreparedQueueOwnerRecord({
3208
+ pid: lease.pid,
2821
3209
  sessionId: lease.sessionId,
2822
3210
  socketPath: lease.socketPath,
2823
3211
  createdAt: lease.createdAt,
@@ -2830,9 +3218,17 @@ async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
2830
3218
  persistsControlState: true,
2831
3219
  ...lease.mcpConfigPath ? { mcpConfigPath: lease.mcpConfigPath } : {},
2832
3220
  ...lease.mcpConfigFingerprint ? { mcpConfigFingerprint: lease.mcpConfigFingerprint } : {}
2833
- }, null, 2);
3221
+ }, publish);
3222
+ }
3223
+ async function replaceQueueOwnerRecord(owner) {
3224
+ await stagePreparedQueueOwnerRecord(owner, async (tempPath) => {
3225
+ await fs$1.rename(tempPath, queueLockFilePath(owner.sessionId));
3226
+ });
3227
+ }
3228
+ async function stagePreparedQueueOwnerRecord(owner, publish) {
3229
+ const payload = JSON.stringify(owner, null, 2);
2834
3230
  await withTempFile({
2835
- rootDir: path.dirname(lease.lockPath),
3231
+ rootDir: path.dirname(queueLockFilePath(owner.sessionId)),
2836
3232
  prefix: "owner",
2837
3233
  fileName: "lease"
2838
3234
  }, async (tempPath) => {
@@ -2856,40 +3252,120 @@ function unverifiedQueueOwnerError(owner) {
2856
3252
  retryable: true
2857
3253
  });
2858
3254
  }
3255
+ function retirementDeadline() {
3256
+ return performance.now() + PROCESS_SIGTERM_GRACE_MS + PROCESS_SIGKILL_GRACE_MS;
3257
+ }
3258
+ async function observeRetiringOwner(owner, deadline) {
3259
+ return await observeProcessIncarnation(owner.pid, owner.processIdentity, process.platform === "win32" ? retirementTimeRemaining(deadline) : void 0);
3260
+ }
3261
+ function retirementBlockedByHeartbeat(owner, signal, requireStale) {
3262
+ return requireStale && owner.retirement === void 0 && signal === "SIGTERM" && !isQueueOwnerHeartbeatStale(owner);
3263
+ }
3264
+ async function prepareWindowsRetirement(owner, deadline) {
3265
+ if (owner.retirement === null) throw queueRetirementIncomplete();
3266
+ owner.retirement = await captureQueueRetirementReceipt(owner, owner.retirement, true, deadline);
3267
+ await replaceQueueOwnerRecord(owner);
3268
+ return await observeRetirementWitness(owner.retirement.root, deadline) === "matching";
3269
+ }
2859
3270
  async function dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement) {
2860
3271
  return await withQueueLeaseMutation(owner.sessionId, async () => {
2861
3272
  const current = await readQueueOwnerRecord(owner.sessionId);
2862
3273
  retirement.leaseReleased = current?.pid !== owner.pid;
2863
- if (!matchesQueueOwner(current, owner) || requireStale && signal === "SIGTERM" && !isQueueOwnerHeartbeatStale(current)) return false;
2864
- const identity = await observeProcessIncarnation(current.pid, current.processIdentity);
3274
+ if (!matchesQueueOwner(current, owner) || retirementBlockedByHeartbeat(current, signal, requireStale)) return false;
3275
+ if (current.retirement === null) throw queueRetirementIncomplete();
3276
+ const identity = await observeRetiringOwner(current, retirement.deadline);
2865
3277
  if (identity !== "matching") {
2866
3278
  retirement.gone = identity === "gone";
2867
3279
  retirement.unverified = identity === "unknown";
2868
3280
  return false;
2869
3281
  }
2870
- return await dispatchQueueOwnerSignal(owner.pid, signal);
3282
+ if (process.platform === "win32" && !await prepareWindowsRetirement(current, retirement.deadline)) return false;
3283
+ return await dispatchQueueOwnerSignal(owner.pid, signal, retirement.deadline);
2871
3284
  });
2872
3285
  }
2873
- async function retireQueueOwner(owner, requireStale) {
2874
- const deadline = performance.now() + PROCESS_SIGTERM_GRACE_MS + PROCESS_SIGKILL_GRACE_MS;
3286
+ async function retireQueueOwner(owner, requireStale, deadline) {
2875
3287
  const retirement = {
2876
3288
  gone: false,
2877
3289
  unverified: false,
2878
- leaseReleased: false
3290
+ leaseReleased: false,
3291
+ deadline
2879
3292
  };
2880
- await terminateWithDispatch(owner.pid, (signal) => dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement), isProcessDefinitelyDead);
2881
- if (retirement.unverified && requireStale) throw unverifiedQueueOwnerError(owner);
2882
- if (retirement.leaseReleased || retirement.unverified) await waitForProcessExit(owner.pid, Math.max(0, deadline - performance.now()), isProcessDefinitelyDead);
2883
- const gone = retirement.gone || await observeProcessIncarnation(owner.pid, owner.processIdentity) === "gone";
3293
+ await terminateWithDispatch(owner.pid, (signal) => dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement), isProcessDefinitelyDead, process.platform === "win32" ? deadline : Infinity);
3294
+ if (await waitForReleasedOwner(owner, requireStale, retirement)) return false;
3295
+ const gone = retirement.gone || await observeRetiringOwner(owner, deadline) === "gone";
2884
3296
  if (retirement.unverified && !gone) throw unverifiedQueueOwnerError(owner);
2885
3297
  return gone;
2886
3298
  }
3299
+ async function waitForReleasedOwner(owner, requireStale, retirement) {
3300
+ if (retirement.unverified && requireStale) throw unverifiedQueueOwnerError(owner);
3301
+ if (retirement.leaseReleased || retirement.unverified) {
3302
+ await waitForProcessExit(owner.pid, Math.max(0, retirement.deadline - performance.now()), isProcessDefinitelyDead);
3303
+ if (performance.now() >= retirement.deadline) {
3304
+ if (retirement.unverified) throw unverifiedQueueOwnerError(owner);
3305
+ return true;
3306
+ }
3307
+ }
3308
+ return false;
3309
+ }
3310
+ async function retireRecordedDescendants(owner, deadline) {
3311
+ await withQueueLeaseMutation(owner.sessionId, async () => {
3312
+ const current = await readQueueOwnerRecord(owner.sessionId);
3313
+ if (!matchesQueueOwner(current, owner) || current.retirement === void 0) return;
3314
+ if (current.retirement === null || process.platform !== "win32") throw queueRetirementIncomplete();
3315
+ if (current.retirement.descendants.length === 0) return;
3316
+ current.retirement = await captureQueueRetirementReceipt(current, current.retirement, false, deadline);
3317
+ await replaceQueueOwnerRecord(current);
3318
+ await signalRecordedDescendants(current.retirement, deadline);
3319
+ await waitForRecordedDescendants(current.retirement, deadline);
3320
+ });
3321
+ }
3322
+ async function hasSurvivingRetirementDescendant(receipt, deadline) {
3323
+ for (const witness of receipt.descendants) {
3324
+ const state = await observeRetirementWitness(witness, deadline);
3325
+ if (state === "unknown") throw queueRetirementIncomplete();
3326
+ if (state === "matching") return true;
3327
+ }
3328
+ return false;
3329
+ }
3330
+ async function signalRecordedDescendants(receipt, deadline) {
3331
+ for (const witness of receipt.descendants) {
3332
+ const state = await observeRetirementWitness(witness, deadline);
3333
+ if (state === "unknown") throw queueRetirementIncomplete();
3334
+ if (state === "matching") {
3335
+ retirementTimeRemaining(deadline);
3336
+ try {
3337
+ process.kill(witness.pid, "SIGKILL");
3338
+ } catch (error) {
3339
+ if (await observeRetirementWitness(witness, deadline) !== "gone") throw error;
3340
+ }
3341
+ }
3342
+ }
3343
+ }
3344
+ async function waitForRecordedDescendants(receipt, deadline) {
3345
+ while (true) {
3346
+ if (!await hasSurvivingRetirementDescendant(receipt, deadline)) return;
3347
+ await waitMs(Math.min(1e3, retirementTimeRemaining(deadline)));
3348
+ }
3349
+ }
3350
+ async function assertNoPendingRetirement(owner) {
3351
+ const current = await readQueueOwnerRecord(owner.sessionId);
3352
+ if (matchesQueueOwner(current, owner) && current.retirement !== void 0) throw queueRetirementIncomplete();
3353
+ }
2887
3354
  async function terminateQueueOwnerForSession(sessionId, expectedOwner, requireStale = false) {
2888
3355
  await settlePendingQueueLeaseGuard(sessionId);
2889
3356
  const owner = expectedOwner ?? await readQueueOwnerRecord(sessionId);
2890
- if (!owner || owner.sessionId !== sessionId || owner.pid === process.pid) return;
2891
- if (!await retireQueueOwner(owner, requireStale)) return;
2892
- await cleanupQueueOwnerFiles(sessionId, owner.socketPath, () => ownsQueueLease(owner));
3357
+ if (!owner || owner.sessionId !== sessionId) return;
3358
+ if (owner.pid === process.pid) {
3359
+ if (owner.retirement !== void 0) throw queueRetirementIncomplete();
3360
+ return;
3361
+ }
3362
+ const deadline = retirementDeadline();
3363
+ if (!await retireQueueOwner(owner, requireStale, deadline)) {
3364
+ await assertNoPendingRetirement(owner);
3365
+ return;
3366
+ }
3367
+ await retireRecordedDescendants(owner, deadline);
3368
+ await cleanupQueueOwnerFiles(sessionId, owner.socketPath, () => ownsQueueLease(owner), void 0, deadline);
2893
3369
  }
2894
3370
  async function waitMs(ms) {
2895
3371
  await new Promise((resolve) => {
@@ -3294,9 +3770,12 @@ function queueOwnerMessageParser(type) {
3294
3770
  }
3295
3771
  function parseEventOwnerMessage(message, context) {
3296
3772
  if (!isAcpJsonRpcMessage(message.message)) return null;
3773
+ const direction = message.direction;
3774
+ if (direction !== void 0 && direction !== "inbound" && direction !== "outbound") return null;
3297
3775
  return {
3298
3776
  type: "event",
3299
3777
  ...context,
3778
+ ...direction === void 0 ? {} : { direction },
3300
3779
  message: message.message
3301
3780
  };
3302
3781
  }
@@ -3578,7 +4057,7 @@ function unexpectedQueueResponseError() {
3578
4057
  }
3579
4058
  function handleAcknowledgedSubmitMessage(message, controls, formatter) {
3580
4059
  if (message.type === "event") {
3581
- notifyObserver(() => formatter.onAcpMessage(message.message));
4060
+ notifyObserver(() => formatter.onAcpMessage(message.message, message.direction));
3582
4061
  return;
3583
4062
  }
3584
4063
  if (message.type === "permission_escalation") {
@@ -3861,6 +4340,6 @@ async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, time
3861
4340
  });
3862
4341
  }
3863
4342
  //#endregion
3864
- export { measurePerf as $, findGitRepositoryRoot as A, PERMISSION_POLICY_ACTIONS as At, writeSessionRecord as B, PermissionDeniedError as Bt, getUnsupportedPromptContentMessage as C, AUTH_POLICIES as Ct, textPrompt as D, OUTPUT_ERROR_ORIGINS as Dt, promptToDisplayText as E, OUTPUT_ERROR_CODES as Et, listSessionsForAgent as F, AgentStartupError as Ft, normalizeAgentSessionId as G, SessionModelReplayError as Gt, parseSessionRecord as H, QueueConnectionError as Ht, normalizeName as I, AuthPolicyError as It, SessionWatchError as J, UnsupportedPromptContentError as Jt, SESSION_JOURNAL_SCHEMA as K, SessionNotFoundError as Kt, pruneSessions as L, ClaudeAcpSessionCreateTimeoutError as Lt, findSessionByDirectoryWalk as M, AcpxOperationalError as Mt, isoNow as N, AgentDisconnectedError as Nt, DEFAULT_HISTORY_LIMIT as O, OUTPUT_FORMATS as Ot, listSessions as P, AgentSpawnError as Pt, incrementPerfCounter as Q, readSessionRecord as R, CopilotAcpUnsupportedError as Rt, PromptInputValidationError as S, isAcpResourceNotFoundError as St, parsePromptSource as T, NON_INTERACTIVE_PERMISSION_POLICIES as Tt, serializeSessionRecordForDisk as U, SessionConfigOptionReplayError as Ut, assertPersistedKeyPolicy as V, PermissionPromptUnavailableError as Vt, extractAgentSessionId as W, SessionModeReplayError as Wt, formatPerfMetric as X, watchSession as Y, getPerfMetricsSnapshot as Z, waitMs as _, writePrivateFile as _t, trySetModelOnRunningOwner as a, isAcpJsonRpcMessage as at, lockOwnerPid as b, extractAcpError as bt, queueRequestExceedsLimit as c, parseJsonRpcErrorMessage as ct, readQueueOwnerRecord as d, DEFAULT_EVENT_SEGMENT_MAX_BYTES as dt, recordPerfDuration as et, refreshQueueOwnerLease as f, defaultSessionEventLog as ft, tryAcquireQueueOwnerLease as g, sessionEventSegmentPath as gt, terminateQueueOwnerForSession as h, sessionEventLockPath as ht, trySetModeOnRunningOwner as i, extractSessionUpdateNotification as it, findSession as j, SESSION_RECORD_SCHEMA as jt, absolutePath as k, PERMISSION_MODES as kt, parseQueueRequest as l, parsePermissionNotice as lt, terminateProcess as m, sessionEventActivePath as mt, tryCloseSessionOnRunningOwner as n, setPerfGauge as nt, trySubmitToRunningOwner as o, isAcpMessageObject as ot, releaseQueueOwnerLease as p, sessionBaseDir as pt, SessionJournalReader as q, SessionResumeRequiredError as qt, trySetConfigOptionOnRunningOwner as r, startPerfTimer as rt, queueRequestByteLimit as s, isSessionUpdateNotification as st, tryCancelOnRunningOwner as t, resetPerfMetrics as tt, probeQueueOwnerHealth as u, parsePromptStopReason as ut, QueueLeaseGuardSettlementError as v, writePrivateJsonFile as vt, mergePromptSourceWithText as w, EXIT_CODES as wt, readProcessTable as x, formatUnknownErrorMessage as xt, createLockOwner as y, isProcessAlive as yt, resolveSessionRecord as z, GeminiAcpStartupTimeoutError as zt };
4343
+ export { measurePerf as $, findGitRepositoryRoot as A, PERMISSION_POLICY_ACTIONS as At, writeSessionRecord as B, PermissionDeniedError as Bt, getUnsupportedPromptContentMessage as C, AUTH_POLICIES as Ct, textPrompt as D, OUTPUT_ERROR_ORIGINS as Dt, promptToDisplayText as E, OUTPUT_ERROR_CODES as Et, listSessionsForAgent as F, AgentStartupError as Ft, normalizeAgentSessionId as G, SessionModelReplayError as Gt, parseSessionRecord as H, QueueConnectionError as Ht, normalizeName as I, AuthPolicyError as It, SessionWatchError as J, UnsupportedPromptContentError as Jt, SESSION_JOURNAL_SCHEMA as K, SessionNotFoundError as Kt, pruneSessions as L, ClaudeAcpSessionCreateTimeoutError as Lt, findSessionByDirectoryWalk as M, AcpxOperationalError as Mt, isoNow as N, AgentDisconnectedError as Nt, DEFAULT_HISTORY_LIMIT as O, OUTPUT_FORMATS as Ot, listSessions as P, AgentSpawnError as Pt, incrementPerfCounter as Q, readSessionRecord as R, CopilotAcpUnsupportedError as Rt, PromptInputValidationError as S, isAcpResourceNotFoundError as St, parsePromptSource as T, NON_INTERACTIVE_PERMISSION_POLICIES as Tt, serializeSessionRecordForDisk as U, SessionConfigOptionReplayError as Ut, assertPersistedKeyPolicy as V, PermissionPromptUnavailableError as Vt, extractAgentSessionId as W, SessionModeReplayError as Wt, formatPerfMetric as X, watchSession as Y, getPerfMetricsSnapshot as Z, waitMs as _, writePrivateFile as _t, trySetModelOnRunningOwner as a, isAcpJsonRpcMessage as at, lockOwnerPid as b, extractAcpError as bt, queueRequestExceedsLimit as c, parseJsonRpcErrorMessage as ct, readQueueOwnerRecord as d, DEFAULT_EVENT_SEGMENT_MAX_BYTES as dt, recordPerfDuration as et, refreshQueueOwnerLease as f, defaultSessionEventLog as ft, tryAcquireQueueOwnerLease as g, sessionEventSegmentPath as gt, terminateQueueOwnerForSession as h, sessionEventLockPath as ht, trySetModeOnRunningOwner as i, extractSessionUpdateNotification as it, findSession as j, SESSION_RECORD_SCHEMA as jt, absolutePath as k, PERMISSION_MODES as kt, parseQueueRequest as l, parsePermissionNotice as lt, terminateProcess as m, sessionEventActivePath as mt, tryCloseSessionOnRunningOwner as n, setPerfGauge as nt, trySubmitToRunningOwner as o, isAcpMessageObject as ot, releaseQueueOwnerLease as p, sessionBaseDir as pt, SessionJournalReader as q, SessionResumeRequiredError as qt, trySetConfigOptionOnRunningOwner as r, startPerfTimer as rt, queueRequestByteLimit as s, isSessionUpdateNotification as st, tryCancelOnRunningOwner as t, resetPerfMetrics as tt, probeQueueOwnerHealth as u, parsePromptStopReason as ut, QueueLeaseGuardSettlementError as v, writePrivateJsonFile as vt, mergePromptSourceWithText as w, EXIT_CODES as wt, ProcessDescendants as x, formatUnknownErrorMessage as xt, createLockOwner as y, isProcessAlive as yt, resolveSessionRecord as z, GeminiAcpStartupTimeoutError as zt };
3865
4344
 
3866
- //# sourceMappingURL=ipc-fMgj5vrO.js.map
4345
+ //# sourceMappingURL=ipc-DAApJ5mi.js.map