acpx 0.19.0 → 0.19.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.
Files changed (40) 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-D-s_eTUy.js} +5 -5
  5. package/dist/{cli-Bmki9cj5.js.map → cli-D-s_eTUy.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-DdxEaeZo.js} +33 -20
  11. package/dist/flows-DdxEaeZo.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-HjxSBsK0.js} +5 -5
  16. package/dist/{invocation-options-DRzxHZmr.js.map → invocation-options-HjxSBsK0.js.map} +1 -1
  17. package/dist/{ipc-fMgj5vrO.js → ipc-D0ZMQ0q9.js} +1393 -86
  18. package/dist/ipc-D0ZMQ0q9.js.map +1 -0
  19. package/dist/{output-D6kDs8BN.js → output-GQnhkJMU.js} +20 -12
  20. package/dist/output-GQnhkJMU.js.map +1 -0
  21. package/dist/{queue-owner-runtime-CLwjMYNL.js → queue-owner-runtime-W0lly6ap.js} +344 -777
  22. package/dist/queue-owner-runtime-W0lly6ap.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-_8yxQ6ba.js} +4 -4
  28. package/dist/{session-watch-BfdIo-n4.js.map → session-watch-_8yxQ6ba.js.map} +1 -1
  29. package/dist/watch-C0mwpJcN.js +85 -0
  30. package/dist/watch-C0mwpJcN.js.map +1 -0
  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
  39. package/dist/watch-DH-zXQH2.js +0 -32
  40. package/dist/watch-DH-zXQH2.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";
@@ -11,6 +11,7 @@ import { createHash, randomInt, randomUUID } from "node:crypto";
11
11
  import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
12
12
  import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
13
13
  import net from "node:net";
14
+ import { resolveSecureTempRoot } from "@openclaw/fs-safe/secure-temp-root";
14
15
  //#region src/errors.ts
15
16
  var AcpxOperationalError = class extends Error {
16
17
  outputCode;
@@ -231,12 +232,12 @@ const SESSION_RECORD_SCHEMA = "acpx.session.v1";
231
232
  //#endregion
232
233
  //#region src/acp/error-shapes.ts
233
234
  const RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
234
- function asRecord$4(value) {
235
+ function asRecord$5(value) {
235
236
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
236
237
  return value;
237
238
  }
238
239
  function toAcpErrorPayload(value) {
239
- const record = asRecord$4(value);
240
+ const record = asRecord$5(value);
240
241
  if (!record) return;
241
242
  if (typeof record.code !== "number" || !Number.isFinite(record.code)) return;
242
243
  if (typeof record.message !== "string" || record.message.length === 0) return;
@@ -250,7 +251,7 @@ function extractAcpErrorInternal(value, depth) {
250
251
  if (depth > 5) return;
251
252
  const direct = toAcpErrorPayload(value);
252
253
  if (direct) return direct;
253
- const record = asRecord$4(value);
254
+ const record = asRecord$5(value);
254
255
  if (!record) return;
255
256
  return extractNestedAcpError(record, depth);
256
257
  }
@@ -285,7 +286,7 @@ function hasSessionNotFoundHint(value, depth = 0) {
285
286
  if (depth > 4) return false;
286
287
  if (isSessionNotFoundText(value)) return true;
287
288
  if (Array.isArray(value)) return value.some((entry) => hasSessionNotFoundHint(entry, depth + 1));
288
- const record = asRecord$4(value);
289
+ const record = asRecord$5(value);
289
290
  if (!record) return false;
290
291
  return Object.values(record).some((entry) => hasSessionNotFoundHint(entry, depth + 1));
291
292
  }
@@ -302,6 +303,136 @@ function isAcpResourceNotFoundError(error) {
302
303
  return isSessionNotFoundText(formatUnknownErrorMessage(error));
303
304
  }
304
305
  //#endregion
306
+ //#region src/acp/error-normalization.ts
307
+ const AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
308
+ const QUERY_CLOSED_BEFORE_RESPONSE_DETAIL = "query closed before response received";
309
+ function asRecord$4(value) {
310
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
311
+ return value;
312
+ }
313
+ function isAuthRequiredMessage(value) {
314
+ if (!value) return false;
315
+ const normalized = value.toLowerCase();
316
+ return [
317
+ "auth required",
318
+ "authentication required",
319
+ "authorization required",
320
+ "credential required",
321
+ "credentials required",
322
+ "token required",
323
+ "login required"
324
+ ].some((needle) => normalized.includes(needle));
325
+ }
326
+ function isAcpAuthRequiredPayload(acp) {
327
+ if (!acp) return false;
328
+ if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) return false;
329
+ if (isAuthRequiredMessage(acp.message)) return true;
330
+ const data = asRecord$4(acp.data);
331
+ if (!data) return false;
332
+ return hasAuthRequiredData(data);
333
+ }
334
+ function hasAuthRequiredData(data) {
335
+ return data.authRequired === true || hasNonEmptyString(data.methodId) || hasNonEmptyArray(data.methods);
336
+ }
337
+ function hasNonEmptyString(value) {
338
+ return typeof value === "string" && value.trim().length > 0;
339
+ }
340
+ function hasNonEmptyArray(value) {
341
+ return Array.isArray(value) && value.length > 0;
342
+ }
343
+ function isOutputErrorCode$1(value) {
344
+ return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
345
+ }
346
+ function isOutputErrorOrigin$1(value) {
347
+ return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
348
+ }
349
+ function readOutputErrorMeta(error) {
350
+ const record = asRecord$4(error);
351
+ if (!record) return {};
352
+ return {
353
+ outputCode: isOutputErrorCode$1(record.outputCode) ? record.outputCode : void 0,
354
+ detailCode: typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0,
355
+ origin: isOutputErrorOrigin$1(record.origin) ? record.origin : void 0,
356
+ retryable: typeof record.retryable === "boolean" ? record.retryable : void 0,
357
+ acp: extractAcpError(record.acp)
358
+ };
359
+ }
360
+ function isTimeoutLike(error) {
361
+ return error instanceof Error && error.name === "TimeoutError";
362
+ }
363
+ function isNoSessionLike(error) {
364
+ return error instanceof Error && error.name === "NoSessionError";
365
+ }
366
+ function isUsageLike(error) {
367
+ if (!(error instanceof Error)) return false;
368
+ return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$4(error)?.code === "commander.invalidArgument";
369
+ }
370
+ function formatErrorMessage(error) {
371
+ return formatUnknownErrorMessage(error);
372
+ }
373
+ function isAcpQueryClosedBeforeResponseError(error) {
374
+ const acp = extractAcpError(error);
375
+ if (!acp || acp.code !== -32603) return false;
376
+ const details = asRecord$4(acp.data)?.details;
377
+ if (typeof details !== "string") return false;
378
+ return details.toLowerCase().includes(QUERY_CLOSED_BEFORE_RESPONSE_DETAIL);
379
+ }
380
+ function mapErrorCode(error) {
381
+ if (error instanceof PermissionPromptUnavailableError) return "PERMISSION_PROMPT_UNAVAILABLE";
382
+ if (error instanceof PermissionDeniedError) return "PERMISSION_DENIED";
383
+ if (isTimeoutLike(error)) return "TIMEOUT";
384
+ if (isNoSessionLike(error) || isAcpResourceNotFoundError(error)) return "NO_SESSION";
385
+ if (isUsageLike(error)) return "USAGE";
386
+ }
387
+ function normalizeOutputError(error, options = {}) {
388
+ const meta = readOutputErrorMeta(error);
389
+ const code = resolveOutputErrorCode(error, options, meta);
390
+ const acp = options.acp ?? meta.acp ?? extractAcpError(error);
391
+ return {
392
+ code,
393
+ message: formatErrorMessage(error),
394
+ detailCode: resolveDetailCode(error, acp, options, meta),
395
+ origin: meta.origin ?? options.origin,
396
+ retryable: meta.retryable ?? options.retryable,
397
+ acp
398
+ };
399
+ }
400
+ function resolveOutputErrorCode(error, options, meta) {
401
+ const code = meta.outputCode ?? mapErrorCode(error) ?? options.defaultCode ?? "RUNTIME";
402
+ if (code === "RUNTIME" && isAcpResourceNotFoundError(error)) return "NO_SESSION";
403
+ return code;
404
+ }
405
+ function resolveDetailCode(error, acp, options, meta) {
406
+ return meta.detailCode ?? options.detailCode ?? (error instanceof AuthPolicyError || isAcpAuthRequiredPayload(acp) ? "AUTH_REQUIRED" : void 0);
407
+ }
408
+ /**
409
+ * Returns true when an error from `client.prompt()` looks transient and
410
+ * can reasonably be retried (e.g. model-API 400/500, network hiccups that
411
+ * surface as ACP internal errors).
412
+ *
413
+ * Errors that are definitively non-recoverable (auth, missing session,
414
+ * invalid params, timeout, permission) return false.
415
+ */
416
+ function isRetryablePromptError(error) {
417
+ if (isNonRetryablePromptError(error)) return false;
418
+ const acp = extractAcpError(error);
419
+ if (!acp) return false;
420
+ return acp.code === -32603 || acp.code === -32700;
421
+ }
422
+ function isNonRetryablePromptError(error) {
423
+ return error instanceof PermissionDeniedError || error instanceof PermissionPromptUnavailableError || isTimeoutLike(error) || isNoSessionLike(error) || isUsageLike(error);
424
+ }
425
+ function exitCodeForOutputErrorCode(code) {
426
+ switch (code) {
427
+ case "USAGE": return EXIT_CODES.USAGE;
428
+ case "TIMEOUT": return EXIT_CODES.TIMEOUT;
429
+ case "NO_SESSION": return EXIT_CODES.NO_SESSION;
430
+ case "PERMISSION_DENIED":
431
+ case "PERMISSION_PROMPT_UNAVAILABLE": return EXIT_CODES.PERMISSION_DENIED;
432
+ default: return EXIT_CODES.ERROR;
433
+ }
434
+ }
435
+ //#endregion
305
436
  //#region src/process-liveness.ts
306
437
  function isProcessAlive(pid) {
307
438
  if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
@@ -686,7 +817,15 @@ function parseCursor(recordId, cursor) {
686
817
  return parsed.data[1];
687
818
  }
688
819
  function fileIdentity(stat) {
689
- return `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
820
+ return `${stat.dev}:${stat.ino}:${stat.birthtimeNs}`;
821
+ }
822
+ async function pathIdentity(filePath) {
823
+ try {
824
+ return fileIdentity(await fs$1.lstat(filePath, { bigint: true }));
825
+ } catch (error) {
826
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
827
+ throw error;
828
+ }
690
829
  }
691
830
  function emptySegment() {
692
831
  return {
@@ -718,12 +857,9 @@ var SessionJournalReader = class {
718
857
  const segments = [];
719
858
  try {
720
859
  const paths = await this.openPresentSegments(segments);
721
- const current = await Promise.all(paths.map((filePath) => statRegularFile(filePath)));
860
+ const current = await Promise.all(paths.map(pathIdentity));
722
861
  const identities = new Map(segments.map((entry) => [entry.filePath, entry.identity]));
723
- if (paths.every((filePath, index) => {
724
- const entry = current[index];
725
- return (entry.missing ? void 0 : fileIdentity(entry.stat)) === identities.get(filePath);
726
- })) return segments;
862
+ if (paths.every((filePath, index) => current[index] === identities.get(filePath))) return segments;
727
863
  } catch (error) {
728
864
  if (!retryableSnapshotError(error)) {
729
865
  await this.closeSnapshot(segments);
@@ -744,11 +880,16 @@ var SessionJournalReader = class {
744
880
  symlinks: "reject",
745
881
  nonBlockingRead: true
746
882
  });
747
- return {
748
- filePath,
749
- opened,
750
- identity: fileIdentity(opened.stat)
751
- };
883
+ try {
884
+ return {
885
+ filePath,
886
+ opened,
887
+ identity: fileIdentity(await opened.handle.stat({ bigint: true }))
888
+ };
889
+ } catch (error) {
890
+ await opened[Symbol.asyncDispose]();
891
+ throw error;
892
+ }
752
893
  }));
753
894
  let failure;
754
895
  for (const result of results) if (result.status === "fulfilled") segments.push(result.value);
@@ -978,6 +1119,7 @@ async function* iterateJournal(record, options) {
978
1119
  let after = options.cursor === void 0 ? -1 : parseCursor(record.acpxRecordId, options.cursor);
979
1120
  const reader = new SessionJournalReader(record);
980
1121
  let first = true;
1122
+ const observation = {};
981
1123
  for (;;) {
982
1124
  if (signal.aborted) return;
983
1125
  const snapshot = await settleUnlessAborted(reader.read(signal, {
@@ -992,16 +1134,34 @@ async function* iterateJournal(record, options) {
992
1134
  after = sequence;
993
1135
  yield event;
994
1136
  }
995
- if (!await continueAfterPage(record, snapshot, options)) return;
1137
+ if (!await continueAfterPage(record, snapshot, options, observation)) return;
996
1138
  }
997
1139
  }
998
- async function continueAfterPage(record, snapshot, options) {
1140
+ async function continueAfterPage(record, snapshot, options, observation) {
1141
+ if (options.signal.aborted || confirmsTerminalObservation(snapshot, observation)) return false;
999
1142
  if (!snapshot.hasMore && options.continueWatching) {
1000
- if (!await settleUnlessAborted(options.continueWatching(record, snapshot.requestId), options.signal)) return false;
1143
+ const decision = await settleUnlessAborted(options.continueWatching(record, snapshot.requestId), options.signal).catch((error) => {
1144
+ if (error instanceof SessionWatchError && error.code === "WATCH_OUTCOME_UNKNOWN") return error;
1145
+ throw error;
1146
+ });
1147
+ if (decision !== true) {
1148
+ observation.terminal = {
1149
+ requestId: snapshot.requestId,
1150
+ error: decision instanceof SessionWatchError ? decision : void 0
1151
+ };
1152
+ return !options.signal.aborted;
1153
+ }
1001
1154
  }
1002
1155
  await settleUnlessAborted(setTimeout$1(snapshot.hasMore ? 0 : 100, void 0, { signal: options.signal }), options.signal);
1003
1156
  return !options.signal.aborted;
1004
1157
  }
1158
+ function confirmsTerminalObservation(snapshot, observation) {
1159
+ const terminal = observation.terminal;
1160
+ delete observation.terminal;
1161
+ if (!terminal || snapshot.events.length > 0 || snapshot.hasMore || snapshot.requestId !== terminal.requestId) return false;
1162
+ if (terminal.error) throw terminal.error;
1163
+ return true;
1164
+ }
1005
1165
  function assertRetainedCursor(after, snapshot, first) {
1006
1166
  if (after < 0) return;
1007
1167
  if (after > snapshot.sequence) throw new SessionWatchError(first ? "WATCH_CURSOR_FUTURE" : "WATCH_CURSOR_EXPIRED", "Cursor is beyond the available session journal");
@@ -1727,17 +1887,18 @@ async function readSessionRecord(sessionId) {
1727
1887
  return;
1728
1888
  }
1729
1889
  }
1730
- function hasGitDirectory(dir) {
1890
+ function hasGitMarker(dir) {
1731
1891
  const gitPath = path.join(dir, ".git");
1732
1892
  try {
1733
- return statSync(gitPath).isDirectory();
1893
+ const marker = statSync(gitPath);
1894
+ return marker.isDirectory() || marker.isFile();
1734
1895
  } catch {
1735
1896
  return false;
1736
1897
  }
1737
1898
  }
1738
1899
  function isWithinBoundary(boundary, target) {
1739
1900
  const relative = path.relative(boundary, target);
1740
- return relative.length === 0 || !relative.startsWith("..") && !path.isAbsolute(relative);
1901
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
1741
1902
  }
1742
1903
  function absolutePath(value) {
1743
1904
  return path.resolve(value);
@@ -1746,7 +1907,7 @@ function findGitRepositoryRoot(startDir) {
1746
1907
  let current = absolutePath(startDir);
1747
1908
  const root = path.parse(current).root;
1748
1909
  for (;;) {
1749
- if (hasGitDirectory(current)) return current;
1910
+ if (hasGitMarker(current)) return current;
1750
1911
  if (current === root) return;
1751
1912
  const parent = path.dirname(current);
1752
1913
  if (parent === current) return;
@@ -2052,6 +2213,39 @@ const LINUX_PROCESS_QUERY = `
2052
2213
  if (error.code === 'ENOENT') return 'unsupported';
2053
2214
  throw error;
2054
2215
  });
2216
+ const prefix = async (name, size) => {
2217
+ const file = await fs.open(name, 'r');
2218
+ try {
2219
+ const buffer = Buffer.alloc(size);
2220
+ const { bytesRead } = await file.read(buffer, 0, size, 0);
2221
+ return buffer.subarray(0, bytesRead).toString('base64');
2222
+ } finally { await file.close(); }
2223
+ };
2224
+ const targets = async () => {
2225
+ if (targetPid !== 'all') {
2226
+ return { targetStat: await fs.readFile('/proc/' + targetPid + '/stat', 'utf8').catch(() => null) };
2227
+ }
2228
+ const required = new Set(JSON.parse(process.argv[3] || '[]'));
2229
+ const pids = new Set((await fs.readdir('/proc')).filter(pid => /^[1-9][0-9]*$/.test(pid)));
2230
+ for (const pid of required) pids.add(String(pid));
2231
+ const processStats = [];
2232
+ let bytes = 0;
2233
+ // Sequential reads keep proc I/O and retained memory bounded within this
2234
+ // single killable helper; never launch one helper or pending read per PID.
2235
+ for (const pid of [...pids].sort((a, b) => Number(a) - Number(b))) {
2236
+ let stat;
2237
+ try { stat = await fs.readFile('/proc/' + pid + '/stat', 'utf8'); }
2238
+ catch (error) {
2239
+ if (error.code === 'ENOENT' || error.code === 'ESRCH') continue;
2240
+ if (!required.has(Number(pid)) && (error.code === 'EACCES' || error.code === 'EPERM')) continue;
2241
+ throw error;
2242
+ }
2243
+ bytes += Buffer.byteLength(JSON.stringify(stat)) + 1;
2244
+ if (bytes > ${PROCESS_HELPER_MAX_BUFFER_BYTES} - 8192) throw new Error('Process table exceeds its byte limit');
2245
+ processStats.push(stat);
2246
+ }
2247
+ return { processStats, auxv: await prefix('/proc/self/auxv', 4096), elf: await prefix('/proc/self/exe', 6) };
2248
+ };
2055
2249
  Promise.all([
2056
2250
  fs.readFile('/proc/sys/kernel/random/boot_id', 'utf8'),
2057
2251
  fs.readlink('/proc/self/ns/pid'),
@@ -2059,12 +2253,12 @@ const LINUX_PROCESS_QUERY = `
2059
2253
  fs.readlink('/proc/' + observerPid + '/ns/pid'),
2060
2254
  timeNamespace(observerPid),
2061
2255
  fs.readFile('/proc/self/stat', 'utf8'),
2062
- fs.readFile('/proc/' + targetPid + '/stat', 'utf8').catch(() => null),
2256
+ targets(),
2063
2257
  ]).then(([bootId, pidNamespace, timeNamespace, observerPidNamespace,
2064
- observerTimeNamespace, selfStat, targetStat]) => {
2258
+ observerTimeNamespace, selfStat, target]) => {
2065
2259
  process.stdout.write(JSON.stringify({bootId: bootId.trim(), pidNamespace,
2066
2260
  timeNamespace, observerPidNamespace, observerTimeNamespace,
2067
- helperPid: process.pid, selfStat, targetStat}));
2261
+ helperPid: process.pid, selfStat, ...target}));
2068
2262
  }).catch(() => { process.exitCode = 1; });
2069
2263
  `;
2070
2264
  const POSIX_MONTHS = [
@@ -2146,31 +2340,48 @@ function parseLinuxStat(raw) {
2146
2340
  const pid = Number(prefix[1]);
2147
2341
  const fields = raw.slice(close + 2).trim().split(/\s+/u);
2148
2342
  const startTicks = fields[19];
2149
- if (!isPositivePid(pid) || !isStartTicks(startTicks) || !/^[RSDTtKWPI]$/.test(fields[0] ?? "")) return;
2150
- return {
2343
+ if (!isPositivePid(pid) || !isStartTicks(startTicks) || !/^[RSDTtKWPI]$/.test(fields[0])) return;
2344
+ const parentPid = Number(fields[1]);
2345
+ const groupPid = Number(fields[2]);
2346
+ return validProcessLinks(parentPid, groupPid) ? {
2151
2347
  pid,
2348
+ parentPid,
2349
+ groupPid,
2152
2350
  startTicks
2153
- };
2351
+ } : void 0;
2154
2352
  }
2155
- async function readLinuxObservation(pid, timeoutMs) {
2156
- const output = await runTimedExecFile(process.execPath, [
2353
+ function validProcessLinks(parentPid, groupPid) {
2354
+ return Number.isSafeInteger(parentPid) && parentPid >= 0 && Number.isSafeInteger(groupPid) && groupPid >= 0;
2355
+ }
2356
+ async function readLinuxQuery(pid, timeoutMs, requiredPids = []) {
2357
+ const args = [
2157
2358
  "--input-type=commonjs",
2158
2359
  "-e",
2159
2360
  LINUX_PROCESS_QUERY,
2160
2361
  String(pid),
2161
2362
  String(process.pid)
2162
- ], {
2363
+ ];
2364
+ if (pid === "all") args.push(JSON.stringify(requiredPids));
2365
+ const output = await runTimedExecFile(process.execPath, args, {
2163
2366
  timeoutMs,
2164
- maxBufferBytes: 8192,
2367
+ maxBufferBytes: pid === "all" ? PROCESS_HELPER_MAX_BUFFER_BYTES : 8192,
2165
2368
  env: {
2166
2369
  ...process.env,
2167
2370
  NODE_OPTIONS: ""
2168
2371
  }
2169
2372
  });
2170
- const raw = JSON.parse(output);
2373
+ return JSON.parse(output);
2374
+ }
2375
+ function verifiedLinuxScope(raw) {
2171
2376
  const scope = parseLinuxScope(raw);
2172
2377
  const self = parseLinuxStat(raw.selfStat);
2173
2378
  if (!scope || !self || self.pid !== raw.helperPid || scope.pidNamespace !== raw.observerPidNamespace || scope.timeNamespace !== raw.observerTimeNamespace) return;
2379
+ return scope;
2380
+ }
2381
+ async function readLinuxObservation(pid, timeoutMs) {
2382
+ const raw = await readLinuxQuery(pid, timeoutMs);
2383
+ const scope = verifiedLinuxScope(raw);
2384
+ if (!scope) return;
2174
2385
  const target = parseLinuxStat(raw.targetStat);
2175
2386
  return {
2176
2387
  scope,
@@ -2181,6 +2392,80 @@ async function readLinuxObservation(pid, timeoutMs) {
2181
2392
  } } : {}
2182
2393
  };
2183
2394
  }
2395
+ function linuxAuxv(raw) {
2396
+ if (typeof raw.elf !== "string" || typeof raw.auxv !== "string") throw new Error("Linux process clock is unavailable");
2397
+ const elf = Buffer.from(raw.elf, "base64");
2398
+ const auxv = Buffer.from(raw.auxv, "base64");
2399
+ 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");
2400
+ return {
2401
+ auxv,
2402
+ size: elf[4] === 1 ? 4 : 8,
2403
+ littleEndian: elf[5] === 1
2404
+ };
2405
+ }
2406
+ function linuxClockTicks(raw) {
2407
+ const { auxv, size, littleEndian } = linuxAuxv(raw);
2408
+ const word = (offset) => readAuxvWord(auxv, offset, size, littleEndian);
2409
+ for (let offset = 0; offset + size * 2 <= auxv.length; offset += size * 2) {
2410
+ const key = word(offset);
2411
+ if (key === 0n) break;
2412
+ if (key === 17n && word(offset + size) > 0n) return word(offset + size);
2413
+ }
2414
+ throw new Error("Linux process clock has no AT_CLKTCK");
2415
+ }
2416
+ function readAuxvWord(buffer, offset, size, littleEndian) {
2417
+ if (size === 8) return littleEndian ? buffer.readBigUInt64LE(offset) : buffer.readBigUInt64BE(offset);
2418
+ return BigInt(littleEndian ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset));
2419
+ }
2420
+ async function readLinuxProcessTable(timeoutMs, requiredPids, pid) {
2421
+ const raw = await readLinuxQuery("all", timeoutMs, requiredPids);
2422
+ const scope = verifiedLinuxScope(raw);
2423
+ if (!scope || !Array.isArray(raw.processStats)) throw new Error("Linux process table scope is unverified");
2424
+ const clockTicksPerSecond = linuxClockTicks(raw);
2425
+ const table = /* @__PURE__ */ new Map();
2426
+ for (const value of raw.processStats) {
2427
+ const stat = parseLinuxStat(value);
2428
+ if (stat && (pid === void 0 || stat.pid === pid)) table.set(stat.pid, {
2429
+ pid: stat.pid,
2430
+ parentPid: stat.parentPid,
2431
+ groupPid: stat.groupPid,
2432
+ birth: {
2433
+ kind: "linux-proc",
2434
+ ...scope,
2435
+ startTicks: stat.startTicks
2436
+ },
2437
+ clockTicksPerSecond
2438
+ });
2439
+ }
2440
+ return table;
2441
+ }
2442
+ function readLinuxExitClock() {
2443
+ let descriptor;
2444
+ try {
2445
+ descriptor = fs.openSync("/proc/uptime", "r");
2446
+ const buffer = Buffer.alloc(64);
2447
+ const bytes = fs.readSync(descriptor, buffer, 0, buffer.length, 0);
2448
+ const match = /^(\d+)\.(\d{2})\s/u.exec(buffer.toString("ascii", 0, bytes));
2449
+ return match ? {
2450
+ centiseconds: BigInt(match[1]) * 100n + BigInt(match[2]),
2451
+ timeNamespace: readOwnTimeNamespace()
2452
+ } : void 0;
2453
+ } catch {
2454
+ return;
2455
+ } finally {
2456
+ if (descriptor !== void 0) try {
2457
+ fs.closeSync(descriptor);
2458
+ } catch {}
2459
+ }
2460
+ }
2461
+ function readOwnTimeNamespace() {
2462
+ try {
2463
+ return fs.readlinkSync("/proc/self/ns/time");
2464
+ } catch (error) {
2465
+ if (error.code === "ENOENT") return "unsupported";
2466
+ throw error;
2467
+ }
2468
+ }
2184
2469
  function isPositivePid(pid) {
2185
2470
  return Number.isSafeInteger(pid) && pid > 0;
2186
2471
  }
@@ -2218,7 +2503,7 @@ function parseProcessTable(output) {
2218
2503
  pid,
2219
2504
  parentPid: Number(match[2]),
2220
2505
  groupPid: Number(match[3]),
2221
- birth
2506
+ birth: processBirthIdentity(birth)
2222
2507
  });
2223
2508
  }
2224
2509
  return table;
@@ -2227,7 +2512,7 @@ function processTableBirth(value) {
2227
2512
  if (process.platform !== "win32") return posixBirth(value);
2228
2513
  return isCanonicalBirth(value, "windows-creation") ? value : void 0;
2229
2514
  }
2230
- async function readProcessTable(timeoutMs, pid) {
2515
+ async function readProcessTable(timeoutMs, pid, requiredPids = []) {
2231
2516
  if (pid !== void 0 && (!Number.isSafeInteger(pid) || pid <= 0)) throw new Error("Process identity requires a positive PID");
2232
2517
  if (process.platform === "win32") return parseProcessTable(await runTimedExecFile(windowsPowerShellPath(), [
2233
2518
  "-NoLogo",
@@ -2239,6 +2524,7 @@ async function readProcessTable(timeoutMs, pid) {
2239
2524
  timeoutMs,
2240
2525
  windowsHide: true
2241
2526
  }));
2527
+ if (process.platform === "linux") return await readLinuxProcessTable(timeoutMs, requiredPids, pid);
2242
2528
  return parseProcessTable(await runTimedExecFile("ps", [
2243
2529
  ...pid === void 0 ? ["-e"] : ["-p", String(pid)],
2244
2530
  "-o",
@@ -2266,8 +2552,7 @@ async function probeProcessIdentity(pid, timeoutMs = IDENTITY_QUERY_TIMEOUT_MS$1
2266
2552
  }
2267
2553
  async function readNativeIdentity(pid, timeoutMs) {
2268
2554
  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;
2555
+ return (await readProcessTable(timeoutMs, pid)).get(pid)?.birth;
2271
2556
  }
2272
2557
  function processBirthIdentity(value) {
2273
2558
  return {
@@ -2314,6 +2599,138 @@ async function getOwnProcessIdentity() {
2314
2599
  return await ownIdentityPending;
2315
2600
  }
2316
2601
  //#endregion
2602
+ //#region src/acp/process-descendants.ts
2603
+ function hasStaleWindowsParent(identity, parent) {
2604
+ return identity.birth.kind === "windows-creation" && (parent?.birth.kind !== "windows-creation" || parent.birth.value > identity.birth.value);
2605
+ }
2606
+ function includeProcessDescendants(table, owned) {
2607
+ let expanded;
2608
+ do {
2609
+ expanded = false;
2610
+ for (const identity of table.values()) {
2611
+ if (hasStaleWindowsParent(identity, table.get(identity.parentPid))) continue;
2612
+ if (owned.has(identity.parentPid) && !owned.has(identity.pid)) {
2613
+ owned.add(identity.pid);
2614
+ expanded = true;
2615
+ }
2616
+ }
2617
+ } while (expanded);
2618
+ }
2619
+ /** Best-effort cleanup for descendants witnessed during this child launch. */
2620
+ var ProcessDescendants = class {
2621
+ child;
2622
+ identities = /* @__PURE__ */ new Map();
2623
+ rootBirth;
2624
+ pending;
2625
+ retired = false;
2626
+ ownProcessGroup;
2627
+ captureGroupAfterExit;
2628
+ groupExitedAt;
2629
+ onRootExit = () => {
2630
+ this.groupExitedAt = process.platform === "linux" ? readLinuxExitClock() ?? null : Date.now();
2631
+ };
2632
+ constructor(child, options = {}) {
2633
+ this.child = child;
2634
+ this.ownProcessGroup = process.platform !== "win32" && Boolean(options.ownProcessGroup);
2635
+ this.captureGroupAfterExit = this.ownProcessGroup && isChildProcessRunning(child);
2636
+ if (this.ownProcessGroup) child.once("exit", this.onRootExit);
2637
+ }
2638
+ capture(timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
2639
+ if (this.retired) return Promise.resolve(true);
2640
+ this.pending ??= this.readSnapshot(timeoutMs).finally(() => {
2641
+ this.pending = void 0;
2642
+ });
2643
+ return this.pending;
2644
+ }
2645
+ async readSnapshot(timeoutMs) {
2646
+ const deadline = performance.now() + timeoutMs;
2647
+ const rootWasRunning = isChildProcessRunning(this.child);
2648
+ const captureRootGroup = rootWasRunning || this.captureGroupAfterExit;
2649
+ if (!rootWasRunning) this.captureGroupAfterExit = false;
2650
+ try {
2651
+ const table = await readProcessTable(timeoutMs, void 0, this.requiredSnapshotPids(rootWasRunning));
2652
+ if (!this.retired) {
2653
+ table.delete(1);
2654
+ table.delete(process.pid);
2655
+ this.refresh(table, captureRootGroup);
2656
+ if (this.captureGroupAfterExit && !isChildProcessRunning(this.child)) return await this.readSnapshot(Math.max(1, deadline - performance.now()));
2657
+ }
2658
+ return true;
2659
+ } catch {
2660
+ if (!isChildProcessRunning(this.child)) this.captureGroupAfterExit = false;
2661
+ return false;
2662
+ }
2663
+ }
2664
+ requiredSnapshotPids(rootWasRunning) {
2665
+ return [...this.identities.keys(), ...rootWasRunning && this.child.pid ? [this.child.pid] : []];
2666
+ }
2667
+ refresh(table, rootWasRunning) {
2668
+ this.verifyScope(table);
2669
+ const owned = /* @__PURE__ */ new Set();
2670
+ for (const [pid, identity] of this.identities) {
2671
+ const observed = table.get(pid);
2672
+ if (observed && compareProcessBirthIdentity(identity.birth, observed.birth) === "matching") owned.add(pid);
2673
+ }
2674
+ this.includeRoot(table, owned);
2675
+ if (this.ownProcessGroup) this.includeProcessGroup(table, owned, rootWasRunning);
2676
+ includeProcessDescendants(table, owned);
2677
+ owned.delete(this.child.pid ?? 0);
2678
+ this.identities = new Map([...table].filter(([pid]) => owned.has(pid)));
2679
+ }
2680
+ verifyScope(table) {
2681
+ const expected = this.rootBirth ?? this.identities.values().next().value?.birth;
2682
+ const observation = table.values().next().value?.birth;
2683
+ if (expected && observation && compareProcessBirthIdentity(expected, observation) === "unknown") throw new Error("Descendant process scope changed");
2684
+ }
2685
+ includeRoot(table, owned) {
2686
+ const root = this.child.pid;
2687
+ const rootIdentity = root && table.get(root);
2688
+ if (rootIdentity && isChildProcessRunning(this.child)) {
2689
+ this.rootBirth ??= rootIdentity.birth;
2690
+ if (compareProcessBirthIdentity(this.rootBirth, rootIdentity.birth) === "matching") owned.add(rootIdentity.pid);
2691
+ }
2692
+ }
2693
+ includeProcessGroup(table, owned, rootWasRunning) {
2694
+ const root = this.child.pid;
2695
+ if (root && (rootWasRunning || [...owned].some((pid) => table.get(pid)?.groupPid === root))) {
2696
+ for (const identity of table.values()) if (identity.groupPid === root && this.startedBeforeRootExit(identity)) owned.add(identity.pid);
2697
+ }
2698
+ }
2699
+ startedBeforeRootExit(identity) {
2700
+ const cutoff = this.groupExitedAt;
2701
+ if (cutoff === void 0) return true;
2702
+ if (cutoff === null) return false;
2703
+ if (identity.birth.kind !== "linux-proc") return typeof cutoff === "number" && Date.parse(identity.birth.value) <= cutoff;
2704
+ if (typeof cutoff === "number" || identity.birth.timeNamespace !== cutoff.timeNamespace || !identity.clockTicksPerSecond) return false;
2705
+ return BigInt(identity.birth.startTicks) * 100n < (cutoff.centiseconds + 1n) * identity.clockTicksPerSecond;
2706
+ }
2707
+ async signal(signal, timeoutMs) {
2708
+ if (!await this.capture(timeoutMs) || this.retired) return;
2709
+ for (const pid of this.identities.keys()) try {
2710
+ process.kill(pid, signal);
2711
+ } catch {
2712
+ this.identities.delete(pid);
2713
+ }
2714
+ }
2715
+ hasTrackedProcesses() {
2716
+ return this.identities.size > 0;
2717
+ }
2718
+ async waitForExit(timeoutMs) {
2719
+ const deadline = performance.now() + timeoutMs;
2720
+ do {
2721
+ if (!await this.capture(Math.max(1, deadline - performance.now()))) return false;
2722
+ if (this.identities.size === 0) return true;
2723
+ await setTimeout$1(Math.min(100, Math.max(0, deadline - performance.now())));
2724
+ } while (performance.now() < deadline);
2725
+ return false;
2726
+ }
2727
+ retire() {
2728
+ this.retired = true;
2729
+ this.child.off("exit", this.onRootExit);
2730
+ this.identities.clear();
2731
+ }
2732
+ };
2733
+ //#endregion
2317
2734
  //#region src/session/lock-owner.ts
2318
2735
  const OBSERVATION_REUSE_MS = 1e3;
2319
2736
  const IDENTITY_QUERY_TIMEOUT_MS = 1e3;
@@ -2377,6 +2794,145 @@ function queueSocketPath(sessionId, homeDir = os.homedir()) {
2377
2794
  return path.join(queueSocketBaseDir(homeDir) ?? "/tmp", `${key}.sock`);
2378
2795
  }
2379
2796
  //#endregion
2797
+ //#region src/session/queue/retirement-receipt.ts
2798
+ const MAX_RETIREMENT_MEMBERS = 4096;
2799
+ const MAX_RETIREMENT_BYTES = 1048576;
2800
+ function hasQueueRetirementReceipt(value) {
2801
+ return value !== null && typeof value === "object" && Object.hasOwn(value, "retirement");
2802
+ }
2803
+ function parseWitness(raw) {
2804
+ if (!raw || typeof raw !== "object") return;
2805
+ const value = raw;
2806
+ const processIdentity = parseProcessBirthIdentity(value.processIdentity);
2807
+ if (typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 1 || processIdentity?.kind !== "windows-creation") return;
2808
+ return {
2809
+ pid: value.pid,
2810
+ processIdentity
2811
+ };
2812
+ }
2813
+ function parseQueueRetirementReceipt(raw, owner) {
2814
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
2815
+ const value = raw;
2816
+ const root = parseReceiptRoot(value, owner);
2817
+ if (!root) return null;
2818
+ const descendants = parseDescendants(value.descendants, root.pid);
2819
+ if (!descendants) return null;
2820
+ try {
2821
+ if (Buffer.byteLength(JSON.stringify(raw)) > MAX_RETIREMENT_BYTES) return null;
2822
+ } catch {
2823
+ return null;
2824
+ }
2825
+ return {
2826
+ ownerGeneration: owner.ownerGeneration,
2827
+ root,
2828
+ descendants
2829
+ };
2830
+ }
2831
+ function parseReceiptRoot(value, owner) {
2832
+ const root = parseWitness(value.root);
2833
+ if (!root || root.pid !== owner.pid || value.ownerGeneration !== owner.ownerGeneration || compareProcessBirthIdentity(owner.processIdentity, root.processIdentity) !== "matching") return;
2834
+ return root;
2835
+ }
2836
+ function parseDescendants(raw, rootPid) {
2837
+ if (!Array.isArray(raw) || raw.length >= MAX_RETIREMENT_MEMBERS) return;
2838
+ const pids = /* @__PURE__ */ new Set([rootPid]);
2839
+ const witnesses = [];
2840
+ for (const value of raw) {
2841
+ const witness = parseWitness(value);
2842
+ if (!witness || pids.has(witness.pid)) return;
2843
+ pids.add(witness.pid);
2844
+ witnesses.push(witness);
2845
+ }
2846
+ return witnesses;
2847
+ }
2848
+ function queueRetirementIncomplete() {
2849
+ 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.", {
2850
+ detailCode: "QUEUE_OWNER_RETIREMENT_INCOMPLETE",
2851
+ origin: "queue",
2852
+ retryable: true
2853
+ });
2854
+ }
2855
+ function retirementTimeRemaining(deadline, maximumMs = 2e3) {
2856
+ const remaining = deadline - performance.now();
2857
+ if (remaining <= 0) throw queueRetirementIncomplete();
2858
+ return Math.min(maximumMs, remaining);
2859
+ }
2860
+ async function readRetirementSnapshot(deadline) {
2861
+ if (process.platform !== "win32") throw queueRetirementIncomplete();
2862
+ return await readProcessTable(retirementTimeRemaining(deadline)).catch(() => {
2863
+ throw queueRetirementIncomplete();
2864
+ });
2865
+ }
2866
+ function observeRetirementSnapshot(witness, table) {
2867
+ if (process.platform !== "win32" || !isWindowsRetirementIdentity(witness.processIdentity)) return "unknown";
2868
+ const observed = table?.get(witness.pid);
2869
+ const comparison = observed ? compareProcessBirthIdentity(witness.processIdentity, observed.birth) : "unknown";
2870
+ if (comparison === "different" || isProcessDefinitelyDead(witness.pid)) return "gone";
2871
+ return witness.pid === process.pid ? "unknown" : comparison;
2872
+ }
2873
+ function isWindowsRetirementIdentity(identity) {
2874
+ return identity === void 0 || identity.kind === "windows-creation";
2875
+ }
2876
+ function captureQueueRetirementReceipt(owner, previous, rootRunning, snapshot) {
2877
+ const root = parseWitness({
2878
+ pid: owner.pid,
2879
+ processIdentity: owner.processIdentity
2880
+ });
2881
+ if (!root || process.platform !== "win32") throw queueRetirementIncomplete();
2882
+ const saved = previous ? previous.descendants : [];
2883
+ const table = new Map(snapshot);
2884
+ const owned = matchingSnapshotPids(table, rootRunning ? [root, ...saved] : saved);
2885
+ if (rootRunning && !owned.has(root.pid)) throw queueRetirementIncomplete();
2886
+ table.delete(1);
2887
+ table.delete(process.pid);
2888
+ includeProcessDescendants(table, owned);
2889
+ owned.delete(root.pid);
2890
+ const receipt = {
2891
+ ownerGeneration: owner.ownerGeneration,
2892
+ root,
2893
+ descendants: unsettledSnapshotWitnesses(snapshot, mergeSnapshotDescendants(table, owned, saved))
2894
+ };
2895
+ if (!parseQueueRetirementReceipt(receipt, owner)) throw queueRetirementIncomplete();
2896
+ return receipt;
2897
+ }
2898
+ function unsettledSnapshotWitnesses(table, witnesses) {
2899
+ return witnesses.filter((witness) => {
2900
+ const observed = table.get(witness.pid);
2901
+ return !observed || compareProcessBirthIdentity(witness.processIdentity, observed.birth) !== "different";
2902
+ });
2903
+ }
2904
+ function matchingSnapshotPids(table, witnesses) {
2905
+ const owned = /* @__PURE__ */ new Set();
2906
+ for (const witness of witnesses) {
2907
+ const observed = table.get(witness.pid);
2908
+ if (observed && compareProcessBirthIdentity(witness.processIdentity, observed.birth) === "matching") owned.add(witness.pid);
2909
+ }
2910
+ return owned;
2911
+ }
2912
+ function mergeSnapshotDescendants(table, owned, saved) {
2913
+ const descendants = new Map(saved.map((witness) => [witness.pid, witness]));
2914
+ for (const pid of owned) {
2915
+ const observed = table.get(pid);
2916
+ if (observed) descendants.set(pid, {
2917
+ pid,
2918
+ processIdentity: observed.birth
2919
+ });
2920
+ }
2921
+ return [...descendants.values()];
2922
+ }
2923
+ async function observeRetirementWitness(witness, deadline) {
2924
+ if (witness.pid === process.pid) throw queueRetirementIncomplete();
2925
+ return await observeProcessIncarnation(witness.pid, witness.processIdentity, retirementTimeRemaining(deadline));
2926
+ }
2927
+ async function assertQueueRetirementComplete(receipt, deadline) {
2928
+ if (receipt === null) throw queueRetirementIncomplete();
2929
+ const witnesses = receipt?.descendants ?? [];
2930
+ if (witnesses.length === 0) return;
2931
+ const live = witnesses.filter((witness) => observeRetirementSnapshot(witness, void 0) !== "gone");
2932
+ const table = live.length > 0 ? await readRetirementSnapshot(deadline) : void 0;
2933
+ for (const witness of live) if (observeRetirementSnapshot(witness, table) !== "gone") throw queueRetirementIncomplete();
2934
+ }
2935
+ //#endregion
2380
2936
  //#region src/session/queue/lease-mutation.ts
2381
2937
  const GUARD_WAIT_MS = 2e3;
2382
2938
  const localMutations = /* @__PURE__ */ new Map();
@@ -2432,7 +2988,7 @@ async function rollbackReservation(lockPath, expected) {
2432
2988
  } catch {
2433
2989
  return;
2434
2990
  }
2435
- if (matchesReservation(value, expected)) await fs$1.unlink(lockPath);
2991
+ if (matchesReservation(value, expected) && !hasQueueRetirementReceipt(value)) await fs$1.unlink(lockPath);
2436
2992
  } catch (error) {
2437
2993
  if (error.code !== "ENOENT") throw error;
2438
2994
  }
@@ -2537,7 +3093,7 @@ function parseQueueOwnerRecord(raw) {
2537
3093
  const record = raw;
2538
3094
  if (!hasValidQueueOwnerRecordFields(record)) return null;
2539
3095
  const processIdentity = parseProcessBirthIdentity(record.processIdentity);
2540
- return {
3096
+ const owner = {
2541
3097
  pid: record.pid,
2542
3098
  sessionId: record.sessionId,
2543
3099
  socketPath: record.socketPath,
@@ -2550,6 +3106,8 @@ function parseQueueOwnerRecord(raw) {
2550
3106
  ...typeof record.mcpConfigPath === "string" ? { mcpConfigPath: record.mcpConfigPath } : {},
2551
3107
  ...typeof record.mcpConfigFingerprint === "string" ? { mcpConfigFingerprint: record.mcpConfigFingerprint } : {}
2552
3108
  };
3109
+ if (hasQueueRetirementReceipt(record)) owner.retirement = parseQueueRetirementReceipt(record.retirement, owner);
3110
+ return owner;
2553
3111
  }
2554
3112
  function parseQueueOwnerCapabilities(record) {
2555
3113
  return {
@@ -2611,20 +3169,21 @@ async function removeSocketFile(socketPath) {
2611
3169
  }
2612
3170
  }
2613
3171
  async function waitForProcessExit(pid, timeoutMs, hasExited) {
2614
- const deadline = Date.now() + Math.max(0, timeoutMs);
2615
- while (Date.now() <= deadline) {
3172
+ const deadline = performance.now() + Math.max(0, timeoutMs);
3173
+ while (performance.now() <= deadline) {
2616
3174
  if (hasExited(pid)) return true;
2617
3175
  await waitMs(PROCESS_POLL_MS);
2618
3176
  }
2619
3177
  return hasExited(pid);
2620
3178
  }
2621
- async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent, capturedIdentity) {
3179
+ async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent, capturedIdentity, deadline = retirementDeadline()) {
2622
3180
  await withQueueLeaseMutation(sessionId, async () => {
2623
- await cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent);
3181
+ await cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent, deadline);
2624
3182
  }, { capturedIdentity });
2625
3183
  }
2626
- async function cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent) {
3184
+ async function cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent, deadline = retirementDeadline()) {
2627
3185
  if (!await isCurrent()) return;
3186
+ await assertQueueRetirementComplete((await readQueueOwnerRecord(sessionId))?.retirement, deadline);
2628
3187
  await removeSocketFile(socketPath).catch(() => {});
2629
3188
  if (!await isCurrent()) return;
2630
3189
  await fs$1.unlink(queueLockFilePath(sessionId)).catch((error) => {
@@ -2640,6 +3199,9 @@ function matchesQueueOwner(current, expected) {
2640
3199
  function ownerIsAlive(owner) {
2641
3200
  return owner.pid === process.pid || isProcessAlive(owner.pid);
2642
3201
  }
3202
+ function ownerAcceptsWork(owner) {
3203
+ return owner.retirement === void 0 && ownerIsAlive(owner) && !isQueueOwnerHeartbeatStale(owner);
3204
+ }
2643
3205
  async function readQueueOwnerRecord(sessionId) {
2644
3206
  const lockPath = queueLockFilePath(sessionId);
2645
3207
  try {
@@ -2665,7 +3227,7 @@ function dispatchSignal(pid, signal) {
2665
3227
  return false;
2666
3228
  }
2667
3229
  }
2668
- async function dispatchQueueOwnerSignal(pid, signal) {
3230
+ async function dispatchQueueOwnerSignal(pid, signal, deadline) {
2669
3231
  if (process.platform !== "win32") return dispatchSignal(pid, signal);
2670
3232
  const systemRoot = process.env.SystemRoot;
2671
3233
  if (!systemRoot || !path.win32.isAbsolute(systemRoot)) throw new Error("Windows queue-owner cleanup requires an absolute SystemRoot directory");
@@ -2676,15 +3238,15 @@ async function dispatchQueueOwnerSignal(pid, signal) {
2676
3238
  "/T",
2677
3239
  "/F"
2678
3240
  ], {
2679
- timeoutMs: PROCESS_SIGTERM_GRACE_MS,
3241
+ timeoutMs: retirementTimeRemaining(deadline, PROCESS_SIGTERM_GRACE_MS),
2680
3242
  windowsHide: true
2681
3243
  });
2682
3244
  return true;
2683
3245
  }
2684
- async function terminateWithDispatch(pid, dispatch, hasExited) {
3246
+ async function terminateWithDispatch(pid, dispatch, hasExited, deadline = Infinity) {
2685
3247
  for (const [signal, graceMs] of [["SIGTERM", PROCESS_SIGTERM_GRACE_MS], ["SIGKILL", PROCESS_SIGKILL_GRACE_MS]]) {
2686
3248
  if (!await dispatch(signal)) return false;
2687
- if (await waitForProcessExit(pid, graceMs, hasExited)) return true;
3249
+ if (await waitForProcessExit(pid, Math.min(graceMs, deadline - performance.now()), hasExited)) return true;
2688
3250
  }
2689
3251
  return false;
2690
3252
  }
@@ -2692,10 +3254,10 @@ async function resolveUsableQueueOwner(sessionId, owner) {
2692
3254
  await settlePendingQueueLeaseGuard(sessionId);
2693
3255
  const observed = await readQueueOwnerRecord(sessionId);
2694
3256
  if (!matchesQueueOwner(observed, owner)) return;
2695
- if (ownerIsAlive(observed) && !isQueueOwnerHeartbeatStale(observed)) return observed;
3257
+ if (ownerAcceptsWork(observed)) return observed;
2696
3258
  await terminateQueueOwnerForSession(sessionId, observed, true);
2697
3259
  const current = await readQueueOwnerRecord(sessionId);
2698
- return matchesQueueOwner(current, observed) && ownerIsAlive(current) && !isQueueOwnerHeartbeatStale(current) ? current : void 0;
3260
+ return matchesQueueOwner(current, observed) && ownerAcceptsWork(current) ? current : void 0;
2699
3261
  }
2700
3262
  async function tryAcquireQueueOwnerLease(sessionId, mcpConfigOrNowIsoFactory, nowIsoFactory = nowIso) {
2701
3263
  const { mcpConfigPath, clock } = resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory);
@@ -2806,9 +3368,13 @@ function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso) {
2806
3368
  if (lease.released) return Promise.resolve();
2807
3369
  const update = lease.updates.then(async () => {
2808
3370
  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);
3371
+ const current = await readQueueOwnerRecord(lease.sessionId);
3372
+ if (!matchesQueueOwner(current, lease)) return;
3373
+ if (current.retirement === null) throw queueRetirementIncomplete();
3374
+ await replaceQueueOwnerRecord({
3375
+ ...current,
3376
+ heartbeatAt: nowIsoFactory(),
3377
+ queueDepth: Math.max(0, Math.round(options.queueDepth))
2812
3378
  });
2813
3379
  }, { capturedIdentity: lease });
2814
3380
  });
@@ -2816,8 +3382,8 @@ function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso) {
2816
3382
  return update;
2817
3383
  }
2818
3384
  async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
2819
- const payload = JSON.stringify({
2820
- pid: process.pid,
3385
+ await stagePreparedQueueOwnerRecord({
3386
+ pid: lease.pid,
2821
3387
  sessionId: lease.sessionId,
2822
3388
  socketPath: lease.socketPath,
2823
3389
  createdAt: lease.createdAt,
@@ -2830,9 +3396,17 @@ async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
2830
3396
  persistsControlState: true,
2831
3397
  ...lease.mcpConfigPath ? { mcpConfigPath: lease.mcpConfigPath } : {},
2832
3398
  ...lease.mcpConfigFingerprint ? { mcpConfigFingerprint: lease.mcpConfigFingerprint } : {}
2833
- }, null, 2);
3399
+ }, publish);
3400
+ }
3401
+ async function replaceQueueOwnerRecord(owner) {
3402
+ await stagePreparedQueueOwnerRecord(owner, async (tempPath) => {
3403
+ await fs$1.rename(tempPath, queueLockFilePath(owner.sessionId));
3404
+ });
3405
+ }
3406
+ async function stagePreparedQueueOwnerRecord(owner, publish) {
3407
+ const payload = JSON.stringify(owner, null, 2);
2834
3408
  await withTempFile({
2835
- rootDir: path.dirname(lease.lockPath),
3409
+ rootDir: path.dirname(queueLockFilePath(owner.sessionId)),
2836
3410
  prefix: "owner",
2837
3411
  fileName: "lease"
2838
3412
  }, async (tempPath) => {
@@ -2856,40 +3430,166 @@ function unverifiedQueueOwnerError(owner) {
2856
3430
  retryable: true
2857
3431
  });
2858
3432
  }
2859
- async function dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement) {
2860
- return await withQueueLeaseMutation(owner.sessionId, async () => {
2861
- const current = await readQueueOwnerRecord(owner.sessionId);
2862
- 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);
2865
- if (identity !== "matching") {
2866
- retirement.gone = identity === "gone";
2867
- retirement.unverified = identity === "unknown";
2868
- return false;
2869
- }
2870
- return await dispatchQueueOwnerSignal(owner.pid, signal);
2871
- });
3433
+ function retirementDeadline() {
3434
+ return performance.now() + PROCESS_SIGTERM_GRACE_MS + PROCESS_SIGKILL_GRACE_MS;
2872
3435
  }
2873
- async function retireQueueOwner(owner, requireStale) {
2874
- const deadline = performance.now() + PROCESS_SIGTERM_GRACE_MS + PROCESS_SIGKILL_GRACE_MS;
2875
- const retirement = {
3436
+ async function createQueueOwnerRetirement(deadline) {
3437
+ const snapshot = process.platform === "win32" ? await readRetirementSnapshot(deadline).catch(() => void 0) : void 0;
3438
+ return {
2876
3439
  gone: false,
2877
3440
  unverified: false,
2878
- leaseReleased: false
3441
+ leaseReleased: false,
3442
+ deadline,
3443
+ snapshot,
3444
+ capturedIdentity: process.platform === "win32" ? { processIdentity: snapshot?.get(process.pid)?.birth } : void 0
2879
3445
  };
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";
3446
+ }
3447
+ async function observeRetiringOwner(owner, deadline) {
3448
+ return await observeProcessIncarnation(owner.pid, owner.processIdentity, process.platform === "win32" ? retirementTimeRemaining(deadline) : void 0);
3449
+ }
3450
+ function retirementBlockedByHeartbeat(owner, signal, requireStale) {
3451
+ return requireStale && owner.retirement === void 0 && signal === "SIGTERM" && !isQueueOwnerHeartbeatStale(owner);
3452
+ }
3453
+ async function prepareWindowsRetirement(owner, table, deadline) {
3454
+ if (owner.retirement === null || !table) throw queueRetirementIncomplete();
3455
+ owner.retirement = captureQueueRetirementReceipt(owner, owner.retirement, true, table);
3456
+ await replaceQueueOwnerRecord(owner);
3457
+ return await observeRetirementWitness(owner.retirement.root, deadline);
3458
+ }
3459
+ async function prepareRetiringOwner(owner, signal, retirement) {
3460
+ const windows = process.platform === "win32";
3461
+ const table = windows && signal !== "SIGTERM" ? await readRetirementSnapshot(retirement.deadline) : retirement.snapshot;
3462
+ let identity = windows ? observeRetirementSnapshot(owner, table) : await observeRetiringOwner(owner, retirement.deadline);
3463
+ if (identity === "matching" && windows) identity = await prepareWindowsRetirement(owner, table, retirement.deadline);
3464
+ retirement.gone = identity === "gone";
3465
+ retirement.unverified = identity === "unknown";
3466
+ return identity === "matching";
3467
+ }
3468
+ async function dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement) {
3469
+ return await withQueueLeaseMutation(owner.sessionId, async () => {
3470
+ const current = await readQueueOwnerRecord(owner.sessionId);
3471
+ retirement.leaseReleased = current?.pid !== owner.pid;
3472
+ if (!matchesQueueOwner(current, owner) || retirementBlockedByHeartbeat(current, signal, requireStale)) return false;
3473
+ if (current.retirement === null) throw queueRetirementIncomplete();
3474
+ if (!await prepareRetiringOwner(current, signal, retirement)) return false;
3475
+ return await dispatchQueueOwnerSignal(owner.pid, signal, retirement.deadline);
3476
+ }, { capturedIdentity: retirement.capturedIdentity });
3477
+ }
3478
+ async function retireQueueOwner(owner, requireStale, retirement) {
3479
+ const { deadline } = retirement;
3480
+ const exited = await terminateWithDispatch(owner.pid, (signal) => dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement), isProcessDefinitelyDead, process.platform === "win32" ? deadline : Infinity);
3481
+ if (await waitForReleasedOwner(owner, requireStale, retirement)) return false;
3482
+ const gone = retirement.gone || process.platform === "win32" && exited || await observeRetiringOwner(owner, deadline) === "gone";
2884
3483
  if (retirement.unverified && !gone) throw unverifiedQueueOwnerError(owner);
2885
3484
  return gone;
2886
3485
  }
3486
+ async function waitForReleasedOwner(owner, requireStale, retirement) {
3487
+ if (retirement.unverified && requireStale) throw unverifiedQueueOwnerError(owner);
3488
+ if (retirement.leaseReleased || retirement.unverified) {
3489
+ await waitForProcessExit(owner.pid, Math.max(0, retirement.deadline - performance.now()), isProcessDefinitelyDead);
3490
+ if (performance.now() >= retirement.deadline) {
3491
+ if (retirement.unverified) throw unverifiedQueueOwnerError(owner);
3492
+ return true;
3493
+ }
3494
+ }
3495
+ return false;
3496
+ }
3497
+ async function retireRecordedDescendants(owner, retirement) {
3498
+ await withQueueLeaseMutation(owner.sessionId, async () => {
3499
+ const current = await readQueueOwnerRecord(owner.sessionId);
3500
+ if (!matchesQueueOwner(current, owner) || current.retirement === void 0) return;
3501
+ if (current.retirement === null || process.platform !== "win32") throw queueRetirementIncomplete();
3502
+ await retireSnapshotDescendants(current, current.retirement, retirement.deadline);
3503
+ }, { capturedIdentity: retirement.capturedIdentity });
3504
+ }
3505
+ function hasNewRetirementWitness(previous, receipt) {
3506
+ const saved = new Map(previous.descendants.map((witness) => [witness.pid, witness.processIdentity]));
3507
+ return receipt.descendants.some((witness) => compareProcessBirthIdentity(saved.get(witness.pid), witness.processIdentity) !== "matching");
3508
+ }
3509
+ async function retireSnapshotDescendants(owner, receipt, deadline) {
3510
+ while (receipt.descendants.length > 0) {
3511
+ const table = await readRetirementSnapshot(deadline);
3512
+ const discovered = captureQueueRetirementReceipt(owner, receipt, false, table);
3513
+ if (hasNewRetirementWitness(receipt, discovered)) {
3514
+ receipt = discovered;
3515
+ owner.retirement = receipt;
3516
+ await replaceQueueOwnerRecord(owner);
3517
+ continue;
3518
+ }
3519
+ signalRecordedDescendants(discovered, table, deadline);
3520
+ await waitForRecordedDescendants(discovered, deadline);
3521
+ discovered.descendants = discovered.descendants.filter((witness) => !isProcessDefinitelyDead(witness.pid));
3522
+ receipt = discovered;
3523
+ owner.retirement = receipt;
3524
+ await replaceQueueOwnerRecord(owner);
3525
+ }
3526
+ }
3527
+ function leafFirstRetirementWitnesses(receipt, table) {
3528
+ const children = new Map(receipt.descendants.map((witness) => [witness.pid, 0]));
3529
+ for (const witness of receipt.descendants) {
3530
+ const parent = retirementParent(witness.pid, table);
3531
+ const count = children.get(parent);
3532
+ if (count !== void 0) children.set(parent, count + 1);
3533
+ }
3534
+ const pending = receipt.descendants.filter((witness) => children.get(witness.pid) === 0);
3535
+ const byPid = new Map(receipt.descendants.map((witness) => [witness.pid, witness]));
3536
+ for (let index = 0; index < pending.length; index += 1) {
3537
+ const parent = retirementParent(pending[index].pid, table);
3538
+ const count = children.get(parent);
3539
+ if (count !== void 0) {
3540
+ const remaining = count - 1;
3541
+ children.set(parent, remaining);
3542
+ if (remaining === 0) pending.push(byPid.get(parent));
3543
+ }
3544
+ }
3545
+ if (pending.length !== receipt.descendants.length) throw queueRetirementIncomplete();
3546
+ return pending;
3547
+ }
3548
+ function retirementParent(pid, table) {
3549
+ const identity = table.get(pid);
3550
+ return identity && !hasStaleWindowsParent(identity, table.get(identity.parentPid)) ? identity.parentPid : 0;
3551
+ }
3552
+ function signalRecordedDescendants(receipt, table, deadline) {
3553
+ if (receipt.descendants.some((witness) => observeRetirementSnapshot(witness, table) === "unknown")) throw queueRetirementIncomplete();
3554
+ const ordered = leafFirstRetirementWitnesses(receipt, table);
3555
+ let failure;
3556
+ for (const witness of ordered) {
3557
+ if (observeRetirementSnapshot(witness, table) !== "matching") continue;
3558
+ retirementTimeRemaining(deadline);
3559
+ try {
3560
+ process.kill(witness.pid, "SIGKILL");
3561
+ } catch (error) {
3562
+ if (!isProcessDefinitelyDead(witness.pid)) failure ??= error;
3563
+ }
3564
+ }
3565
+ if (failure) throw failure;
3566
+ }
3567
+ async function waitForRecordedDescendants(receipt, deadline) {
3568
+ for (let waited = 0; waited < 1e3; waited += PROCESS_POLL_MS) {
3569
+ if (receipt.descendants.every((witness) => isProcessDefinitelyDead(witness.pid))) return;
3570
+ await waitMs(Math.min(PROCESS_POLL_MS, retirementTimeRemaining(deadline)));
3571
+ }
3572
+ }
3573
+ async function assertNoPendingRetirement(owner) {
3574
+ const current = await readQueueOwnerRecord(owner.sessionId);
3575
+ if (matchesQueueOwner(current, owner) && current.retirement !== void 0) throw queueRetirementIncomplete();
3576
+ }
2887
3577
  async function terminateQueueOwnerForSession(sessionId, expectedOwner, requireStale = false) {
2888
3578
  await settlePendingQueueLeaseGuard(sessionId);
2889
3579
  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));
3580
+ if (!owner || owner.sessionId !== sessionId) return;
3581
+ if (owner.pid === process.pid) {
3582
+ if (owner.retirement !== void 0) throw queueRetirementIncomplete();
3583
+ return;
3584
+ }
3585
+ const deadline = retirementDeadline();
3586
+ const retirement = await createQueueOwnerRetirement(deadline);
3587
+ if (!await retireQueueOwner(owner, requireStale, retirement)) {
3588
+ await assertNoPendingRetirement(owner);
3589
+ return;
3590
+ }
3591
+ await retireRecordedDescendants(owner, retirement);
3592
+ await cleanupQueueOwnerFiles(sessionId, owner.socketPath, () => ownsQueueLease(owner), retirement.capturedIdentity, deadline);
2893
3593
  }
2894
3594
  async function waitMs(ms) {
2895
3595
  await new Promise((resolve) => {
@@ -3294,9 +3994,12 @@ function queueOwnerMessageParser(type) {
3294
3994
  }
3295
3995
  function parseEventOwnerMessage(message, context) {
3296
3996
  if (!isAcpJsonRpcMessage(message.message)) return null;
3997
+ const direction = message.direction;
3998
+ if (direction !== void 0 && direction !== "inbound" && direction !== "outbound") return null;
3297
3999
  return {
3298
4000
  type: "event",
3299
4001
  ...context,
4002
+ ...direction === void 0 ? {} : { direction },
3300
4003
  message: message.message
3301
4004
  };
3302
4005
  }
@@ -3391,6 +4094,610 @@ function assertQueueRequestSize(line) {
3391
4094
  });
3392
4095
  }
3393
4096
  //#endregion
4097
+ //#region src/session/queue/socket-output.ts
4098
+ const OUTPUT_CHUNK_BYTES = 65536;
4099
+ const DRAIN_TIMEOUT_MS = 1e3;
4100
+ const DEFAULT_LIMITS = Object.freeze({
4101
+ observerBytes: 67108864,
4102
+ totalBytes: 268435456,
4103
+ observers: 64
4104
+ });
4105
+ var QueueOutputBudget = class {
4106
+ limits;
4107
+ bytes = 0;
4108
+ files = 0;
4109
+ observers = 0;
4110
+ constructor(limits = DEFAULT_LIMITS) {
4111
+ this.limits = limits;
4112
+ }
4113
+ acquire() {
4114
+ if (this.files >= this.limits.observers) throw new Error("Queue output spool descriptor limit reached");
4115
+ this.files += 1;
4116
+ }
4117
+ grow(bytes) {
4118
+ if (bytes > this.limits.totalBytes - this.bytes) throw new Error("Queue output spool storage limit reached");
4119
+ this.bytes += bytes;
4120
+ }
4121
+ release(bytes) {
4122
+ this.bytes -= bytes;
4123
+ this.files -= 1;
4124
+ }
4125
+ acquireObserver() {
4126
+ if (this.observers >= this.limits.observers) throw new Error("Queue blocked observer limit reached");
4127
+ this.observers += 1;
4128
+ }
4129
+ releaseObserver() {
4130
+ this.observers -= 1;
4131
+ }
4132
+ };
4133
+ var OutputRing = class {
4134
+ budget;
4135
+ fd;
4136
+ physicalBytes = 0;
4137
+ readOffset = 0;
4138
+ length = 0;
4139
+ constructor(budget) {
4140
+ this.budget = budget;
4141
+ budget.acquire();
4142
+ try {
4143
+ const root = resolveSecureTempRoot({ fallbackPrefix: "acpx-output" });
4144
+ const file = path.join(root, `${randomUUID()}.tmp`);
4145
+ this.fd = fs.openSync(file, "wx+", 384);
4146
+ fs.unlinkSync(file);
4147
+ } catch (error) {
4148
+ if (this.fd === void 0) budget.release(0);
4149
+ else this.close();
4150
+ throw error;
4151
+ }
4152
+ }
4153
+ append(bytes, offset) {
4154
+ const capacity = this.budget.limits.observerBytes;
4155
+ if (bytes.length - offset > capacity - this.length) throw new Error("Queue observer output backlog limit reached");
4156
+ while (offset < bytes.length) {
4157
+ const position = (this.readOffset + this.length) % capacity;
4158
+ const count = Math.min(OUTPUT_CHUNK_BYTES, bytes.length - offset, capacity - position);
4159
+ const growth = Math.max(0, position + count - this.physicalBytes);
4160
+ this.budget.grow(growth);
4161
+ this.physicalBytes += growth;
4162
+ const written = fs.writeSync(this.fd, bytes, offset, count, position);
4163
+ if (written <= 0) throw new Error("Queue output spool write made no progress");
4164
+ offset += written;
4165
+ this.length += written;
4166
+ }
4167
+ }
4168
+ read() {
4169
+ const capacity = this.budget.limits.observerBytes;
4170
+ const bytes = Buffer.allocUnsafeSlow(Math.min(OUTPUT_CHUNK_BYTES, this.length, capacity - this.readOffset));
4171
+ let offset = 0;
4172
+ while (offset < bytes.length) {
4173
+ const read = fs.readSync(this.fd, bytes, offset, bytes.length - offset, this.readOffset + offset);
4174
+ if (read <= 0) throw new Error("Queue output spool ended before its committed bytes");
4175
+ offset += read;
4176
+ }
4177
+ this.readOffset = (this.readOffset + bytes.length) % capacity;
4178
+ this.length -= bytes.length;
4179
+ return bytes;
4180
+ }
4181
+ close() {
4182
+ const fd = this.fd;
4183
+ this.fd = void 0;
4184
+ if (fd !== void 0) {
4185
+ fs.closeSync(fd);
4186
+ this.budget.release(this.physicalBytes);
4187
+ }
4188
+ }
4189
+ };
4190
+ var QueueSocketOutput = class {
4191
+ socket;
4192
+ budget;
4193
+ ring;
4194
+ blocked = false;
4195
+ ending = false;
4196
+ disposed = false;
4197
+ reserved = false;
4198
+ pendingWrites = 0;
4199
+ constructor(socket, budget) {
4200
+ this.socket = socket;
4201
+ this.budget = budget;
4202
+ socket.on("drain", this.onDrain);
4203
+ socket.once("close", () => {
4204
+ this.disposed = true;
4205
+ socket.off("drain", this.onDrain);
4206
+ this.closeSpool();
4207
+ this.releaseObserver();
4208
+ });
4209
+ }
4210
+ send(message) {
4211
+ if (this.ending || this.socket.destroyed || !this.socket.writable) return;
4212
+ try {
4213
+ this.writeFrame(Buffer.from(`${JSON.stringify(message)}\n`));
4214
+ } catch (error) {
4215
+ this.fail(error);
4216
+ }
4217
+ }
4218
+ writeFrame(bytes) {
4219
+ let offset = 0;
4220
+ while (!this.blocked && offset < bytes.length) {
4221
+ const end = Math.min(offset + OUTPUT_CHUNK_BYTES, bytes.length);
4222
+ const chunk = Buffer.allocUnsafeSlow(end - offset);
4223
+ bytes.copy(chunk, 0, offset, end);
4224
+ this.write(chunk);
4225
+ offset = end;
4226
+ }
4227
+ if (offset < bytes.length) {
4228
+ this.ring ??= new OutputRing(this.budget);
4229
+ this.ring.append(bytes, offset);
4230
+ }
4231
+ }
4232
+ end() {
4233
+ if (this.ending || this.socket.destroyed) return;
4234
+ this.ending = true;
4235
+ this.armTimeout();
4236
+ this.finishIfDrained();
4237
+ }
4238
+ write(bytes) {
4239
+ if (!this.reserved) {
4240
+ this.budget.acquireObserver();
4241
+ this.reserved = true;
4242
+ }
4243
+ this.pendingWrites += 1;
4244
+ try {
4245
+ this.blocked = !this.socket.write(bytes, this.onWrite);
4246
+ } catch (error) {
4247
+ this.pendingWrites -= 1;
4248
+ throw error;
4249
+ }
4250
+ if (this.blocked && !this.socket.timeout) this.armTimeout();
4251
+ }
4252
+ onWrite = (error) => {
4253
+ this.pendingWrites -= 1;
4254
+ if (this.disposed) return;
4255
+ if (error) {
4256
+ this.fail(error);
4257
+ return;
4258
+ }
4259
+ this.releaseIfIdle();
4260
+ };
4261
+ armTimeout() {
4262
+ if (process.platform !== "win32") this.socket.setTimeout(DRAIN_TIMEOUT_MS);
4263
+ }
4264
+ onDrain = () => {
4265
+ if (this.disposed) return;
4266
+ this.blocked = false;
4267
+ if (!this.ending) this.socket.setTimeout(0);
4268
+ try {
4269
+ this.pump();
4270
+ this.releaseIfIdle();
4271
+ this.finishIfDrained();
4272
+ } catch (error) {
4273
+ this.fail(error);
4274
+ }
4275
+ };
4276
+ pump() {
4277
+ while (!this.blocked && this.ring && this.ring.length > 0) this.write(this.ring.read());
4278
+ if (this.ring?.length === 0) this.closeSpool();
4279
+ }
4280
+ finishIfDrained() {
4281
+ if (this.ending && !this.ring?.length && !this.socket.writableEnded) this.socket.end(() => this.socket.destroy());
4282
+ }
4283
+ releaseObserver() {
4284
+ if (this.reserved) {
4285
+ this.reserved = false;
4286
+ this.budget.releaseObserver();
4287
+ }
4288
+ }
4289
+ releaseIfIdle() {
4290
+ if (this.pendingWrites === 0 && !this.blocked && !this.ring?.length) this.releaseObserver();
4291
+ }
4292
+ closeSpool() {
4293
+ const ring = this.ring;
4294
+ this.ring = void 0;
4295
+ try {
4296
+ ring?.close();
4297
+ } catch (error) {
4298
+ this.socket.destroy(error instanceof Error ? error : new Error(String(error)));
4299
+ }
4300
+ }
4301
+ fail(error) {
4302
+ this.disposed = true;
4303
+ this.socket.destroy(error instanceof Error ? error : new Error(String(error)));
4304
+ this.closeSpool();
4305
+ }
4306
+ };
4307
+ //#endregion
4308
+ //#region src/session/queue/ipc-server.ts
4309
+ const QUEUE_SOCKET_DRAIN_TIMEOUT_MS = 1e3;
4310
+ function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
4311
+ return {
4312
+ type: "error",
4313
+ requestId,
4314
+ ownerGeneration: void 0,
4315
+ code: "RUNTIME",
4316
+ detailCode,
4317
+ origin: "queue",
4318
+ retryable: options.retryable,
4319
+ message
4320
+ };
4321
+ }
4322
+ function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
4323
+ const normalized = normalizeOutputError(error, {
4324
+ defaultCode: "RUNTIME",
4325
+ origin: "queue",
4326
+ detailCode,
4327
+ retryable: options.retryable
4328
+ });
4329
+ return {
4330
+ type: "error",
4331
+ requestId,
4332
+ code: normalized.code,
4333
+ detailCode: normalized.detailCode,
4334
+ origin: normalized.origin,
4335
+ message: normalized.message,
4336
+ retryable: normalized.retryable,
4337
+ acp: normalized.acp
4338
+ };
4339
+ }
4340
+ var SessionQueueOwner = class SessionQueueOwner {
4341
+ server;
4342
+ controlHandlers;
4343
+ ownerGeneration;
4344
+ maxQueueDepth;
4345
+ maxRequestBytes;
4346
+ onQueueDepthChanged;
4347
+ pending = [];
4348
+ activeTask;
4349
+ waiters = [];
4350
+ sockets = /* @__PURE__ */ new Set();
4351
+ taskSockets = /* @__PURE__ */ new Set();
4352
+ drainingSockets = /* @__PURE__ */ new Set();
4353
+ outputBudget = new QueueOutputBudget();
4354
+ outputs = /* @__PURE__ */ new Map();
4355
+ closed = false;
4356
+ constructor(server, controlHandlers, lease, options) {
4357
+ this.server = server;
4358
+ this.controlHandlers = controlHandlers;
4359
+ this.ownerGeneration = lease.ownerGeneration;
4360
+ this.maxQueueDepth = Math.max(1, Math.round(options.maxQueueDepth));
4361
+ this.maxRequestBytes = queueRequestByteLimit(options.maxRequestBytes?.toString());
4362
+ this.onQueueDepthChanged = options.onQueueDepthChanged;
4363
+ }
4364
+ static async start(lease, controlHandlers, options = { maxQueueDepth: 16 }) {
4365
+ const ownerRef = { current: void 0 };
4366
+ const server = net.createServer((socket) => {
4367
+ ownerRef.current?.handleConnection(socket);
4368
+ });
4369
+ ownerRef.current = new SessionQueueOwner(server, controlHandlers, lease, options);
4370
+ await new Promise((resolve, reject) => {
4371
+ const onListening = () => {
4372
+ server.off("error", onError);
4373
+ resolve();
4374
+ };
4375
+ const onError = (error) => {
4376
+ server.off("listening", onListening);
4377
+ reject(error);
4378
+ };
4379
+ server.once("listening", onListening);
4380
+ server.once("error", onError);
4381
+ server.listen(lease.socketPath);
4382
+ });
4383
+ return ownerRef.current;
4384
+ }
4385
+ beginShutdown() {
4386
+ if (this.closed) return;
4387
+ this.closed = true;
4388
+ for (const waiter of this.waiters.splice(0)) waiter(void 0);
4389
+ for (const task of this.pending.splice(0)) {
4390
+ if (task.waitForCompletion) task.send(makeQueueOwnerError(task.requestId, "Queue owner shutting down before prompt execution", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
4391
+ task.close();
4392
+ }
4393
+ for (const socket of this.sockets) if (!this.taskSockets.has(socket) && !this.drainingSockets.has(socket)) socket.destroy();
4394
+ this.emitQueueDepth();
4395
+ }
4396
+ async close() {
4397
+ this.beginShutdown();
4398
+ await new Promise((resolve) => {
4399
+ const forceCloseTimer = setTimeout(() => {
4400
+ for (const socket of this.sockets) socket.destroy();
4401
+ }, QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
4402
+ forceCloseTimer.unref();
4403
+ this.server.close(() => {
4404
+ clearTimeout(forceCloseTimer);
4405
+ resolve();
4406
+ });
4407
+ });
4408
+ }
4409
+ async nextTask(timeoutMs) {
4410
+ if (this.pending.length > 0) {
4411
+ const task = this.pending.shift();
4412
+ this.emitQueueDepth();
4413
+ if (task) {
4414
+ this.activeTask = task;
4415
+ recordPerfDuration("queue.owner.wait_ms", Date.now() - task.enqueuedAt);
4416
+ }
4417
+ return task;
4418
+ }
4419
+ if (this.closed) return;
4420
+ return await new Promise((resolve) => {
4421
+ const timer = timeoutMs != null && setTimeout(() => {
4422
+ const index = this.waiters.indexOf(waiter);
4423
+ if (index >= 0) this.waiters.splice(index, 1);
4424
+ resolve(void 0);
4425
+ }, Math.max(0, timeoutMs));
4426
+ const waiter = (task) => {
4427
+ if (timer) clearTimeout(timer);
4428
+ resolve(task);
4429
+ };
4430
+ this.waiters.push(waiter);
4431
+ });
4432
+ }
4433
+ queueDepth() {
4434
+ return this.pending.length;
4435
+ }
4436
+ completeTask(task) {
4437
+ if (this.activeTask === task) this.activeTask = void 0;
4438
+ }
4439
+ async cancelPrompt(targetRequestId) {
4440
+ if (targetRequestId === void 0) return await this.controlHandlers.cancelPrompt();
4441
+ const index = this.pending.findIndex((task) => task.requestId === targetRequestId);
4442
+ if (index >= 0) {
4443
+ const [task] = this.pending.splice(index, 1);
4444
+ task.send(makeQueueOwnerError(task.requestId, "Prompt cancelled before execution", "QUEUE_REQUEST_CANCELLED", { retryable: false }));
4445
+ task.close();
4446
+ this.emitQueueDepth();
4447
+ return true;
4448
+ }
4449
+ return this.activeTask?.requestId === targetRequestId ? await this.controlHandlers.cancelPrompt() : false;
4450
+ }
4451
+ emitQueueDepth() {
4452
+ this.onQueueDepthChanged?.(this.pending.length);
4453
+ }
4454
+ enqueue(task) {
4455
+ if (this.closed) {
4456
+ task.send(makeQueueOwnerError(task.requestId, "Queue owner is shutting down", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
4457
+ task.close();
4458
+ return false;
4459
+ }
4460
+ if (this.activeTask?.requestId === task.requestId || this.pending.some((queued) => queued.requestId === task.requestId)) {
4461
+ task.send(makeQueueOwnerError(task.requestId, "Request is already queued or active", "QUEUE_REQUEST_DUPLICATE", { retryable: false }));
4462
+ task.close();
4463
+ return false;
4464
+ }
4465
+ const waiter = this.waiters.shift();
4466
+ if (waiter) {
4467
+ this.activeTask = task;
4468
+ waiter(task);
4469
+ return true;
4470
+ }
4471
+ if (this.pending.length >= this.maxQueueDepth) {
4472
+ task.send({
4473
+ ...makeQueueOwnerError(task.requestId, `Queue owner is overloaded (${this.pending.length}/${this.maxQueueDepth} queued)`, "QUEUE_OWNER_OVERLOADED", { retryable: true }),
4474
+ ownerGeneration: this.ownerGeneration
4475
+ });
4476
+ task.close();
4477
+ return false;
4478
+ }
4479
+ this.pending.push(task);
4480
+ this.emitQueueDepth();
4481
+ return true;
4482
+ }
4483
+ handleControlRequest(options) {
4484
+ this.taskSockets.add(options.socket);
4485
+ this.writeQueueMessage(options.socket, {
4486
+ type: "accepted",
4487
+ requestId: options.requestId,
4488
+ ownerGeneration: this.ownerGeneration
4489
+ });
4490
+ options.run().then((message) => {
4491
+ this.writeQueueMessage(options.socket, {
4492
+ ...message,
4493
+ ownerGeneration: this.ownerGeneration
4494
+ });
4495
+ }).catch((error) => {
4496
+ this.writeQueueMessage(options.socket, {
4497
+ ...makeQueueOwnerErrorFromUnknown(options.requestId, error, "QUEUE_CONTROL_REQUEST_FAILED"),
4498
+ ownerGeneration: this.ownerGeneration
4499
+ });
4500
+ }).finally(() => {
4501
+ this.endSocket(options.socket);
4502
+ });
4503
+ }
4504
+ writeQueueMessage(socket, message) {
4505
+ this.outputs.get(socket)?.send(message);
4506
+ }
4507
+ endSocket(socket) {
4508
+ if (socket.destroyed || this.drainingSockets.has(socket)) return;
4509
+ this.taskSockets.delete(socket);
4510
+ this.drainingSockets.add(socket);
4511
+ this.outputs.get(socket)?.end();
4512
+ }
4513
+ failRequest(socket, requestId, message, detailCode) {
4514
+ this.writeQueueMessage(socket, {
4515
+ ...makeQueueOwnerError(requestId, message, detailCode, { retryable: false }),
4516
+ ownerGeneration: this.ownerGeneration
4517
+ });
4518
+ this.endSocket(socket);
4519
+ }
4520
+ parseRequestLine(socket, line) {
4521
+ let parsed;
4522
+ try {
4523
+ parsed = JSON.parse(line);
4524
+ } catch {
4525
+ this.failRequest(socket, "unknown", "Invalid queue request payload", "QUEUE_REQUEST_PAYLOAD_INVALID_JSON");
4526
+ return;
4527
+ }
4528
+ const request = parseQueueRequest(parsed);
4529
+ if (!request) this.failRequest(socket, "unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
4530
+ return request ?? void 0;
4531
+ }
4532
+ rejectStaleOwnerGeneration(socket, request) {
4533
+ if (request.ownerGeneration === void 0 || this.ownerGeneration === void 0 || request.ownerGeneration === this.ownerGeneration) return false;
4534
+ this.failRequest(socket, request.requestId, "Queue request targeted a stale queue owner generation", "QUEUE_OWNER_GENERATION_MISMATCH");
4535
+ return true;
4536
+ }
4537
+ rejectClosedOwner(socket, request) {
4538
+ if (!this.closed) return false;
4539
+ this.writeQueueMessage(socket, {
4540
+ ...makeQueueOwnerError(request.requestId, "Queue owner is shutting down", "QUEUE_OWNER_CLOSED", { retryable: true }),
4541
+ ownerGeneration: this.ownerGeneration
4542
+ });
4543
+ this.endSocket(socket);
4544
+ return true;
4545
+ }
4546
+ handleControlQueueRequest(socket, request) {
4547
+ if (request.type === "cancel_prompt") {
4548
+ this.handleControlRequest({
4549
+ socket,
4550
+ requestId: request.requestId,
4551
+ run: async () => ({
4552
+ type: "cancel_result",
4553
+ requestId: request.requestId,
4554
+ cancelled: await this.cancelPrompt(request.targetRequestId)
4555
+ })
4556
+ });
4557
+ return true;
4558
+ }
4559
+ if (request.type === "close_session") {
4560
+ this.handleControlRequest({
4561
+ socket,
4562
+ requestId: request.requestId,
4563
+ run: async () => ({
4564
+ type: "close_session_result",
4565
+ requestId: request.requestId,
4566
+ closed: await this.controlHandlers.closeSession(request.timeoutMs)
4567
+ })
4568
+ });
4569
+ return true;
4570
+ }
4571
+ return this.handleSessionControlQueueRequest(socket, request);
4572
+ }
4573
+ handleSessionControlQueueRequest(socket, request) {
4574
+ if (request.type === "set_mode") {
4575
+ this.handleControlRequest({
4576
+ socket,
4577
+ requestId: request.requestId,
4578
+ run: async () => {
4579
+ await this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs);
4580
+ return {
4581
+ type: "set_mode_result",
4582
+ requestId: request.requestId,
4583
+ modeId: request.modeId
4584
+ };
4585
+ }
4586
+ });
4587
+ return true;
4588
+ }
4589
+ if (request.type === "set_model") {
4590
+ this.handleControlRequest({
4591
+ socket,
4592
+ requestId: request.requestId,
4593
+ run: async () => {
4594
+ const response = await this.controlHandlers.setSessionModel(request.modelId, request.timeoutMs);
4595
+ return {
4596
+ type: "set_model_result",
4597
+ requestId: request.requestId,
4598
+ modelId: request.modelId,
4599
+ response
4600
+ };
4601
+ }
4602
+ });
4603
+ return true;
4604
+ }
4605
+ if (request.type === "set_config_option") {
4606
+ this.handleControlRequest({
4607
+ socket,
4608
+ requestId: request.requestId,
4609
+ run: async () => ({
4610
+ type: "set_config_option_result",
4611
+ requestId: request.requestId,
4612
+ response: await this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs)
4613
+ })
4614
+ });
4615
+ return true;
4616
+ }
4617
+ return false;
4618
+ }
4619
+ enqueuePromptRequest(socket, request) {
4620
+ this.taskSockets.add(socket);
4621
+ const task = {
4622
+ requestId: request.requestId,
4623
+ message: request.message,
4624
+ prompt: request.prompt ?? textPrompt(request.message),
4625
+ permissionMode: request.permissionMode,
4626
+ resumePolicy: request.resumePolicy,
4627
+ nonInteractivePermissions: request.nonInteractivePermissions,
4628
+ permissionPolicy: request.permissionPolicy,
4629
+ timeoutMs: request.timeoutMs,
4630
+ suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
4631
+ promptRetries: request.promptRetries,
4632
+ sessionOptions: request.sessionOptions,
4633
+ waitForCompletion: request.waitForCompletion,
4634
+ reportPromptStarted: request.reportPromptStarted,
4635
+ enqueuedAt: Date.now(),
4636
+ send: (message) => {
4637
+ this.writeQueueMessage(socket, {
4638
+ ...message,
4639
+ ownerGeneration: this.ownerGeneration
4640
+ });
4641
+ },
4642
+ close: () => {
4643
+ this.endSocket(socket);
4644
+ }
4645
+ };
4646
+ if (!this.enqueue(task)) return;
4647
+ this.writeQueueMessage(socket, {
4648
+ type: "accepted",
4649
+ requestId: request.requestId,
4650
+ ownerGeneration: this.ownerGeneration
4651
+ });
4652
+ if (!request.waitForCompletion) task.close();
4653
+ }
4654
+ handleConnection(socket) {
4655
+ this.sockets.add(socket);
4656
+ this.outputs.set(socket, new QueueSocketOutput(socket, this.outputBudget));
4657
+ socket.setEncoding("utf8");
4658
+ socket.on("timeout", () => socket.destroy());
4659
+ socket.once("close", () => {
4660
+ this.outputs.delete(socket);
4661
+ this.sockets.delete(socket);
4662
+ this.taskSockets.delete(socket);
4663
+ this.drainingSockets.delete(socket);
4664
+ });
4665
+ let buffer = "";
4666
+ let handled = false;
4667
+ const processLine = (line) => {
4668
+ if (handled) return;
4669
+ handled = true;
4670
+ const request = this.parseRequestLine(socket, line);
4671
+ if (!request || this.rejectClosedOwner(socket, request) || this.rejectStaleOwnerGeneration(socket, request)) return;
4672
+ if (this.handleControlQueueRequest(socket, request)) return;
4673
+ if (request.type !== "submit_prompt") {
4674
+ this.failRequest(socket, request.requestId, "Invalid queue request", "QUEUE_REQUEST_INVALID");
4675
+ return;
4676
+ }
4677
+ this.enqueuePromptRequest(socket, request);
4678
+ };
4679
+ socket.on("data", (chunk) => {
4680
+ buffer += chunk;
4681
+ let index = buffer.indexOf("\n");
4682
+ while (index >= 0) {
4683
+ const rawLine = buffer.slice(0, index);
4684
+ if (this.rejectOversizedRequest(socket, rawLine)) return;
4685
+ const line = rawLine.trim();
4686
+ buffer = buffer.slice(index + 1);
4687
+ if (line.length > 0) processLine(line);
4688
+ index = buffer.indexOf("\n");
4689
+ }
4690
+ this.rejectOversizedRequest(socket, buffer);
4691
+ });
4692
+ socket.on("error", () => {});
4693
+ }
4694
+ rejectOversizedRequest(socket, line) {
4695
+ if (!queueRequestExceedsLimit(line, this.maxRequestBytes)) return false;
4696
+ socket.destroy();
4697
+ return true;
4698
+ }
4699
+ };
4700
+ //#endregion
3394
4701
  //#region src/session/queue/ipc.ts
3395
4702
  const MAX_MESSAGE_BUFFER_SIZE = 10485760;
3396
4703
  function assertOwnerGeneration(owner, message) {
@@ -3578,7 +4885,7 @@ function unexpectedQueueResponseError() {
3578
4885
  }
3579
4886
  function handleAcknowledgedSubmitMessage(message, controls, formatter) {
3580
4887
  if (message.type === "event") {
3581
- notifyObserver(() => formatter.onAcpMessage(message.message));
4888
+ notifyObserver(() => formatter.onAcpMessage(message.message, message.direction));
3582
4889
  return;
3583
4890
  }
3584
4891
  if (message.type === "permission_escalation") {
@@ -3861,6 +5168,6 @@ async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, time
3861
5168
  });
3862
5169
  }
3863
5170
  //#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 };
5171
+ export { resetPerfMetrics as $, findSession as A, PERMISSION_POLICY_ACTIONS as At, assertPersistedKeyPolicy as B, PermissionDeniedError as Bt, mergePromptSourceWithText as C, extractAcpError as Ct, DEFAULT_HISTORY_LIMIT as D, NON_INTERACTIVE_PERMISSION_POLICIES as Dt, textPrompt as E, EXIT_CODES as Et, normalizeName as F, AgentStartupError as Ft, SESSION_JOURNAL_SCHEMA as G, SessionModelReplayError as Gt, serializeSessionRecordForDisk as H, QueueConnectionError as Ht, pruneSessions as I, AuthPolicyError as It, watchSession as J, UnsupportedPromptContentError as Jt, SessionJournalReader as K, SessionNotFoundError as Kt, readSessionRecord as L, ClaudeAcpSessionCreateTimeoutError as Lt, isoNow as M, AcpxOperationalError as Mt, listSessions as N, AgentDisconnectedError as Nt, absolutePath as O, OUTPUT_FORMATS as Ot, listSessionsForAgent as P, AgentSpawnError as Pt, measurePerf as Q, resolveSessionRecord as R, CopilotAcpUnsupportedError as Rt, getUnsupportedPromptContentMessage as S, normalizeOutputError as St, promptToDisplayText as T, AUTH_POLICIES as Tt, extractAgentSessionId as U, SessionConfigOptionReplayError as Ut, parseSessionRecord as V, PermissionPromptUnavailableError as Vt, normalizeAgentSessionId as W, SessionModeReplayError as Wt, getPerfMetricsSnapshot as X, formatPerfMetric as Y, incrementPerfCounter as Z, createLockOwner as _, isProcessAlive as _t, trySetModelOnRunningOwner as a, isSessionUpdateNotification as at, observeProcessIncarnation as b, isAcpQueryClosedBeforeResponseError as bt, probeQueueOwnerHealth as c, parsePromptStopReason as ct, releaseQueueOwnerLease as d, sessionBaseDir as dt, setPerfGauge as et, terminateProcess as f, sessionEventActivePath as ft, QueueLeaseGuardSettlementError as g, writePrivateJsonFile as gt, waitMs as h, writePrivateFile as ht, trySetModeOnRunningOwner as i, isAcpMessageObject as it, findSessionByDirectoryWalk as j, SESSION_RECORD_SCHEMA as jt, findGitRepositoryRoot as k, PERMISSION_MODES as kt, readQueueOwnerRecord as l, DEFAULT_EVENT_SEGMENT_MAX_BYTES as lt, tryAcquireQueueOwnerLease as m, sessionEventSegmentPath as mt, tryCloseSessionOnRunningOwner as n, extractSessionUpdateNotification as nt, trySubmitToRunningOwner as o, parseJsonRpcErrorMessage as ot, terminateQueueOwnerForSession as p, sessionEventLockPath as pt, SessionWatchError as q, SessionResumeRequiredError as qt, trySetConfigOptionOnRunningOwner as r, isAcpJsonRpcMessage as rt, SessionQueueOwner as s, parsePermissionNotice as st, tryCancelOnRunningOwner as t, startPerfTimer as tt, refreshQueueOwnerLease as u, defaultSessionEventLog as ut, lockOwnerPid as v, exitCodeForOutputErrorCode as vt, parsePromptSource as w, isAcpResourceNotFoundError as wt, PromptInputValidationError as x, isRetryablePromptError as xt, ProcessDescendants as y, formatErrorMessage as yt, writeSessionRecord as z, GeminiAcpStartupTimeoutError as zt };
3865
5172
 
3866
- //# sourceMappingURL=ipc-fMgj5vrO.js.map
5173
+ //# sourceMappingURL=ipc-D0ZMQ0q9.js.map