acpx 0.19.1 → 0.19.3

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 (41) hide show
  1. package/dist/{agent-registry-DWlx5BQ4.js → agent-registry-DWnqUICY.js} +20 -17
  2. package/dist/agent-registry-DWnqUICY.js.map +1 -0
  3. package/dist/agent-registry.js +1 -1
  4. package/dist/{cli-BerngYEz.js → cli-C1uofM8S.js} +5 -5
  5. package/dist/{cli-BerngYEz.js.map → cli-C1uofM8S.js.map} +1 -1
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/cli.js +56 -40
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{client-BchvQRpz.d.ts → client-CdG6nVDG.d.ts} +4 -3
  10. package/dist/client-CdG6nVDG.d.ts.map +1 -0
  11. package/dist/{flows-DimX0it5.js → flows-hz_Pfsp-.js} +9 -5
  12. package/dist/flows-hz_Pfsp-.js.map +1 -0
  13. package/dist/flows.d.ts +2 -2
  14. package/dist/flows.js +1 -1
  15. package/dist/{invocation-options-vobVApfO.js → invocation-options-DbZzMvKM.js} +5 -5
  16. package/dist/{invocation-options-vobVApfO.js.map → invocation-options-DbZzMvKM.js.map} +1 -1
  17. package/dist/{ipc-DAApJ5mi.js → ipc-BW06Pbb8.js} +1246 -369
  18. package/dist/ipc-BW06Pbb8.js.map +1 -0
  19. package/dist/{output-Cnuji8Bo.js → output-BjeeG-cj.js} +3 -3
  20. package/dist/{output-Cnuji8Bo.js.map → output-BjeeG-cj.js.map} +1 -1
  21. package/dist/{queue-owner-runtime-C4OOosbo.js → queue-owner-runtime-Dhr4gxNE.js} +542 -929
  22. package/dist/queue-owner-runtime-Dhr4gxNE.js.map +1 -0
  23. package/dist/runtime.d.ts +7 -2
  24. package/dist/runtime.d.ts.map +1 -1
  25. package/dist/runtime.js +24 -17
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/{session-options-Dp6mzNSy.d.ts → session-options-X7LhX9BF.d.ts} +2 -1
  28. package/dist/{session-options-Dp6mzNSy.d.ts.map → session-options-X7LhX9BF.d.ts.map} +1 -1
  29. package/dist/{session-watch-Bj9LoYUq.js → session-watch-CgfkUKLA.js} +4 -4
  30. package/dist/{session-watch-Bj9LoYUq.js.map → session-watch-CgfkUKLA.js.map} +1 -1
  31. package/dist/watch-BGdqBiNX.js +85 -0
  32. package/dist/watch-BGdqBiNX.js.map +1 -0
  33. package/package.json +11 -11
  34. package/skills/acpx/SKILL.md +7 -2
  35. package/dist/agent-registry-DWlx5BQ4.js.map +0 -1
  36. package/dist/client-BchvQRpz.d.ts.map +0 -1
  37. package/dist/flows-DimX0it5.js.map +0 -1
  38. package/dist/ipc-DAApJ5mi.js.map +0 -1
  39. package/dist/queue-owner-runtime-C4OOosbo.js.map +0 -1
  40. package/dist/watch-Cz6jbrRs.js +0 -32
  41. package/dist/watch-Cz6jbrRs.js.map +0 -1
@@ -1,4 +1,4 @@
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";
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-DWnqUICY.js";
2
2
  import fs, { statSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import fs$1 from "node:fs/promises";
@@ -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,7 @@ 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)));
722
- 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;
860
+ if (await this.matchesSnapshot(paths, segments)) return segments;
727
861
  } catch (error) {
728
862
  if (!retryableSnapshotError(error)) {
729
863
  await this.closeSnapshot(segments);
@@ -744,11 +878,16 @@ var SessionJournalReader = class {
744
878
  symlinks: "reject",
745
879
  nonBlockingRead: true
746
880
  });
747
- return {
748
- filePath,
749
- opened,
750
- identity: fileIdentity(opened.stat)
751
- };
881
+ try {
882
+ return {
883
+ filePath,
884
+ opened,
885
+ identity: fileIdentity(await opened.handle.stat({ bigint: true }))
886
+ };
887
+ } catch (error) {
888
+ await opened[Symbol.asyncDispose]();
889
+ throw error;
890
+ }
752
891
  }));
753
892
  let failure;
754
893
  for (const result of results) if (result.status === "fulfilled") segments.push(result.value);
@@ -759,10 +898,33 @@ var SessionJournalReader = class {
759
898
  async closeSnapshot(segments) {
760
899
  await Promise.all(segments.map(({ opened }) => opened[Symbol.asyncDispose]()));
761
900
  }
901
+ async matchesSnapshot(paths, segments) {
902
+ const current = await Promise.all(paths.map(pathIdentity));
903
+ const identities = new Map(segments.map((entry) => [entry.filePath, entry.identity]));
904
+ return paths.every((filePath, index) => current[index] === identities.get(filePath));
905
+ }
906
+ async withSnapshot(read, signal) {
907
+ for (;;) {
908
+ const segments = await this.openSnapshot(signal);
909
+ try {
910
+ const outcome = await read(segments).then((value) => ({ value }), (error) => {
911
+ if (!(error instanceof SessionWatchError) || error.code !== "WATCH_JOURNAL_CORRUPT") throw error;
912
+ return { error };
913
+ });
914
+ if (await this.matchesSnapshot(this.paths(), segments)) {
915
+ signal?.throwIfAborted();
916
+ if ("error" in outcome) throw outcome.error;
917
+ return outcome.value;
918
+ }
919
+ } finally {
920
+ await this.closeSnapshot(segments);
921
+ }
922
+ await setTimeout$1(5, void 0, { signal });
923
+ }
924
+ }
762
925
  async readAcpMessages() {
763
- const segments = await this.openSnapshot();
764
- const messages = [];
765
- try {
926
+ return await this.withSnapshot(async (segments) => {
927
+ const messages = [];
766
928
  for (const { opened } of segments) {
767
929
  const payload = await opened.handle.readFile("utf8");
768
930
  for (const line of payload.split("\n")) try {
@@ -771,15 +933,12 @@ var SessionJournalReader = class {
771
933
  } catch {}
772
934
  }
773
935
  return messages;
774
- } finally {
775
- await this.closeSnapshot(segments);
776
- }
936
+ });
777
937
  }
778
938
  async readTail() {
779
- const segments = await this.openSnapshot();
780
- let tail = emptySegment();
781
- let active = emptySegment();
782
- try {
939
+ return await this.withSnapshot(async (segments) => {
940
+ let tail = emptySegment();
941
+ let active = emptySegment();
783
942
  for (const segment of segments.toReversed()) {
784
943
  const state = emptySegment();
785
944
  await this.readSegment(segment.opened.handle, segment.opened.stat.size, state, void 0);
@@ -797,22 +956,20 @@ var SessionJournalReader = class {
797
956
  activeAnchored: active.firstSequence !== void 0,
798
957
  activePartial: active.pending.length > 0
799
958
  };
800
- } finally {
801
- await this.closeSnapshot(segments);
802
- }
959
+ });
803
960
  }
804
961
  async read(signal, options) {
805
- const page = createJournalPage(options);
806
- const segments = await this.openSnapshot(signal);
807
- let hasMore = false;
808
- let firstSequence;
809
- let tail = emptySegment();
810
- let active = emptySegment();
811
- try {
962
+ const { result, states } = await this.withSnapshot(async (segments) => {
963
+ const page = createJournalPage(options);
964
+ const states = new Map(this.states);
965
+ let hasMore = false;
966
+ let firstSequence;
967
+ let tail = emptySegment();
968
+ let active = emptySegment();
812
969
  for (const segment of segments) {
813
- const state = this.states.get(segment.identity) ?? emptySegment();
970
+ const state = { ...states.get(segment.identity) ?? emptySegment() };
814
971
  await this.readSegment(segment.opened.handle, segment.opened.stat.size, state, page, signal);
815
- this.states.set(segment.identity, state);
972
+ states.set(segment.identity, state);
816
973
  if (state.firstSequence !== void 0) {
817
974
  if (firstSequence === void 0) firstSequence = state.firstSequence;
818
975
  else if (state.firstSequence !== tail.sequence) corrupt("Session journal segments are not contiguous");
@@ -824,25 +981,29 @@ var SessionJournalReader = class {
824
981
  break;
825
982
  }
826
983
  }
827
- this.pruneStates(segments);
984
+ this.pruneStates(states, segments);
828
985
  return {
829
- events: page.events,
830
- hasMore,
831
- firstSequence,
832
- sequence: tail.sequence,
833
- messageSequence: tail.messageSequence,
834
- requestId: tail.requestId,
835
- activeSize: active.offset,
836
- activeAnchored: active.firstSequence !== void 0,
837
- activePartial: active.pending.length > 0
986
+ states,
987
+ result: {
988
+ events: page.events,
989
+ hasMore,
990
+ firstSequence,
991
+ sequence: tail.sequence,
992
+ messageSequence: tail.messageSequence,
993
+ requestId: tail.requestId,
994
+ activeSize: active.offset,
995
+ activeAnchored: active.firstSequence !== void 0,
996
+ activePartial: active.pending.length > 0
997
+ }
838
998
  };
839
- } finally {
840
- await this.closeSnapshot(segments);
841
- }
999
+ }, signal);
1000
+ signal?.throwIfAborted();
1001
+ this.states = states;
1002
+ return result;
842
1003
  }
843
- pruneStates(segments) {
1004
+ pruneStates(states, segments) {
844
1005
  const retained = new Set(segments.map((entry) => entry.identity));
845
- for (const identity of this.states.keys()) if (!retained.has(identity)) this.states.delete(identity);
1006
+ for (const identity of states.keys()) if (!retained.has(identity)) states.delete(identity);
846
1007
  }
847
1008
  async readSegment(handle, size, state, page, signal) {
848
1009
  if (size < state.offset) corrupt("Session journal was truncated while watching");
@@ -978,6 +1139,7 @@ async function* iterateJournal(record, options) {
978
1139
  let after = options.cursor === void 0 ? -1 : parseCursor(record.acpxRecordId, options.cursor);
979
1140
  const reader = new SessionJournalReader(record);
980
1141
  let first = true;
1142
+ const observation = {};
981
1143
  for (;;) {
982
1144
  if (signal.aborted) return;
983
1145
  const snapshot = await settleUnlessAborted(reader.read(signal, {
@@ -992,16 +1154,34 @@ async function* iterateJournal(record, options) {
992
1154
  after = sequence;
993
1155
  yield event;
994
1156
  }
995
- if (!await continueAfterPage(record, snapshot, options)) return;
1157
+ if (!await continueAfterPage(record, snapshot, options, observation)) return;
996
1158
  }
997
1159
  }
998
- async function continueAfterPage(record, snapshot, options) {
1160
+ async function continueAfterPage(record, snapshot, options, observation) {
1161
+ if (options.signal.aborted || confirmsTerminalObservation(snapshot, observation)) return false;
999
1162
  if (!snapshot.hasMore && options.continueWatching) {
1000
- if (!await settleUnlessAborted(options.continueWatching(record, snapshot.requestId), options.signal)) return false;
1163
+ const decision = await settleUnlessAborted(options.continueWatching(record, snapshot.requestId), options.signal).catch((error) => {
1164
+ if (error instanceof SessionWatchError && error.code === "WATCH_OUTCOME_UNKNOWN") return error;
1165
+ throw error;
1166
+ });
1167
+ if (decision !== true) {
1168
+ observation.terminal = {
1169
+ requestId: snapshot.requestId,
1170
+ error: decision instanceof SessionWatchError ? decision : void 0
1171
+ };
1172
+ return !options.signal.aborted;
1173
+ }
1001
1174
  }
1002
1175
  await settleUnlessAborted(setTimeout$1(snapshot.hasMore ? 0 : 100, void 0, { signal: options.signal }), options.signal);
1003
1176
  return !options.signal.aborted;
1004
1177
  }
1178
+ function confirmsTerminalObservation(snapshot, observation) {
1179
+ const terminal = observation.terminal;
1180
+ delete observation.terminal;
1181
+ if (!terminal || snapshot.events.length > 0 || snapshot.hasMore || snapshot.requestId !== terminal.requestId) return false;
1182
+ if (terminal.error) throw terminal.error;
1183
+ return true;
1184
+ }
1005
1185
  function assertRetainedCursor(after, snapshot, first) {
1006
1186
  if (after < 0) return;
1007
1187
  if (after > snapshot.sequence) throw new SessionWatchError(first ? "WATCH_CURSOR_FUTURE" : "WATCH_CURSOR_EXPIRED", "Cursor is beyond the available session journal");
@@ -1238,12 +1418,15 @@ function parseRequestTokenUsage(raw) {
1238
1418
  }
1239
1419
  return usage;
1240
1420
  }
1421
+ function isValidImageSize(size) {
1422
+ if (size === void 0 || size === null) return true;
1423
+ const record = asRecord$2(size);
1424
+ return !!record && isFiniteNumber(record.width) && isFiniteNumber(record.height);
1425
+ }
1241
1426
  function isSessionMessageImage(raw) {
1242
1427
  const record = asRecord$2(raw);
1243
- if (!record || typeof record.source !== "string") return false;
1244
- if (record.size === void 0 || record.size === null) return true;
1245
- const size = asRecord$2(record.size);
1246
- return !!size && isFiniteNumber(size.width) && isFiniteNumber(size.height);
1428
+ if (!record || typeof record.source !== "string" || !isOptionalString(record.mime_type)) return false;
1429
+ return isValidImageSize(record.size);
1247
1430
  }
1248
1431
  function isSessionMessageAudio(raw) {
1249
1432
  const record = asRecord$2(raw);
@@ -1867,182 +2050,6 @@ async function unlinkCountingBytes(filePath) {
1867
2050
  return bytes;
1868
2051
  }
1869
2052
  //#endregion
1870
- //#region src/prompt-content.ts
1871
- var PromptInputValidationError = class extends Error {
1872
- constructor(message) {
1873
- super(message);
1874
- this.name = "PromptInputValidationError";
1875
- }
1876
- };
1877
- function asRecord$1(value) {
1878
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
1879
- return value;
1880
- }
1881
- function isNonEmptyString(value) {
1882
- return typeof value === "string" && value.trim().length > 0;
1883
- }
1884
- function isBase64Data(value) {
1885
- if (value.length === 0 || value.length % 4 !== 0) return false;
1886
- return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
1887
- }
1888
- function isImageMimeType(value) {
1889
- return /^image\/[A-Za-z0-9.+-]+$/i.test(value);
1890
- }
1891
- function isAudioMimeType(value) {
1892
- return /^audio\/[A-Za-z0-9.+-]+$/i.test(value);
1893
- }
1894
- function isTextBlock(value) {
1895
- const record = asRecord$1(value);
1896
- return record?.type === "text" && typeof record.text === "string";
1897
- }
1898
- function isImageBlock(value) {
1899
- const record = asRecord$1(value);
1900
- return record?.type === "image" && isNonEmptyString(record.mimeType) && isImageMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
1901
- }
1902
- function isAudioBlock(value) {
1903
- const record = asRecord$1(value);
1904
- return record?.type === "audio" && isNonEmptyString(record.mimeType) && isAudioMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
1905
- }
1906
- function isResourceLinkBlock(value) {
1907
- const record = asRecord$1(value);
1908
- return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title == null || typeof record.title === "string") && typeof record.name === "string";
1909
- }
1910
- function isResourcePayload(value) {
1911
- const record = asRecord$1(value);
1912
- if (!record || !isNonEmptyString(record.uri)) return false;
1913
- return typeof record.text === "string" || typeof record.blob === "string";
1914
- }
1915
- function isResourceBlock(value) {
1916
- const record = asRecord$1(value);
1917
- return record?.type === "resource" && isResourcePayload(record.resource);
1918
- }
1919
- const CONTENT_BLOCK_VALIDATORS = [
1920
- isTextBlock,
1921
- isImageBlock,
1922
- isAudioBlock,
1923
- isResourceLinkBlock,
1924
- isResourceBlock
1925
- ];
1926
- function isContentBlock(value) {
1927
- return CONTENT_BLOCK_VALIDATORS.some((validator) => validator(value));
1928
- }
1929
- const CONTENT_BLOCK_ERROR_VALIDATORS = {
1930
- text: validateTextContentBlock,
1931
- image: validateImageContentBlock,
1932
- audio: validateAudioContentBlock,
1933
- resource_link: validateResourceLinkContentBlock,
1934
- resource: validateResourceContentBlock
1935
- };
1936
- function contentBlockErrorValidator(type) {
1937
- return Object.hasOwn(CONTENT_BLOCK_ERROR_VALIDATORS, type) ? CONTENT_BLOCK_ERROR_VALIDATORS[type] : void 0;
1938
- }
1939
- function validateTextContentBlock(record, index) {
1940
- return typeof record.text === "string" ? void 0 : `prompt[${index}] text block must include a string text field`;
1941
- }
1942
- function validateImageContentBlock(record, index) {
1943
- if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] image block must include a non-empty mimeType`;
1944
- if (!isImageMimeType(record.mimeType)) return `prompt[${index}] image block mimeType must start with image/`;
1945
- if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] image block must include non-empty base64 data`;
1946
- return isBase64Data(record.data) ? void 0 : `prompt[${index}] image block data must be valid base64`;
1947
- }
1948
- function validateAudioContentBlock(record, index) {
1949
- if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] audio block must include a non-empty mimeType`;
1950
- if (!isAudioMimeType(record.mimeType)) return `prompt[${index}] audio block mimeType must start with audio/`;
1951
- if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] audio block must include non-empty base64 data`;
1952
- return isBase64Data(record.data) ? void 0 : `prompt[${index}] audio block data must be valid base64`;
1953
- }
1954
- function validateResourceLinkContentBlock(record, index) {
1955
- if (!isNonEmptyString(record.uri)) return `prompt[${index}] resource_link block must include a non-empty uri`;
1956
- if (record.title != null && typeof record.title !== "string") return `prompt[${index}] resource_link block title must be a string or null when present`;
1957
- if (typeof record.name !== "string") return `prompt[${index}] resource_link block must include a string name`;
1958
- }
1959
- function validateResourceContentBlock(record, index) {
1960
- if (!asRecord$1(record.resource)) return `prompt[${index}] resource block must include a resource object`;
1961
- return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and a string text or blob field`;
1962
- }
1963
- function getContentBlockValidationError(value, index) {
1964
- const record = asRecord$1(value);
1965
- if (!record || typeof record.type !== "string") return `prompt[${index}] must be an ACP content block object`;
1966
- const validator = contentBlockErrorValidator(record.type);
1967
- return validator ? validator(record, index) : `prompt[${index}] has unsupported content block type ${JSON.stringify(record.type)}`;
1968
- }
1969
- function isPromptInput(value) {
1970
- return Array.isArray(value) && value.every((entry) => isContentBlock(entry));
1971
- }
1972
- function promptCapabilityRequirement(block) {
1973
- switch (block.type) {
1974
- case "image": return {
1975
- blockType: "image",
1976
- capability: "image"
1977
- };
1978
- case "audio": return {
1979
- blockType: "audio",
1980
- capability: "audio"
1981
- };
1982
- case "resource": return {
1983
- blockType: "resource",
1984
- capability: "embeddedContext"
1985
- };
1986
- default: return;
1987
- }
1988
- }
1989
- function getUnsupportedPromptContentMessage(prompt, agentCapabilities) {
1990
- for (const [index, block] of prompt.entries()) {
1991
- const requirement = promptCapabilityRequirement(block);
1992
- if (!requirement) continue;
1993
- if (agentCapabilities?.promptCapabilities?.[requirement.capability] === true) continue;
1994
- return `prompt[${index}] ${requirement.blockType} content requires agentCapabilities.promptCapabilities.${requirement.capability}`;
1995
- }
1996
- }
1997
- function textPrompt(text) {
1998
- return [{
1999
- type: "text",
2000
- text
2001
- }];
2002
- }
2003
- function parseStructuredPrompt(source) {
2004
- if (!source.startsWith("[")) return;
2005
- try {
2006
- const parsed = JSON.parse(source);
2007
- if (isPromptInput(parsed)) return parsed;
2008
- if (Array.isArray(parsed)) throw new PromptInputValidationError(parsed.map((entry, index) => getContentBlockValidationError(entry, index)).find((message) => message !== void 0) ?? "Structured prompt JSON must be an array of valid ACP content blocks");
2009
- return;
2010
- } catch (error) {
2011
- if (error instanceof PromptInputValidationError) throw error;
2012
- return;
2013
- }
2014
- }
2015
- function parsePromptSource(source) {
2016
- const trimmed = source.trim();
2017
- const structured = parseStructuredPrompt(trimmed);
2018
- if (structured) return structured;
2019
- if (!trimmed) return [];
2020
- return textPrompt(trimmed);
2021
- }
2022
- function mergePromptSourceWithText(source, suffixText) {
2023
- const prompt = parsePromptSource(source);
2024
- const appended = suffixText.trim();
2025
- if (!appended) return prompt;
2026
- if (prompt.length === 0) return textPrompt(appended);
2027
- return [...prompt, ...textPrompt(appended)];
2028
- }
2029
- function promptToDisplayText(prompt) {
2030
- return prompt.map((block) => contentBlockDisplayText(block)).filter((entry) => entry.trim().length > 0).join("\n\n").trim();
2031
- }
2032
- function contentBlockDisplayText(block) {
2033
- switch (block.type) {
2034
- case "text": return block.text;
2035
- case "resource_link": return block.title ?? block.name ?? block.uri;
2036
- case "resource": return resourceBlockDisplayText(block);
2037
- case "image": return `[image] ${block.mimeType}`;
2038
- case "audio": return `[audio] ${block.mimeType}`;
2039
- default: return "";
2040
- }
2041
- }
2042
- function resourceBlockDisplayText(block) {
2043
- return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
2044
- }
2045
- //#endregion
2046
2053
  //#region src/process-identity.ts
2047
2054
  const IDENTITY_QUERY_TIMEOUT_MS$1 = 2e3;
2048
2055
  const LINUX_PROCESS_QUERY = `
@@ -2439,12 +2446,228 @@ async function getOwnProcessIdentity() {
2439
2446
  return await ownIdentityPending;
2440
2447
  }
2441
2448
  //#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;
2449
+ //#region src/session/lock-owner.ts
2450
+ const OBSERVATION_REUSE_MS = 1e3;
2451
+ const IDENTITY_QUERY_TIMEOUT_MS = 1e3;
2452
+ function lockOwnerPid(value) {
2453
+ if (!value || typeof value !== "object" || !("pid" in value)) return;
2454
+ const pid = value.pid;
2455
+ return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
2456
+ }
2457
+ function canReuseObservation(preserved, pid, identity, fresh) {
2458
+ return !fresh && preserved?.pid === pid && compareProcessBirthIdentity(preserved.identity, identity) === "matching" && performance.now() - preserved.checkedAt < OBSERVATION_REUSE_MS;
2459
+ }
2460
+ async function createLockOwner(options = {}) {
2461
+ const { signal, capturedIdentity } = options;
2462
+ signal?.throwIfAborted();
2463
+ const processIdentity = capturedIdentity === void 0 ? await getOwnProcessIdentity() : capturedIdentity.processIdentity;
2464
+ signal?.throwIfAborted();
2465
+ let preserved;
2466
+ return {
2467
+ payload: {
2468
+ pid: process.pid,
2469
+ processIdentity
2470
+ },
2471
+ async hasExited(payload, fresh, signal) {
2472
+ signal?.throwIfAborted();
2473
+ const pid = lockOwnerPid(payload);
2474
+ if (!pid) return false;
2475
+ const identity = parseProcessBirthIdentity(payload.processIdentity);
2476
+ if (identity && canReuseObservation(preserved, pid, identity, fresh)) return false;
2477
+ const incarnation = await observeProcessIncarnation(pid, identity, IDENTITY_QUERY_TIMEOUT_MS);
2478
+ signal?.throwIfAborted();
2479
+ if (identity && incarnation !== "gone") preserved = {
2480
+ pid,
2481
+ identity,
2482
+ checkedAt: performance.now()
2483
+ };
2484
+ return incarnation === "gone";
2485
+ }
2486
+ };
2487
+ }
2488
+ //#endregion
2489
+ //#region src/prompt-content.ts
2490
+ var PromptInputValidationError = class extends Error {
2491
+ constructor(message) {
2492
+ super(message);
2493
+ this.name = "PromptInputValidationError";
2494
+ }
2495
+ };
2496
+ function asRecord$1(value) {
2497
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
2498
+ return value;
2499
+ }
2500
+ function isNonEmptyString(value) {
2501
+ return typeof value === "string" && value.trim().length > 0;
2502
+ }
2503
+ function isBase64Data(value) {
2504
+ if (value.length === 0 || value.length % 4 !== 0) return false;
2505
+ return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
2506
+ }
2507
+ function isImageMimeType(value) {
2508
+ return /^image\/[A-Za-z0-9.+-]+$/i.test(value);
2509
+ }
2510
+ function isAudioMimeType(value) {
2511
+ return /^audio\/[A-Za-z0-9.+-]+$/i.test(value);
2512
+ }
2513
+ function isTextBlock(value) {
2514
+ const record = asRecord$1(value);
2515
+ return record?.type === "text" && typeof record.text === "string";
2516
+ }
2517
+ function isImageBlock(value) {
2518
+ const record = asRecord$1(value);
2519
+ return record?.type === "image" && isNonEmptyString(record.mimeType) && isImageMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
2520
+ }
2521
+ function isAudioBlock(value) {
2522
+ const record = asRecord$1(value);
2523
+ return record?.type === "audio" && isNonEmptyString(record.mimeType) && isAudioMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
2524
+ }
2525
+ function isResourceLinkBlock(value) {
2526
+ const record = asRecord$1(value);
2527
+ return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title == null || typeof record.title === "string") && typeof record.name === "string";
2528
+ }
2529
+ function isResourcePayload(value) {
2530
+ const record = asRecord$1(value);
2531
+ if (!record || !isNonEmptyString(record.uri)) return false;
2532
+ return typeof record.text === "string" || typeof record.blob === "string";
2533
+ }
2534
+ function isResourceBlock(value) {
2535
+ const record = asRecord$1(value);
2536
+ return record?.type === "resource" && isResourcePayload(record.resource);
2537
+ }
2538
+ const CONTENT_BLOCK_VALIDATORS = [
2539
+ isTextBlock,
2540
+ isImageBlock,
2541
+ isAudioBlock,
2542
+ isResourceLinkBlock,
2543
+ isResourceBlock
2544
+ ];
2545
+ function isContentBlock(value) {
2546
+ return CONTENT_BLOCK_VALIDATORS.some((validator) => validator(value));
2547
+ }
2548
+ const CONTENT_BLOCK_ERROR_VALIDATORS = {
2549
+ text: validateTextContentBlock,
2550
+ image: validateImageContentBlock,
2551
+ audio: validateAudioContentBlock,
2552
+ resource_link: validateResourceLinkContentBlock,
2553
+ resource: validateResourceContentBlock
2554
+ };
2555
+ function contentBlockErrorValidator(type) {
2556
+ return Object.hasOwn(CONTENT_BLOCK_ERROR_VALIDATORS, type) ? CONTENT_BLOCK_ERROR_VALIDATORS[type] : void 0;
2557
+ }
2558
+ function validateTextContentBlock(record, index) {
2559
+ return typeof record.text === "string" ? void 0 : `prompt[${index}] text block must include a string text field`;
2560
+ }
2561
+ function validateImageContentBlock(record, index) {
2562
+ if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] image block must include a non-empty mimeType`;
2563
+ if (!isImageMimeType(record.mimeType)) return `prompt[${index}] image block mimeType must start with image/`;
2564
+ if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] image block must include non-empty base64 data`;
2565
+ return isBase64Data(record.data) ? void 0 : `prompt[${index}] image block data must be valid base64`;
2566
+ }
2567
+ function validateAudioContentBlock(record, index) {
2568
+ if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] audio block must include a non-empty mimeType`;
2569
+ if (!isAudioMimeType(record.mimeType)) return `prompt[${index}] audio block mimeType must start with audio/`;
2570
+ if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] audio block must include non-empty base64 data`;
2571
+ return isBase64Data(record.data) ? void 0 : `prompt[${index}] audio block data must be valid base64`;
2572
+ }
2573
+ function validateResourceLinkContentBlock(record, index) {
2574
+ if (!isNonEmptyString(record.uri)) return `prompt[${index}] resource_link block must include a non-empty uri`;
2575
+ if (record.title != null && typeof record.title !== "string") return `prompt[${index}] resource_link block title must be a string or null when present`;
2576
+ if (typeof record.name !== "string") return `prompt[${index}] resource_link block must include a string name`;
2577
+ }
2578
+ function validateResourceContentBlock(record, index) {
2579
+ if (!asRecord$1(record.resource)) return `prompt[${index}] resource block must include a resource object`;
2580
+ return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and a string text or blob field`;
2581
+ }
2582
+ function getContentBlockValidationError(value, index) {
2583
+ const record = asRecord$1(value);
2584
+ if (!record || typeof record.type !== "string") return `prompt[${index}] must be an ACP content block object`;
2585
+ const validator = contentBlockErrorValidator(record.type);
2586
+ return validator ? validator(record, index) : `prompt[${index}] has unsupported content block type ${JSON.stringify(record.type)}`;
2587
+ }
2588
+ function isPromptInput(value) {
2589
+ return Array.isArray(value) && value.every((entry) => isContentBlock(entry));
2590
+ }
2591
+ function promptCapabilityRequirement(block) {
2592
+ switch (block.type) {
2593
+ case "image": return {
2594
+ blockType: "image",
2595
+ capability: "image"
2596
+ };
2597
+ case "audio": return {
2598
+ blockType: "audio",
2599
+ capability: "audio"
2600
+ };
2601
+ case "resource": return {
2602
+ blockType: "resource",
2603
+ capability: "embeddedContext"
2604
+ };
2605
+ default: return;
2606
+ }
2607
+ }
2608
+ function getUnsupportedPromptContentMessage(prompt, agentCapabilities) {
2609
+ for (const [index, block] of prompt.entries()) {
2610
+ const requirement = promptCapabilityRequirement(block);
2611
+ if (!requirement) continue;
2612
+ if (agentCapabilities?.promptCapabilities?.[requirement.capability] === true) continue;
2613
+ return `prompt[${index}] ${requirement.blockType} content requires agentCapabilities.promptCapabilities.${requirement.capability}`;
2614
+ }
2615
+ }
2616
+ function textPrompt(text) {
2617
+ return [{
2618
+ type: "text",
2619
+ text
2620
+ }];
2621
+ }
2622
+ function parseStructuredPrompt(source) {
2623
+ if (!source.startsWith("[")) return;
2624
+ try {
2625
+ const parsed = JSON.parse(source);
2626
+ if (isPromptInput(parsed)) return parsed;
2627
+ if (Array.isArray(parsed)) throw new PromptInputValidationError(parsed.map((entry, index) => getContentBlockValidationError(entry, index)).find((message) => message !== void 0) ?? "Structured prompt JSON must be an array of valid ACP content blocks");
2628
+ return;
2629
+ } catch (error) {
2630
+ if (error instanceof PromptInputValidationError) throw error;
2631
+ return;
2632
+ }
2633
+ }
2634
+ function parsePromptSource(source) {
2635
+ const trimmed = source.trim();
2636
+ const structured = parseStructuredPrompt(trimmed);
2637
+ if (structured) return structured;
2638
+ if (!trimmed) return [];
2639
+ return textPrompt(trimmed);
2640
+ }
2641
+ function mergePromptSourceWithText(source, suffixText) {
2642
+ const prompt = parsePromptSource(source);
2643
+ const appended = suffixText.trim();
2644
+ if (!appended) return prompt;
2645
+ if (prompt.length === 0) return textPrompt(appended);
2646
+ return [...prompt, ...textPrompt(appended)];
2647
+ }
2648
+ function promptToDisplayText(prompt) {
2649
+ return prompt.map((block) => contentBlockDisplayText(block)).filter((entry) => entry.trim().length > 0).join("\n\n").trim();
2650
+ }
2651
+ function contentBlockDisplayText(block) {
2652
+ switch (block.type) {
2653
+ case "text": return block.text;
2654
+ case "resource_link": return block.title ?? block.name ?? block.uri;
2655
+ case "resource": return resourceBlockDisplayText(block);
2656
+ case "image": return `[image] ${block.mimeType}`;
2657
+ case "audio": return `[audio] ${block.mimeType}`;
2658
+ default: return "";
2659
+ }
2660
+ }
2661
+ function resourceBlockDisplayText(block) {
2662
+ return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
2663
+ }
2664
+ //#endregion
2665
+ //#region src/acp/process-descendants.ts
2666
+ function hasStaleWindowsParent(identity, parent) {
2667
+ return identity.birth.kind === "windows-creation" && (parent?.birth.kind !== "windows-creation" || parent.birth.value > identity.birth.value);
2668
+ }
2669
+ function includeProcessDescendants(table, owned) {
2670
+ let expanded;
2448
2671
  do {
2449
2672
  expanded = false;
2450
2673
  for (const identity of table.values()) {
@@ -2463,6 +2686,8 @@ var ProcessDescendants = class {
2463
2686
  rootBirth;
2464
2687
  pending;
2465
2688
  retired = false;
2689
+ failedRootObservation = false;
2690
+ unresolvedOwnership = false;
2466
2691
  ownProcessGroup;
2467
2692
  captureGroupAfterExit;
2468
2693
  groupExitedAt;
@@ -2497,10 +2722,18 @@ var ProcessDescendants = class {
2497
2722
  }
2498
2723
  return true;
2499
2724
  } catch {
2500
- if (!isChildProcessRunning(this.child)) this.captureGroupAfterExit = false;
2725
+ this.recordSnapshotFailure(captureRootGroup);
2501
2726
  return false;
2502
2727
  }
2503
2728
  }
2729
+ recordSnapshotFailure(captureRootGroup) {
2730
+ if (isChildProcessRunning(this.child)) {
2731
+ this.failedRootObservation = true;
2732
+ return;
2733
+ }
2734
+ if (captureRootGroup) this.unresolvedOwnership = true;
2735
+ this.captureGroupAfterExit = false;
2736
+ }
2504
2737
  requiredSnapshotPids(rootWasRunning) {
2505
2738
  return [...this.identities.keys(), ...rootWasRunning && this.child.pid ? [this.child.pid] : []];
2506
2739
  }
@@ -2512,6 +2745,10 @@ var ProcessDescendants = class {
2512
2745
  if (observed && compareProcessBirthIdentity(identity.birth, observed.birth) === "matching") owned.add(pid);
2513
2746
  }
2514
2747
  this.includeRoot(table, owned);
2748
+ if (owned.has(this.child.pid ?? 0)) {
2749
+ this.failedRootObservation = false;
2750
+ this.unresolvedOwnership = false;
2751
+ }
2515
2752
  if (this.ownProcessGroup) this.includeProcessGroup(table, owned, rootWasRunning);
2516
2753
  includeProcessDescendants(table, owned);
2517
2754
  owned.delete(this.child.pid ?? 0);
@@ -2532,29 +2769,40 @@ var ProcessDescendants = class {
2532
2769
  }
2533
2770
  includeProcessGroup(table, owned, rootWasRunning) {
2534
2771
  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);
2772
+ if (!root || !(rootWasRunning || [...owned].some((pid) => table.get(pid)?.groupPid === root))) return;
2773
+ this.unresolvedOwnership = !this.admitGroupMembers(table, owned, root);
2774
+ if (!this.unresolvedOwnership) this.failedRootObservation = false;
2775
+ }
2776
+ admitGroupMembers(table, owned, groupPid) {
2777
+ let verified = true;
2778
+ for (const identity of table.values()) {
2779
+ if (identity.groupPid !== groupPid) continue;
2780
+ const beforeExit = this.startedBeforeRootExit(identity);
2781
+ if (beforeExit === void 0) verified = false;
2782
+ else if (beforeExit) owned.add(identity.pid);
2537
2783
  }
2784
+ return verified;
2538
2785
  }
2539
2786
  startedBeforeRootExit(identity) {
2540
2787
  const cutoff = this.groupExitedAt;
2541
2788
  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;
2789
+ if (cutoff === null) return;
2790
+ if (identity.birth.kind !== "linux-proc") return typeof cutoff === "number" ? Date.parse(identity.birth.value) <= cutoff : void 0;
2791
+ if (typeof cutoff === "number" || identity.birth.timeNamespace !== cutoff.timeNamespace || !identity.clockTicksPerSecond) return;
2545
2792
  return BigInt(identity.birth.startTicks) * 100n < (cutoff.centiseconds + 1n) * identity.clockTicksPerSecond;
2546
2793
  }
2547
2794
  async signal(signal, timeoutMs) {
2548
2795
  if (!await this.capture(timeoutMs) || this.retired) return;
2549
2796
  for (const pid of this.identities.keys()) try {
2550
2797
  process.kill(pid, signal);
2551
- } catch {
2552
- this.identities.delete(pid);
2553
- }
2798
+ } catch {}
2554
2799
  }
2555
2800
  hasTrackedProcesses() {
2556
2801
  return this.identities.size > 0;
2557
2802
  }
2803
+ hasUnresolvedOwnership() {
2804
+ return this.unresolvedOwnership || !this.ownProcessGroup && this.failedRootObservation && !isChildProcessRunning(this.child);
2805
+ }
2558
2806
  async waitForExit(timeoutMs) {
2559
2807
  const deadline = performance.now() + timeoutMs;
2560
2808
  do {
@@ -2571,46 +2819,6 @@ var ProcessDescendants = class {
2571
2819
  }
2572
2820
  };
2573
2821
  //#endregion
2574
- //#region src/session/lock-owner.ts
2575
- const OBSERVATION_REUSE_MS = 1e3;
2576
- const IDENTITY_QUERY_TIMEOUT_MS = 1e3;
2577
- function lockOwnerPid(value) {
2578
- if (!value || typeof value !== "object" || !("pid" in value)) return;
2579
- const pid = value.pid;
2580
- return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
2581
- }
2582
- function canReuseObservation(preserved, pid, identity, fresh) {
2583
- return !fresh && preserved?.pid === pid && compareProcessBirthIdentity(preserved.identity, identity) === "matching" && performance.now() - preserved.checkedAt < OBSERVATION_REUSE_MS;
2584
- }
2585
- async function createLockOwner(options = {}) {
2586
- const { signal, capturedIdentity } = options;
2587
- signal?.throwIfAborted();
2588
- const processIdentity = capturedIdentity === void 0 ? await getOwnProcessIdentity() : capturedIdentity.processIdentity;
2589
- signal?.throwIfAborted();
2590
- let preserved;
2591
- return {
2592
- payload: {
2593
- pid: process.pid,
2594
- processIdentity
2595
- },
2596
- async hasExited(payload, fresh, signal) {
2597
- signal?.throwIfAborted();
2598
- const pid = lockOwnerPid(payload);
2599
- if (!pid) return false;
2600
- const identity = parseProcessBirthIdentity(payload.processIdentity);
2601
- if (identity && canReuseObservation(preserved, pid, identity, fresh)) return false;
2602
- const incarnation = await observeProcessIncarnation(pid, identity, IDENTITY_QUERY_TIMEOUT_MS);
2603
- signal?.throwIfAborted();
2604
- if (identity && incarnation !== "gone") preserved = {
2605
- pid,
2606
- identity,
2607
- checkedAt: performance.now()
2608
- };
2609
- return incarnation === "gone";
2610
- }
2611
- };
2612
- }
2613
- //#endregion
2614
2822
  //#region src/session/queue/paths.ts
2615
2823
  function shortHash(value, length) {
2616
2824
  return createHash("sha256").update(value).digest("hex").slice(0, length);
@@ -2697,16 +2905,30 @@ function retirementTimeRemaining(deadline, maximumMs = 2e3) {
2697
2905
  if (remaining <= 0) throw queueRetirementIncomplete();
2698
2906
  return Math.min(maximumMs, remaining);
2699
2907
  }
2700
- async function captureQueueRetirementReceipt(owner, previous, rootRunning, deadline) {
2908
+ async function readRetirementSnapshot(deadline) {
2909
+ if (process.platform !== "win32") throw queueRetirementIncomplete();
2910
+ return await readProcessTable(retirementTimeRemaining(deadline)).catch(() => {
2911
+ throw queueRetirementIncomplete();
2912
+ });
2913
+ }
2914
+ function observeRetirementSnapshot(witness, table) {
2915
+ if (process.platform !== "win32" || !isWindowsRetirementIdentity(witness.processIdentity)) return "unknown";
2916
+ const observed = table?.get(witness.pid);
2917
+ const comparison = observed ? compareProcessBirthIdentity(witness.processIdentity, observed.birth) : "unknown";
2918
+ if (comparison === "different" || isProcessDefinitelyDead(witness.pid)) return "gone";
2919
+ return witness.pid === process.pid ? "unknown" : comparison;
2920
+ }
2921
+ function isWindowsRetirementIdentity(identity) {
2922
+ return identity === void 0 || identity.kind === "windows-creation";
2923
+ }
2924
+ function captureQueueRetirementReceipt(owner, previous, rootRunning, snapshot) {
2701
2925
  const root = parseWitness({
2702
2926
  pid: owner.pid,
2703
2927
  processIdentity: owner.processIdentity
2704
2928
  });
2705
2929
  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 : []);
2930
+ const saved = previous ? previous.descendants : [];
2931
+ const table = new Map(snapshot);
2710
2932
  const owned = matchingSnapshotPids(table, rootRunning ? [root, ...saved] : saved);
2711
2933
  if (rootRunning && !owned.has(root.pid)) throw queueRetirementIncomplete();
2712
2934
  table.delete(1);
@@ -2716,7 +2938,7 @@ async function captureQueueRetirementReceipt(owner, previous, rootRunning, deadl
2716
2938
  const receipt = {
2717
2939
  ownerGeneration: owner.ownerGeneration,
2718
2940
  root,
2719
- descendants: mergeSnapshotDescendants(table, owned, saved)
2941
+ descendants: unsettledSnapshotWitnesses(snapshot, mergeSnapshotDescendants(table, owned, saved))
2720
2942
  };
2721
2943
  if (!parseQueueRetirementReceipt(receipt, owner)) throw queueRetirementIncomplete();
2722
2944
  return receipt;
@@ -2752,7 +2974,11 @@ async function observeRetirementWitness(witness, deadline) {
2752
2974
  }
2753
2975
  async function assertQueueRetirementComplete(receipt, deadline) {
2754
2976
  if (receipt === null) throw queueRetirementIncomplete();
2755
- for (const witness of receipt?.descendants ?? []) if (await observeRetirementWitness(witness, deadline) !== "gone") throw queueRetirementIncomplete();
2977
+ const witnesses = receipt?.descendants ?? [];
2978
+ if (witnesses.length === 0) return;
2979
+ const live = witnesses.filter((witness) => observeRetirementSnapshot(witness, void 0) !== "gone");
2980
+ const table = live.length > 0 ? await readRetirementSnapshot(deadline) : void 0;
2981
+ for (const witness of live) if (observeRetirementSnapshot(witness, table) !== "gone") throw queueRetirementIncomplete();
2756
2982
  }
2757
2983
  //#endregion
2758
2984
  //#region src/session/queue/lease-mutation.ts
@@ -3255,17 +3481,37 @@ function unverifiedQueueOwnerError(owner) {
3255
3481
  function retirementDeadline() {
3256
3482
  return performance.now() + PROCESS_SIGTERM_GRACE_MS + PROCESS_SIGKILL_GRACE_MS;
3257
3483
  }
3484
+ async function createQueueOwnerRetirement(deadline) {
3485
+ const snapshot = process.platform === "win32" ? await readRetirementSnapshot(deadline).catch(() => void 0) : void 0;
3486
+ return {
3487
+ gone: false,
3488
+ unverified: false,
3489
+ leaseReleased: false,
3490
+ deadline,
3491
+ snapshot,
3492
+ capturedIdentity: process.platform === "win32" ? { processIdentity: snapshot?.get(process.pid)?.birth } : void 0
3493
+ };
3494
+ }
3258
3495
  async function observeRetiringOwner(owner, deadline) {
3259
3496
  return await observeProcessIncarnation(owner.pid, owner.processIdentity, process.platform === "win32" ? retirementTimeRemaining(deadline) : void 0);
3260
3497
  }
3261
3498
  function retirementBlockedByHeartbeat(owner, signal, requireStale) {
3262
3499
  return requireStale && owner.retirement === void 0 && signal === "SIGTERM" && !isQueueOwnerHeartbeatStale(owner);
3263
3500
  }
3264
- async function prepareWindowsRetirement(owner, deadline) {
3265
- if (owner.retirement === null) throw queueRetirementIncomplete();
3266
- owner.retirement = await captureQueueRetirementReceipt(owner, owner.retirement, true, deadline);
3501
+ async function prepareWindowsRetirement(owner, table, deadline) {
3502
+ if (owner.retirement === null || !table) throw queueRetirementIncomplete();
3503
+ owner.retirement = captureQueueRetirementReceipt(owner, owner.retirement, true, table);
3267
3504
  await replaceQueueOwnerRecord(owner);
3268
- return await observeRetirementWitness(owner.retirement.root, deadline) === "matching";
3505
+ return await observeRetirementWitness(owner.retirement.root, deadline);
3506
+ }
3507
+ async function prepareRetiringOwner(owner, signal, retirement) {
3508
+ const windows = process.platform === "win32";
3509
+ const table = windows && signal !== "SIGTERM" ? await readRetirementSnapshot(retirement.deadline) : retirement.snapshot;
3510
+ let identity = windows ? observeRetirementSnapshot(owner, table) : await observeRetiringOwner(owner, retirement.deadline);
3511
+ if (identity === "matching" && windows) identity = await prepareWindowsRetirement(owner, table, retirement.deadline);
3512
+ retirement.gone = identity === "gone";
3513
+ retirement.unverified = identity === "unknown";
3514
+ return identity === "matching";
3269
3515
  }
3270
3516
  async function dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement) {
3271
3517
  return await withQueueLeaseMutation(owner.sessionId, async () => {
@@ -3273,26 +3519,15 @@ async function dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, ret
3273
3519
  retirement.leaseReleased = current?.pid !== owner.pid;
3274
3520
  if (!matchesQueueOwner(current, owner) || retirementBlockedByHeartbeat(current, signal, requireStale)) return false;
3275
3521
  if (current.retirement === null) throw queueRetirementIncomplete();
3276
- const identity = await observeRetiringOwner(current, retirement.deadline);
3277
- if (identity !== "matching") {
3278
- retirement.gone = identity === "gone";
3279
- retirement.unverified = identity === "unknown";
3280
- return false;
3281
- }
3282
- if (process.platform === "win32" && !await prepareWindowsRetirement(current, retirement.deadline)) return false;
3522
+ if (!await prepareRetiringOwner(current, signal, retirement)) return false;
3283
3523
  return await dispatchQueueOwnerSignal(owner.pid, signal, retirement.deadline);
3284
- });
3524
+ }, { capturedIdentity: retirement.capturedIdentity });
3285
3525
  }
3286
- async function retireQueueOwner(owner, requireStale, deadline) {
3287
- const retirement = {
3288
- gone: false,
3289
- unverified: false,
3290
- leaseReleased: false,
3291
- deadline
3292
- };
3293
- await terminateWithDispatch(owner.pid, (signal) => dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement), isProcessDefinitelyDead, process.platform === "win32" ? deadline : Infinity);
3526
+ async function retireQueueOwner(owner, requireStale, retirement) {
3527
+ const { deadline } = retirement;
3528
+ const exited = await terminateWithDispatch(owner.pid, (signal) => dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement), isProcessDefinitelyDead, process.platform === "win32" ? deadline : Infinity);
3294
3529
  if (await waitForReleasedOwner(owner, requireStale, retirement)) return false;
3295
- const gone = retirement.gone || await observeRetiringOwner(owner, deadline) === "gone";
3530
+ const gone = retirement.gone || process.platform === "win32" && exited || await observeRetiringOwner(owner, deadline) === "gone";
3296
3531
  if (retirement.unverified && !gone) throw unverifiedQueueOwnerError(owner);
3297
3532
  return gone;
3298
3533
  }
@@ -3307,44 +3542,80 @@ async function waitForReleasedOwner(owner, requireStale, retirement) {
3307
3542
  }
3308
3543
  return false;
3309
3544
  }
3310
- async function retireRecordedDescendants(owner, deadline) {
3545
+ async function retireRecordedDescendants(owner, retirement) {
3311
3546
  await withQueueLeaseMutation(owner.sessionId, async () => {
3312
3547
  const current = await readQueueOwnerRecord(owner.sessionId);
3313
3548
  if (!matchesQueueOwner(current, owner) || current.retirement === void 0) return;
3314
3549
  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;
3550
+ await retireSnapshotDescendants(current, current.retirement, retirement.deadline);
3551
+ }, { capturedIdentity: retirement.capturedIdentity });
3552
+ }
3553
+ function hasNewRetirementWitness(previous, receipt) {
3554
+ const saved = new Map(previous.descendants.map((witness) => [witness.pid, witness.processIdentity]));
3555
+ return receipt.descendants.some((witness) => compareProcessBirthIdentity(saved.get(witness.pid), witness.processIdentity) !== "matching");
3556
+ }
3557
+ async function retireSnapshotDescendants(owner, receipt, deadline) {
3558
+ while (receipt.descendants.length > 0) {
3559
+ const table = await readRetirementSnapshot(deadline);
3560
+ const discovered = captureQueueRetirementReceipt(owner, receipt, false, table);
3561
+ if (hasNewRetirementWitness(receipt, discovered)) {
3562
+ receipt = discovered;
3563
+ owner.retirement = receipt;
3564
+ await replaceQueueOwnerRecord(owner);
3565
+ continue;
3566
+ }
3567
+ signalRecordedDescendants(discovered, table, deadline);
3568
+ await waitForRecordedDescendants(discovered, deadline);
3569
+ discovered.descendants = discovered.descendants.filter((witness) => !isProcessDefinitelyDead(witness.pid));
3570
+ receipt = discovered;
3571
+ owner.retirement = receipt;
3572
+ await replaceQueueOwnerRecord(owner);
3327
3573
  }
3328
- return false;
3329
3574
  }
3330
- async function signalRecordedDescendants(receipt, deadline) {
3575
+ function leafFirstRetirementWitnesses(receipt, table) {
3576
+ const children = new Map(receipt.descendants.map((witness) => [witness.pid, 0]));
3331
3577
  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
- }
3578
+ const parent = retirementParent(witness.pid, table);
3579
+ const count = children.get(parent);
3580
+ if (count !== void 0) children.set(parent, count + 1);
3581
+ }
3582
+ const pending = receipt.descendants.filter((witness) => children.get(witness.pid) === 0);
3583
+ const byPid = new Map(receipt.descendants.map((witness) => [witness.pid, witness]));
3584
+ for (let index = 0; index < pending.length; index += 1) {
3585
+ const parent = retirementParent(pending[index].pid, table);
3586
+ const count = children.get(parent);
3587
+ if (count !== void 0) {
3588
+ const remaining = count - 1;
3589
+ children.set(parent, remaining);
3590
+ if (remaining === 0) pending.push(byPid.get(parent));
3341
3591
  }
3342
3592
  }
3593
+ if (pending.length !== receipt.descendants.length) throw queueRetirementIncomplete();
3594
+ return pending;
3595
+ }
3596
+ function retirementParent(pid, table) {
3597
+ const identity = table.get(pid);
3598
+ return identity && !hasStaleWindowsParent(identity, table.get(identity.parentPid)) ? identity.parentPid : 0;
3599
+ }
3600
+ function signalRecordedDescendants(receipt, table, deadline) {
3601
+ if (receipt.descendants.some((witness) => observeRetirementSnapshot(witness, table) === "unknown")) throw queueRetirementIncomplete();
3602
+ const ordered = leafFirstRetirementWitnesses(receipt, table);
3603
+ let failure;
3604
+ for (const witness of ordered) {
3605
+ if (observeRetirementSnapshot(witness, table) !== "matching") continue;
3606
+ retirementTimeRemaining(deadline);
3607
+ try {
3608
+ process.kill(witness.pid, "SIGKILL");
3609
+ } catch (error) {
3610
+ if (!isProcessDefinitelyDead(witness.pid)) failure ??= error;
3611
+ }
3612
+ }
3613
+ if (failure) throw failure;
3343
3614
  }
3344
3615
  async function waitForRecordedDescendants(receipt, deadline) {
3345
- while (true) {
3346
- if (!await hasSurvivingRetirementDescendant(receipt, deadline)) return;
3347
- await waitMs(Math.min(1e3, retirementTimeRemaining(deadline)));
3616
+ for (let waited = 0; waited < 1e3; waited += PROCESS_POLL_MS) {
3617
+ if (receipt.descendants.every((witness) => isProcessDefinitelyDead(witness.pid))) return;
3618
+ await waitMs(Math.min(PROCESS_POLL_MS, retirementTimeRemaining(deadline)));
3348
3619
  }
3349
3620
  }
3350
3621
  async function assertNoPendingRetirement(owner) {
@@ -3360,12 +3631,13 @@ async function terminateQueueOwnerForSession(sessionId, expectedOwner, requireSt
3360
3631
  return;
3361
3632
  }
3362
3633
  const deadline = retirementDeadline();
3363
- if (!await retireQueueOwner(owner, requireStale, deadline)) {
3634
+ const retirement = await createQueueOwnerRetirement(deadline);
3635
+ if (!await retireQueueOwner(owner, requireStale, retirement)) {
3364
3636
  await assertNoPendingRetirement(owner);
3365
3637
  return;
3366
3638
  }
3367
- await retireRecordedDescendants(owner, deadline);
3368
- await cleanupQueueOwnerFiles(sessionId, owner.socketPath, () => ownsQueueLease(owner), void 0, deadline);
3639
+ await retireRecordedDescendants(owner, retirement);
3640
+ await cleanupQueueOwnerFiles(sessionId, owner.socketPath, () => ownsQueueLease(owner), retirement.capturedIdentity, deadline);
3369
3641
  }
3370
3642
  async function waitMs(ms) {
3371
3643
  await new Promise((resolve) => {
@@ -3815,7 +4087,7 @@ function parseStringResultOwnerMessage(message, context, type, field) {
3815
4087
  function parseSetModelOwnerMessage(message, context) {
3816
4088
  if (typeof message.modelId !== "string") return null;
3817
4089
  const response = asRecord(message.response);
3818
- if (message.response !== void 0 && (!response || !Array.isArray(response.configOptions))) return null;
4090
+ if (message.response !== void 0 && (!response || response.configOptions !== void 0 && !Array.isArray(response.configOptions))) return null;
3819
4091
  return {
3820
4092
  type: "set_model_result",
3821
4093
  ...context,
@@ -3825,7 +4097,7 @@ function parseSetModelOwnerMessage(message, context) {
3825
4097
  }
3826
4098
  function parseSetConfigOptionOwnerMessage(message, context) {
3827
4099
  const response = asRecord(message.response);
3828
- if (!response || !Array.isArray(response.configOptions)) return null;
4100
+ if (!response || response.configOptions !== void 0 && !Array.isArray(response.configOptions)) return null;
3829
4101
  return {
3830
4102
  type: "set_config_option_result",
3831
4103
  ...context,
@@ -3870,8 +4142,611 @@ function assertQueueRequestSize(line) {
3870
4142
  });
3871
4143
  }
3872
4144
  //#endregion
4145
+ //#region src/session/queue/socket-output.ts
4146
+ const OUTPUT_CHUNK_BYTES = 65536;
4147
+ const DRAIN_TIMEOUT_MS = 1e3;
4148
+ const DEFAULT_LIMITS = Object.freeze({
4149
+ observerBytes: 67108864,
4150
+ totalBytes: 268435456,
4151
+ observers: 64
4152
+ });
4153
+ var QueueOutputBudget = class {
4154
+ limits;
4155
+ bytes = 0;
4156
+ files = 0;
4157
+ observers = 0;
4158
+ constructor(limits = DEFAULT_LIMITS) {
4159
+ this.limits = limits;
4160
+ }
4161
+ acquire() {
4162
+ if (this.files >= this.limits.observers) throw new Error("Queue output spool descriptor limit reached");
4163
+ this.files += 1;
4164
+ }
4165
+ grow(bytes) {
4166
+ if (bytes > this.limits.totalBytes - this.bytes) throw new Error("Queue output spool storage limit reached");
4167
+ this.bytes += bytes;
4168
+ }
4169
+ release(bytes) {
4170
+ this.bytes -= bytes;
4171
+ this.files -= 1;
4172
+ }
4173
+ acquireObserver() {
4174
+ if (this.observers >= this.limits.observers) throw new Error("Queue blocked observer limit reached");
4175
+ this.observers += 1;
4176
+ }
4177
+ releaseObserver() {
4178
+ this.observers -= 1;
4179
+ }
4180
+ };
4181
+ var OutputRing = class {
4182
+ budget;
4183
+ fd;
4184
+ physicalBytes = 0;
4185
+ readOffset = 0;
4186
+ length = 0;
4187
+ constructor(budget) {
4188
+ this.budget = budget;
4189
+ budget.acquire();
4190
+ try {
4191
+ const root = resolveSecureTempRoot({ fallbackPrefix: "acpx-output" });
4192
+ const file = path.join(root, `${randomUUID()}.tmp`);
4193
+ this.fd = fs.openSync(file, "wx+", 384);
4194
+ fs.unlinkSync(file);
4195
+ } catch (error) {
4196
+ if (this.fd === void 0) budget.release(0);
4197
+ else this.close();
4198
+ throw error;
4199
+ }
4200
+ }
4201
+ append(bytes, offset) {
4202
+ const capacity = this.budget.limits.observerBytes;
4203
+ if (bytes.length - offset > capacity - this.length) throw new Error("Queue observer output backlog limit reached");
4204
+ while (offset < bytes.length) {
4205
+ const position = (this.readOffset + this.length) % capacity;
4206
+ const count = Math.min(OUTPUT_CHUNK_BYTES, bytes.length - offset, capacity - position);
4207
+ const growth = Math.max(0, position + count - this.physicalBytes);
4208
+ this.budget.grow(growth);
4209
+ this.physicalBytes += growth;
4210
+ const written = fs.writeSync(this.fd, bytes, offset, count, position);
4211
+ if (written <= 0) throw new Error("Queue output spool write made no progress");
4212
+ offset += written;
4213
+ this.length += written;
4214
+ }
4215
+ }
4216
+ read() {
4217
+ const capacity = this.budget.limits.observerBytes;
4218
+ const bytes = Buffer.allocUnsafeSlow(Math.min(OUTPUT_CHUNK_BYTES, this.length, capacity - this.readOffset));
4219
+ let offset = 0;
4220
+ while (offset < bytes.length) {
4221
+ const read = fs.readSync(this.fd, bytes, offset, bytes.length - offset, this.readOffset + offset);
4222
+ if (read <= 0) throw new Error("Queue output spool ended before its committed bytes");
4223
+ offset += read;
4224
+ }
4225
+ this.readOffset = (this.readOffset + bytes.length) % capacity;
4226
+ this.length -= bytes.length;
4227
+ return bytes;
4228
+ }
4229
+ close() {
4230
+ const fd = this.fd;
4231
+ this.fd = void 0;
4232
+ if (fd !== void 0) {
4233
+ fs.closeSync(fd);
4234
+ this.budget.release(this.physicalBytes);
4235
+ }
4236
+ }
4237
+ };
4238
+ var QueueSocketOutput = class {
4239
+ socket;
4240
+ budget;
4241
+ ring;
4242
+ blocked = false;
4243
+ ending = false;
4244
+ disposed = false;
4245
+ reserved = false;
4246
+ pendingWrites = 0;
4247
+ constructor(socket, budget) {
4248
+ this.socket = socket;
4249
+ this.budget = budget;
4250
+ socket.on("drain", this.onDrain);
4251
+ socket.once("close", () => {
4252
+ this.disposed = true;
4253
+ socket.off("drain", this.onDrain);
4254
+ this.closeSpool();
4255
+ this.releaseObserver();
4256
+ });
4257
+ }
4258
+ send(message) {
4259
+ if (this.ending || this.socket.destroyed || !this.socket.writable) return;
4260
+ try {
4261
+ this.writeFrame(Buffer.from(`${JSON.stringify(message)}\n`));
4262
+ } catch (error) {
4263
+ this.fail(error);
4264
+ }
4265
+ }
4266
+ writeFrame(bytes) {
4267
+ let offset = 0;
4268
+ while (!this.blocked && offset < bytes.length) {
4269
+ const end = Math.min(offset + OUTPUT_CHUNK_BYTES, bytes.length);
4270
+ const chunk = Buffer.allocUnsafeSlow(end - offset);
4271
+ bytes.copy(chunk, 0, offset, end);
4272
+ this.write(chunk);
4273
+ offset = end;
4274
+ }
4275
+ if (offset < bytes.length) {
4276
+ this.ring ??= new OutputRing(this.budget);
4277
+ this.ring.append(bytes, offset);
4278
+ }
4279
+ }
4280
+ end() {
4281
+ if (this.ending || this.socket.destroyed) return;
4282
+ this.ending = true;
4283
+ this.armTimeout();
4284
+ this.finishIfDrained();
4285
+ }
4286
+ write(bytes) {
4287
+ if (!this.reserved) {
4288
+ this.budget.acquireObserver();
4289
+ this.reserved = true;
4290
+ }
4291
+ this.pendingWrites += 1;
4292
+ try {
4293
+ this.blocked = !this.socket.write(bytes, this.onWrite);
4294
+ } catch (error) {
4295
+ this.pendingWrites -= 1;
4296
+ throw error;
4297
+ }
4298
+ if (this.blocked && !this.socket.timeout) this.armTimeout();
4299
+ }
4300
+ onWrite = (error) => {
4301
+ this.pendingWrites -= 1;
4302
+ if (this.disposed) return;
4303
+ if (error) {
4304
+ this.fail(error);
4305
+ return;
4306
+ }
4307
+ this.releaseIfIdle();
4308
+ };
4309
+ armTimeout() {
4310
+ if (process.platform !== "win32") this.socket.setTimeout(DRAIN_TIMEOUT_MS);
4311
+ }
4312
+ onDrain = () => {
4313
+ if (this.disposed) return;
4314
+ this.blocked = false;
4315
+ if (!this.ending) this.socket.setTimeout(0);
4316
+ try {
4317
+ this.pump();
4318
+ this.releaseIfIdle();
4319
+ this.finishIfDrained();
4320
+ } catch (error) {
4321
+ this.fail(error);
4322
+ }
4323
+ };
4324
+ pump() {
4325
+ while (!this.blocked && this.ring && this.ring.length > 0) this.write(this.ring.read());
4326
+ if (this.ring?.length === 0) this.closeSpool();
4327
+ }
4328
+ finishIfDrained() {
4329
+ if (this.ending && !this.ring?.length && !this.socket.writableEnded) this.socket.end(() => this.socket.destroy());
4330
+ }
4331
+ releaseObserver() {
4332
+ if (this.reserved) {
4333
+ this.reserved = false;
4334
+ this.budget.releaseObserver();
4335
+ }
4336
+ }
4337
+ releaseIfIdle() {
4338
+ if (this.pendingWrites === 0 && !this.blocked && !this.ring?.length) this.releaseObserver();
4339
+ }
4340
+ closeSpool() {
4341
+ const ring = this.ring;
4342
+ this.ring = void 0;
4343
+ try {
4344
+ ring?.close();
4345
+ } catch (error) {
4346
+ this.socket.destroy(error instanceof Error ? error : new Error(String(error)));
4347
+ }
4348
+ }
4349
+ fail(error) {
4350
+ this.disposed = true;
4351
+ this.socket.destroy(error instanceof Error ? error : new Error(String(error)));
4352
+ this.closeSpool();
4353
+ }
4354
+ };
4355
+ //#endregion
4356
+ //#region src/session/queue/ipc-server.ts
4357
+ const QUEUE_SOCKET_DRAIN_TIMEOUT_MS = 1e3;
4358
+ function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
4359
+ return {
4360
+ type: "error",
4361
+ requestId,
4362
+ ownerGeneration: void 0,
4363
+ code: "RUNTIME",
4364
+ detailCode,
4365
+ origin: "queue",
4366
+ retryable: options.retryable,
4367
+ message
4368
+ };
4369
+ }
4370
+ function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
4371
+ const normalized = normalizeOutputError(error, {
4372
+ defaultCode: "RUNTIME",
4373
+ origin: "queue",
4374
+ detailCode,
4375
+ retryable: options.retryable
4376
+ });
4377
+ return {
4378
+ type: "error",
4379
+ requestId,
4380
+ code: normalized.code,
4381
+ detailCode: normalized.detailCode,
4382
+ origin: normalized.origin,
4383
+ message: normalized.message,
4384
+ retryable: normalized.retryable,
4385
+ acp: normalized.acp
4386
+ };
4387
+ }
4388
+ var SessionQueueOwner = class SessionQueueOwner {
4389
+ server;
4390
+ controlHandlers;
4391
+ ownerGeneration;
4392
+ maxQueueDepth;
4393
+ maxRequestBytes;
4394
+ onQueueDepthChanged;
4395
+ pending = [];
4396
+ activeTask;
4397
+ waiters = [];
4398
+ sockets = /* @__PURE__ */ new Set();
4399
+ taskSockets = /* @__PURE__ */ new Set();
4400
+ drainingSockets = /* @__PURE__ */ new Set();
4401
+ outputBudget = new QueueOutputBudget();
4402
+ outputs = /* @__PURE__ */ new Map();
4403
+ closed = false;
4404
+ constructor(server, controlHandlers, lease, options) {
4405
+ this.server = server;
4406
+ this.controlHandlers = controlHandlers;
4407
+ this.ownerGeneration = lease.ownerGeneration;
4408
+ this.maxQueueDepth = Math.max(1, Math.round(options.maxQueueDepth));
4409
+ this.maxRequestBytes = queueRequestByteLimit(options.maxRequestBytes?.toString());
4410
+ this.onQueueDepthChanged = options.onQueueDepthChanged;
4411
+ }
4412
+ static async start(lease, controlHandlers, options = { maxQueueDepth: 16 }) {
4413
+ const ownerRef = { current: void 0 };
4414
+ const server = net.createServer((socket) => {
4415
+ ownerRef.current?.handleConnection(socket);
4416
+ });
4417
+ ownerRef.current = new SessionQueueOwner(server, controlHandlers, lease, options);
4418
+ await new Promise((resolve, reject) => {
4419
+ const onListening = () => {
4420
+ server.off("error", onError);
4421
+ resolve();
4422
+ };
4423
+ const onError = (error) => {
4424
+ server.off("listening", onListening);
4425
+ reject(error);
4426
+ };
4427
+ server.once("listening", onListening);
4428
+ server.once("error", onError);
4429
+ server.listen(lease.socketPath);
4430
+ });
4431
+ return ownerRef.current;
4432
+ }
4433
+ beginShutdown() {
4434
+ if (this.closed) return;
4435
+ this.closed = true;
4436
+ for (const waiter of this.waiters.splice(0)) waiter(void 0);
4437
+ for (const task of this.pending.splice(0)) {
4438
+ if (task.waitForCompletion) task.send(makeQueueOwnerError(task.requestId, "Queue owner shutting down before prompt execution", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
4439
+ task.close();
4440
+ }
4441
+ for (const socket of this.sockets) if (!this.taskSockets.has(socket) && !this.drainingSockets.has(socket)) socket.destroy();
4442
+ this.emitQueueDepth();
4443
+ }
4444
+ async close() {
4445
+ this.beginShutdown();
4446
+ await new Promise((resolve) => {
4447
+ const forceCloseTimer = setTimeout(() => {
4448
+ for (const socket of this.sockets) socket.destroy();
4449
+ }, QUEUE_SOCKET_DRAIN_TIMEOUT_MS);
4450
+ forceCloseTimer.unref();
4451
+ this.server.close(() => {
4452
+ clearTimeout(forceCloseTimer);
4453
+ resolve();
4454
+ });
4455
+ });
4456
+ }
4457
+ async nextTask(timeoutMs) {
4458
+ if (this.pending.length > 0) {
4459
+ const task = this.pending.shift();
4460
+ this.emitQueueDepth();
4461
+ if (task) {
4462
+ this.activeTask = task;
4463
+ recordPerfDuration("queue.owner.wait_ms", Date.now() - task.enqueuedAt);
4464
+ }
4465
+ return task;
4466
+ }
4467
+ if (this.closed) return;
4468
+ return await new Promise((resolve) => {
4469
+ const timer = timeoutMs != null && setTimeout(() => {
4470
+ const index = this.waiters.indexOf(waiter);
4471
+ if (index >= 0) this.waiters.splice(index, 1);
4472
+ resolve(void 0);
4473
+ }, Math.max(0, timeoutMs));
4474
+ const waiter = (task) => {
4475
+ if (timer) clearTimeout(timer);
4476
+ resolve(task);
4477
+ };
4478
+ this.waiters.push(waiter);
4479
+ });
4480
+ }
4481
+ queueDepth() {
4482
+ return this.pending.length;
4483
+ }
4484
+ completeTask(task) {
4485
+ if (this.activeTask === task) this.activeTask = void 0;
4486
+ }
4487
+ async cancelPrompt(targetRequestId) {
4488
+ if (targetRequestId === void 0) return await this.controlHandlers.cancelPrompt();
4489
+ const index = this.pending.findIndex((task) => task.requestId === targetRequestId);
4490
+ if (index >= 0) {
4491
+ const [task] = this.pending.splice(index, 1);
4492
+ task.send(makeQueueOwnerError(task.requestId, "Prompt cancelled before execution", "QUEUE_REQUEST_CANCELLED", { retryable: false }));
4493
+ task.close();
4494
+ this.emitQueueDepth();
4495
+ return true;
4496
+ }
4497
+ return this.activeTask?.requestId === targetRequestId ? await this.controlHandlers.cancelPrompt() : false;
4498
+ }
4499
+ emitQueueDepth() {
4500
+ this.onQueueDepthChanged?.(this.pending.length);
4501
+ }
4502
+ enqueue(task) {
4503
+ if (this.closed) {
4504
+ task.send(makeQueueOwnerError(task.requestId, "Queue owner is shutting down", "QUEUE_OWNER_SHUTTING_DOWN", { retryable: true }));
4505
+ task.close();
4506
+ return false;
4507
+ }
4508
+ if (this.activeTask?.requestId === task.requestId || this.pending.some((queued) => queued.requestId === task.requestId)) {
4509
+ task.send(makeQueueOwnerError(task.requestId, "Request is already queued or active", "QUEUE_REQUEST_DUPLICATE", { retryable: false }));
4510
+ task.close();
4511
+ return false;
4512
+ }
4513
+ const waiter = this.waiters.shift();
4514
+ if (waiter) {
4515
+ this.activeTask = task;
4516
+ waiter(task);
4517
+ return true;
4518
+ }
4519
+ if (this.pending.length >= this.maxQueueDepth) {
4520
+ task.send({
4521
+ ...makeQueueOwnerError(task.requestId, `Queue owner is overloaded (${this.pending.length}/${this.maxQueueDepth} queued)`, "QUEUE_OWNER_OVERLOADED", { retryable: true }),
4522
+ ownerGeneration: this.ownerGeneration
4523
+ });
4524
+ task.close();
4525
+ return false;
4526
+ }
4527
+ this.pending.push(task);
4528
+ this.emitQueueDepth();
4529
+ return true;
4530
+ }
4531
+ handleControlRequest(options) {
4532
+ this.taskSockets.add(options.socket);
4533
+ this.writeQueueMessage(options.socket, {
4534
+ type: "accepted",
4535
+ requestId: options.requestId,
4536
+ ownerGeneration: this.ownerGeneration
4537
+ });
4538
+ options.run().then((message) => {
4539
+ this.writeQueueMessage(options.socket, {
4540
+ ...message,
4541
+ ownerGeneration: this.ownerGeneration
4542
+ });
4543
+ }).catch((error) => {
4544
+ this.writeQueueMessage(options.socket, {
4545
+ ...makeQueueOwnerErrorFromUnknown(options.requestId, error, "QUEUE_CONTROL_REQUEST_FAILED"),
4546
+ ownerGeneration: this.ownerGeneration
4547
+ });
4548
+ }).finally(() => {
4549
+ this.endSocket(options.socket);
4550
+ });
4551
+ }
4552
+ writeQueueMessage(socket, message) {
4553
+ this.outputs.get(socket)?.send(message);
4554
+ }
4555
+ endSocket(socket) {
4556
+ if (socket.destroyed || this.drainingSockets.has(socket)) return;
4557
+ this.taskSockets.delete(socket);
4558
+ this.drainingSockets.add(socket);
4559
+ this.outputs.get(socket)?.end();
4560
+ }
4561
+ failRequest(socket, requestId, message, detailCode) {
4562
+ this.writeQueueMessage(socket, {
4563
+ ...makeQueueOwnerError(requestId, message, detailCode, { retryable: false }),
4564
+ ownerGeneration: this.ownerGeneration
4565
+ });
4566
+ this.endSocket(socket);
4567
+ }
4568
+ parseRequestLine(socket, line) {
4569
+ let parsed;
4570
+ try {
4571
+ parsed = JSON.parse(line);
4572
+ } catch {
4573
+ this.failRequest(socket, "unknown", "Invalid queue request payload", "QUEUE_REQUEST_PAYLOAD_INVALID_JSON");
4574
+ return;
4575
+ }
4576
+ const request = parseQueueRequest(parsed);
4577
+ if (!request) this.failRequest(socket, "unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
4578
+ return request ?? void 0;
4579
+ }
4580
+ rejectStaleOwnerGeneration(socket, request) {
4581
+ if (request.ownerGeneration === void 0 || this.ownerGeneration === void 0 || request.ownerGeneration === this.ownerGeneration) return false;
4582
+ this.failRequest(socket, request.requestId, "Queue request targeted a stale queue owner generation", "QUEUE_OWNER_GENERATION_MISMATCH");
4583
+ return true;
4584
+ }
4585
+ rejectClosedOwner(socket, request) {
4586
+ if (!this.closed) return false;
4587
+ this.writeQueueMessage(socket, {
4588
+ ...makeQueueOwnerError(request.requestId, "Queue owner is shutting down", "QUEUE_OWNER_CLOSED", { retryable: true }),
4589
+ ownerGeneration: this.ownerGeneration
4590
+ });
4591
+ this.endSocket(socket);
4592
+ return true;
4593
+ }
4594
+ handleControlQueueRequest(socket, request) {
4595
+ if (request.type === "cancel_prompt") {
4596
+ this.handleControlRequest({
4597
+ socket,
4598
+ requestId: request.requestId,
4599
+ run: async () => ({
4600
+ type: "cancel_result",
4601
+ requestId: request.requestId,
4602
+ cancelled: await this.cancelPrompt(request.targetRequestId)
4603
+ })
4604
+ });
4605
+ return true;
4606
+ }
4607
+ if (request.type === "close_session") {
4608
+ this.handleControlRequest({
4609
+ socket,
4610
+ requestId: request.requestId,
4611
+ run: async () => ({
4612
+ type: "close_session_result",
4613
+ requestId: request.requestId,
4614
+ closed: await this.controlHandlers.closeSession(request.timeoutMs)
4615
+ })
4616
+ });
4617
+ return true;
4618
+ }
4619
+ return this.handleSessionControlQueueRequest(socket, request);
4620
+ }
4621
+ handleSessionControlQueueRequest(socket, request) {
4622
+ if (request.type === "set_mode") {
4623
+ this.handleControlRequest({
4624
+ socket,
4625
+ requestId: request.requestId,
4626
+ run: async () => {
4627
+ await this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs);
4628
+ return {
4629
+ type: "set_mode_result",
4630
+ requestId: request.requestId,
4631
+ modeId: request.modeId
4632
+ };
4633
+ }
4634
+ });
4635
+ return true;
4636
+ }
4637
+ if (request.type === "set_model") {
4638
+ this.handleControlRequest({
4639
+ socket,
4640
+ requestId: request.requestId,
4641
+ run: async () => {
4642
+ const response = await this.controlHandlers.setSessionModel(request.modelId, request.timeoutMs);
4643
+ return {
4644
+ type: "set_model_result",
4645
+ requestId: request.requestId,
4646
+ modelId: request.modelId,
4647
+ response
4648
+ };
4649
+ }
4650
+ });
4651
+ return true;
4652
+ }
4653
+ if (request.type === "set_config_option") {
4654
+ this.handleControlRequest({
4655
+ socket,
4656
+ requestId: request.requestId,
4657
+ run: async () => ({
4658
+ type: "set_config_option_result",
4659
+ requestId: request.requestId,
4660
+ response: await this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs)
4661
+ })
4662
+ });
4663
+ return true;
4664
+ }
4665
+ return false;
4666
+ }
4667
+ enqueuePromptRequest(socket, request) {
4668
+ this.taskSockets.add(socket);
4669
+ const task = {
4670
+ requestId: request.requestId,
4671
+ message: request.message,
4672
+ prompt: request.prompt ?? textPrompt(request.message),
4673
+ permissionMode: request.permissionMode,
4674
+ resumePolicy: request.resumePolicy,
4675
+ nonInteractivePermissions: request.nonInteractivePermissions,
4676
+ permissionPolicy: request.permissionPolicy,
4677
+ timeoutMs: request.timeoutMs,
4678
+ suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
4679
+ promptRetries: request.promptRetries,
4680
+ sessionOptions: request.sessionOptions,
4681
+ waitForCompletion: request.waitForCompletion,
4682
+ reportPromptStarted: request.reportPromptStarted,
4683
+ enqueuedAt: Date.now(),
4684
+ send: (message) => {
4685
+ this.writeQueueMessage(socket, {
4686
+ ...message,
4687
+ ownerGeneration: this.ownerGeneration
4688
+ });
4689
+ },
4690
+ close: () => {
4691
+ this.endSocket(socket);
4692
+ }
4693
+ };
4694
+ if (!this.enqueue(task)) return;
4695
+ this.writeQueueMessage(socket, {
4696
+ type: "accepted",
4697
+ requestId: request.requestId,
4698
+ ownerGeneration: this.ownerGeneration
4699
+ });
4700
+ if (!request.waitForCompletion) task.close();
4701
+ }
4702
+ handleConnection(socket) {
4703
+ this.sockets.add(socket);
4704
+ this.outputs.set(socket, new QueueSocketOutput(socket, this.outputBudget));
4705
+ socket.setEncoding("utf8");
4706
+ socket.on("timeout", () => socket.destroy());
4707
+ socket.once("close", () => {
4708
+ this.outputs.delete(socket);
4709
+ this.sockets.delete(socket);
4710
+ this.taskSockets.delete(socket);
4711
+ this.drainingSockets.delete(socket);
4712
+ });
4713
+ let buffer = "";
4714
+ let handled = false;
4715
+ const processLine = (line) => {
4716
+ if (handled) return;
4717
+ handled = true;
4718
+ const request = this.parseRequestLine(socket, line);
4719
+ if (!request || this.rejectClosedOwner(socket, request) || this.rejectStaleOwnerGeneration(socket, request)) return;
4720
+ if (this.handleControlQueueRequest(socket, request)) return;
4721
+ if (request.type !== "submit_prompt") {
4722
+ this.failRequest(socket, request.requestId, "Invalid queue request", "QUEUE_REQUEST_INVALID");
4723
+ return;
4724
+ }
4725
+ this.enqueuePromptRequest(socket, request);
4726
+ };
4727
+ socket.on("data", (chunk) => {
4728
+ buffer += chunk;
4729
+ let index = buffer.indexOf("\n");
4730
+ while (index >= 0) {
4731
+ const rawLine = buffer.slice(0, index);
4732
+ if (this.rejectOversizedRequest(socket, rawLine)) return;
4733
+ const line = rawLine.trim();
4734
+ buffer = buffer.slice(index + 1);
4735
+ if (line.length > 0) processLine(line);
4736
+ index = buffer.indexOf("\n");
4737
+ }
4738
+ this.rejectOversizedRequest(socket, buffer);
4739
+ });
4740
+ socket.on("error", () => {});
4741
+ }
4742
+ rejectOversizedRequest(socket, line) {
4743
+ if (!queueRequestExceedsLimit(line, this.maxRequestBytes)) return false;
4744
+ socket.destroy();
4745
+ return true;
4746
+ }
4747
+ };
4748
+ //#endregion
3873
4749
  //#region src/session/queue/ipc.ts
3874
- const MAX_MESSAGE_BUFFER_SIZE = 10485760;
3875
4750
  function assertOwnerGeneration(owner, message) {
3876
4751
  if (owner.ownerGeneration !== void 0 && message.ownerGeneration !== void 0 && message.ownerGeneration !== owner.ownerGeneration) throw new QueueProtocolError("Queue owner returned mismatched generation", {
3877
4752
  detailCode: "QUEUE_OWNER_GENERATION_MISMATCH",
@@ -3957,7 +4832,7 @@ async function runQueueOwnerRequest(options) {
3957
4832
  socket.setEncoding("utf8");
3958
4833
  return await new Promise((resolve, reject) => {
3959
4834
  let settled = false;
3960
- let buffer = "";
4835
+ const responseFragments = [];
3961
4836
  const state = { acknowledged: false };
3962
4837
  const finishResolve = (result) => {
3963
4838
  if (settled) return;
@@ -4001,19 +4876,21 @@ async function runQueueOwnerRequest(options) {
4001
4876
  }
4002
4877
  };
4003
4878
  socket.on("data", (chunk) => {
4004
- buffer += chunk;
4005
- if (buffer.length > 10485760) {
4006
- socket.destroy();
4007
- finishReject(uncertainQueueOutcome(/* @__PURE__ */ new Error(`Message buffer exceeded ${MAX_MESSAGE_BUFFER_SIZE} bytes`)));
4008
- return;
4009
- }
4010
- let index = buffer.indexOf("\n");
4011
- while (index >= 0) {
4012
- if (settled) return;
4013
- const line = buffer.slice(0, index).trim();
4014
- buffer = buffer.slice(index + 1);
4015
- if (line.length > 0) processLine(line);
4016
- index = buffer.indexOf("\n");
4879
+ try {
4880
+ let offset = 0;
4881
+ let newline = chunk.indexOf("\n");
4882
+ while (newline >= 0) {
4883
+ if (settled) return;
4884
+ responseFragments.push(chunk.slice(offset, newline));
4885
+ const line = responseFragments.join("").trim();
4886
+ responseFragments.length = 0;
4887
+ if (line.length > 0) processLine(line);
4888
+ offset = newline + 1;
4889
+ newline = chunk.indexOf("\n", offset);
4890
+ }
4891
+ if (!settled && offset < chunk.length) responseFragments.push(chunk.slice(offset));
4892
+ } catch (error) {
4893
+ finishReject(uncertainQueueOutcome(error));
4017
4894
  }
4018
4895
  });
4019
4896
  socket.once("error", (error) => {
@@ -4340,6 +5217,6 @@ async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, time
4340
5217
  });
4341
5218
  }
4342
5219
  //#endregion
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 };
5220
+ export { resetPerfMetrics as $, findSession as A, PERMISSION_MODES as At, assertPersistedKeyPolicy as B, GeminiAcpStartupTimeoutError as Bt, textPrompt as C, normalizeOutputError as Ct, DEFAULT_HISTORY_LIMIT as D, EXIT_CODES as Dt, observeProcessIncarnation as E, AUTH_POLICIES as Et, normalizeName as F, AgentSpawnError as Ft, SESSION_JOURNAL_SCHEMA as G, SessionModeReplayError as Gt, serializeSessionRecordForDisk as H, PermissionPromptUnavailableError as Ht, pruneSessions as I, AgentStartupError as It, watchSession as J, SessionResumeRequiredError as Jt, SessionJournalReader as K, SessionModelReplayError as Kt, readSessionRecord as L, AuthPolicyError as Lt, isoNow as M, SESSION_RECORD_SCHEMA as Mt, listSessions as N, AcpxOperationalError as Nt, absolutePath as O, NON_INTERACTIVE_PERMISSION_POLICIES as Ot, listSessionsForAgent as P, AgentDisconnectedError as Pt, measurePerf as Q, resolveSessionRecord as R, ClaudeAcpSessionCreateTimeoutError as Rt, promptToDisplayText as S, isRetryablePromptError as St, lockOwnerPid as T, isAcpResourceNotFoundError as Tt, extractAgentSessionId as U, QueueConnectionError as Ut, parseSessionRecord as V, PermissionDeniedError as Vt, normalizeAgentSessionId as W, SessionConfigOptionReplayError as Wt, getPerfMetricsSnapshot as X, formatPerfMetric as Y, UnsupportedPromptContentError as Yt, incrementPerfCounter as Z, ProcessDescendants as _, _usingCtx as _t, trySetModelOnRunningOwner as a, isSessionUpdateNotification as at, mergePromptSourceWithText as b, formatErrorMessage 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, PERMISSION_POLICY_ACTIONS as jt, findGitRepositoryRoot as k, OUTPUT_FORMATS 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, SessionNotFoundError 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, PromptInputValidationError as v, isProcessAlive as vt, createLockOwner as w, extractAcpError as wt, parsePromptSource as x, isAcpQueryClosedBeforeResponseError as xt, getUnsupportedPromptContentMessage as y, exitCodeForOutputErrorCode as yt, writeSessionRecord as z, CopilotAcpUnsupportedError as zt };
4344
5221
 
4345
- //# sourceMappingURL=ipc-DAApJ5mi.js.map
5222
+ //# sourceMappingURL=ipc-BW06Pbb8.js.map