perchai-cli 2.4.11 → 2.4.12

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 (2) hide show
  1. package/dist/perch.mjs +1735 -386
  2. package/package.json +1 -1
package/dist/perch.mjs CHANGED
@@ -5198,8 +5198,8 @@ var require_xlsx = __commonJS({
5198
5198
  }
5199
5199
  return L.length - R.length;
5200
5200
  }
5201
- function dirname(p) {
5202
- if (p.charAt(p.length - 1) == "/") return p.slice(0, -1).indexOf("/") === -1 ? p : dirname(p.slice(0, -1));
5201
+ function dirname2(p) {
5202
+ if (p.charAt(p.length - 1) == "/") return p.slice(0, -1).indexOf("/") === -1 ? p : dirname2(p.slice(0, -1));
5203
5203
  var c = p.lastIndexOf("/");
5204
5204
  return c === -1 ? p : p.slice(0, c + 1);
5205
5205
  }
@@ -5620,7 +5620,7 @@ var require_xlsx = __commonJS({
5620
5620
  data.push([cfb.FullPaths[i2], cfb.FileIndex[i2]]);
5621
5621
  }
5622
5622
  for (i2 = 0; i2 < data.length; ++i2) {
5623
- var dad = dirname(data[i2][0]);
5623
+ var dad = dirname2(data[i2][0]);
5624
5624
  s = fullPaths[dad];
5625
5625
  if (!s) {
5626
5626
  data.push([dad, {
@@ -5656,13 +5656,13 @@ var require_xlsx = __commonJS({
5656
5656
  elt.size = 0;
5657
5657
  elt.type = 5;
5658
5658
  } else if (nm.slice(-1) == "/") {
5659
- for (j = i2 + 1; j < data.length; ++j) if (dirname(cfb.FullPaths[j]) == nm) break;
5659
+ for (j = i2 + 1; j < data.length; ++j) if (dirname2(cfb.FullPaths[j]) == nm) break;
5660
5660
  elt.C = j >= data.length ? -1 : j;
5661
- for (j = i2 + 1; j < data.length; ++j) if (dirname(cfb.FullPaths[j]) == dirname(nm)) break;
5661
+ for (j = i2 + 1; j < data.length; ++j) if (dirname2(cfb.FullPaths[j]) == dirname2(nm)) break;
5662
5662
  elt.R = j >= data.length ? -1 : j;
5663
5663
  elt.type = 1;
5664
5664
  } else {
5665
- if (dirname(cfb.FullPaths[i2 + 1] || "") == dirname(nm)) elt.R = i2 + 1;
5665
+ if (dirname2(cfb.FullPaths[i2 + 1] || "") == dirname2(nm)) elt.R = i2 + 1;
5666
5666
  elt.type = 2;
5667
5667
  }
5668
5668
  }
@@ -41950,11 +41950,11 @@ var require_html2canvas = __commonJS({
41950
41950
  };
41951
41951
  function __awaiter3(thisArg, _arguments, P, generator) {
41952
41952
  function adopt(value) {
41953
- return value instanceof P ? value : new P(function(resolve4) {
41954
- resolve4(value);
41953
+ return value instanceof P ? value : new P(function(resolve5) {
41954
+ resolve5(value);
41955
41955
  });
41956
41956
  }
41957
- return new (P || (P = Promise))(function(resolve4, reject2) {
41957
+ return new (P || (P = Promise))(function(resolve5, reject2) {
41958
41958
  function fulfilled(value) {
41959
41959
  try {
41960
41960
  step(generator.next(value));
@@ -41970,7 +41970,7 @@ var require_html2canvas = __commonJS({
41970
41970
  }
41971
41971
  }
41972
41972
  function step(result2) {
41973
- result2.done ? resolve4(result2.value) : adopt(result2.value).then(fulfilled, rejected);
41973
+ result2.done ? resolve5(result2.value) : adopt(result2.value).then(fulfilled, rejected);
41974
41974
  }
41975
41975
  step((generator = generator.apply(thisArg, _arguments || [])).next());
41976
41976
  });
@@ -43962,16 +43962,16 @@ var require_html2canvas = __commonJS({
43962
43962
  [width, 0],
43963
43963
  [width, height]
43964
43964
  ];
43965
- return corners.reduce(function(stat, corner) {
43965
+ return corners.reduce(function(stat2, corner) {
43966
43966
  var cx = corner[0], cy = corner[1];
43967
43967
  var d = distance(x - cx, y - cy);
43968
- if (closest ? d < stat.optimumDistance : d > stat.optimumDistance) {
43968
+ if (closest ? d < stat2.optimumDistance : d > stat2.optimumDistance) {
43969
43969
  return {
43970
43970
  optimumCorner: corner,
43971
43971
  optimumDistance: d
43972
43972
  };
43973
43973
  }
43974
- return stat;
43974
+ return stat2;
43975
43975
  }, {
43976
43976
  optimumDistance: closest ? Infinity : -Infinity,
43977
43977
  optimumCorner: null
@@ -46099,10 +46099,10 @@ var require_html2canvas = __commonJS({
46099
46099
  return svg;
46100
46100
  };
46101
46101
  var loadSerializedSVG$1 = function(svg) {
46102
- return new Promise(function(resolve4, reject2) {
46102
+ return new Promise(function(resolve5, reject2) {
46103
46103
  var img = new Image();
46104
46104
  img.onload = function() {
46105
- return resolve4(img);
46105
+ return resolve5(img);
46106
46106
  };
46107
46107
  img.onerror = reject2;
46108
46108
  img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(new XMLSerializer().serializeToString(svg));
@@ -47464,24 +47464,24 @@ var require_html2canvas = __commonJS({
47464
47464
  return cloneIframeContainer;
47465
47465
  };
47466
47466
  var imageReady = function(img) {
47467
- return new Promise(function(resolve4) {
47467
+ return new Promise(function(resolve5) {
47468
47468
  if (img.complete) {
47469
- resolve4();
47469
+ resolve5();
47470
47470
  return;
47471
47471
  }
47472
47472
  if (!img.src) {
47473
- resolve4();
47473
+ resolve5();
47474
47474
  return;
47475
47475
  }
47476
- img.onload = resolve4;
47477
- img.onerror = resolve4;
47476
+ img.onload = resolve5;
47477
+ img.onerror = resolve5;
47478
47478
  });
47479
47479
  };
47480
47480
  var imagesReady = function(document2) {
47481
47481
  return Promise.all([].slice.call(document2.images, 0).map(imageReady));
47482
47482
  };
47483
47483
  var iframeLoader = function(iframe) {
47484
- return new Promise(function(resolve4, reject2) {
47484
+ return new Promise(function(resolve5, reject2) {
47485
47485
  var cloneWindow = iframe.contentWindow;
47486
47486
  if (!cloneWindow) {
47487
47487
  return reject2("No window assigned for iframe");
@@ -47492,7 +47492,7 @@ var require_html2canvas = __commonJS({
47492
47492
  var interval = setInterval(function() {
47493
47493
  if (documentClone.body.childNodes.length > 0 && documentClone.readyState === "complete") {
47494
47494
  clearInterval(interval);
47495
- resolve4(iframe);
47495
+ resolve5(iframe);
47496
47496
  }
47497
47497
  }, 50);
47498
47498
  };
@@ -47631,10 +47631,10 @@ var require_html2canvas = __commonJS({
47631
47631
  _a.label = 2;
47632
47632
  case 2:
47633
47633
  this.context.logger.debug("Added image " + key.substring(0, 256));
47634
- return [4, new Promise(function(resolve4, reject2) {
47634
+ return [4, new Promise(function(resolve5, reject2) {
47635
47635
  var img = new Image();
47636
47636
  img.onload = function() {
47637
- return resolve4(img);
47637
+ return resolve5(img);
47638
47638
  };
47639
47639
  img.onerror = reject2;
47640
47640
  if (isInlineBase64Image(src) || useCORS) {
@@ -47643,7 +47643,7 @@ var require_html2canvas = __commonJS({
47643
47643
  img.src = src;
47644
47644
  if (img.complete === true) {
47645
47645
  setTimeout(function() {
47646
- return resolve4(img);
47646
+ return resolve5(img);
47647
47647
  }, 500);
47648
47648
  }
47649
47649
  if (_this._options.imageTimeout > 0) {
@@ -47671,17 +47671,17 @@ var require_html2canvas = __commonJS({
47671
47671
  throw new Error("No proxy defined");
47672
47672
  }
47673
47673
  var key = src.substring(0, 256);
47674
- return new Promise(function(resolve4, reject2) {
47674
+ return new Promise(function(resolve5, reject2) {
47675
47675
  var responseType = FEATURES.SUPPORT_RESPONSE_TYPE ? "blob" : "text";
47676
47676
  var xhr = new XMLHttpRequest();
47677
47677
  xhr.onload = function() {
47678
47678
  if (xhr.status === 200) {
47679
47679
  if (responseType === "text") {
47680
- resolve4(xhr.response);
47680
+ resolve5(xhr.response);
47681
47681
  } else {
47682
47682
  var reader_1 = new FileReader();
47683
47683
  reader_1.addEventListener("load", function() {
47684
- return resolve4(reader_1.result);
47684
+ return resolve5(reader_1.result);
47685
47685
  }, false);
47686
47686
  reader_1.addEventListener("error", function(e2) {
47687
47687
  return reject2(e2);
@@ -49496,10 +49496,10 @@ var require_html2canvas = __commonJS({
49496
49496
  })(Renderer)
49497
49497
  );
49498
49498
  var loadSerializedSVG = function(svg) {
49499
- return new Promise(function(resolve4, reject2) {
49499
+ return new Promise(function(resolve5, reject2) {
49500
49500
  var img = new Image();
49501
49501
  img.onload = function() {
49502
- resolve4(img);
49502
+ resolve5(img);
49503
49503
  };
49504
49504
  img.onerror = reject2;
49505
49505
  img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(new XMLSerializer().serializeToString(svg));
@@ -51584,7 +51584,7 @@ var require_make_built_in = __commonJS({
51584
51584
  var defineProperty = Object.defineProperty;
51585
51585
  var stringSlice = uncurryThis("".slice);
51586
51586
  var replace = uncurryThis("".replace);
51587
- var join = uncurryThis([].join);
51587
+ var join2 = uncurryThis([].join);
51588
51588
  var CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function() {
51589
51589
  return defineProperty(function() {
51590
51590
  }, "length", { value: 8 }).length !== 8;
@@ -51611,7 +51611,7 @@ var require_make_built_in = __commonJS({
51611
51611
  }
51612
51612
  var state = enforceInternalState(value);
51613
51613
  if (!hasOwn(state, "source")) {
51614
- state.source = join(TEMPLATE, typeof name == "string" ? name : "");
51614
+ state.source = join2(TEMPLATE, typeof name == "string" ? name : "");
51615
51615
  }
51616
51616
  return value;
51617
51617
  };
@@ -52685,8 +52685,8 @@ var require_promise_constructor_detection = __commonJS({
52685
52685
  if (!GLOBAL_CORE_JS_PROMISE && V8_VERSION === 66) return true;
52686
52686
  if (IS_PURE && !(NativePromisePrototype["catch"] && NativePromisePrototype["finally"])) return true;
52687
52687
  if (!V8_VERSION || V8_VERSION < 51 || !/native code/.test(PROMISE_CONSTRUCTOR_SOURCE)) {
52688
- var promise = new NativePromiseConstructor(function(resolve4) {
52689
- resolve4(1);
52688
+ var promise = new NativePromiseConstructor(function(resolve5) {
52689
+ resolve5(1);
52690
52690
  });
52691
52691
  var FakePromise = function(exec3) {
52692
52692
  exec3(function() {
@@ -52716,13 +52716,13 @@ var require_new_promise_capability = __commonJS({
52716
52716
  var aCallable = require_a_callable();
52717
52717
  var $TypeError = TypeError;
52718
52718
  var PromiseCapability = function(C) {
52719
- var resolve4, reject2;
52719
+ var resolve5, reject2;
52720
52720
  this.promise = new C(function($$resolve, $$reject) {
52721
- if (resolve4 !== void 0 || reject2 !== void 0) throw new $TypeError("Bad Promise constructor");
52722
- resolve4 = $$resolve;
52721
+ if (resolve5 !== void 0 || reject2 !== void 0) throw new $TypeError("Bad Promise constructor");
52722
+ resolve5 = $$resolve;
52723
52723
  reject2 = $$reject;
52724
52724
  });
52725
- this.resolve = aCallable(resolve4);
52725
+ this.resolve = aCallable(resolve5);
52726
52726
  this.reject = aCallable(reject2);
52727
52727
  };
52728
52728
  module2.exports.f = function(C) {
@@ -52793,7 +52793,7 @@ var require_es_promise_constructor = __commonJS({
52793
52793
  var value = state.value;
52794
52794
  var ok = state.state === FULFILLED;
52795
52795
  var handler = ok ? reaction.ok : reaction.fail;
52796
- var resolve4 = reaction.resolve;
52796
+ var resolve5 = reaction.resolve;
52797
52797
  var reject2 = reaction.reject;
52798
52798
  var domain = reaction.domain;
52799
52799
  var result2, then, exited;
@@ -52815,8 +52815,8 @@ var require_es_promise_constructor = __commonJS({
52815
52815
  if (result2 === reaction.promise) {
52816
52816
  reject2(new TypeError2("Promise-chain cycle"));
52817
52817
  } else if (then = isThenable(result2)) {
52818
- call(then, result2, resolve4, reject2);
52819
- } else resolve4(result2);
52818
+ call(then, result2, resolve5, reject2);
52819
+ } else resolve5(result2);
52820
52820
  } else reject2(value);
52821
52821
  } catch (error) {
52822
52822
  if (domain && !exited) domain.exit();
@@ -52972,8 +52972,8 @@ var require_es_promise_constructor = __commonJS({
52972
52972
  if (!NATIVE_PROMISE_SUBCLASSING) {
52973
52973
  defineBuiltIn(NativePromisePrototype, "then", function then(onFulfilled, onRejected) {
52974
52974
  var that = this;
52975
- return new PromiseConstructor(function(resolve4, reject2) {
52976
- call(nativeThen, that, resolve4, reject2);
52975
+ return new PromiseConstructor(function(resolve5, reject2) {
52976
+ call(nativeThen, that, resolve5, reject2);
52977
52977
  }).then(onFulfilled, onRejected);
52978
52978
  }, { unsafe: true });
52979
52979
  }
@@ -53233,7 +53233,7 @@ var require_es_promise_all = __commonJS({
53233
53233
  all: function all(iterable) {
53234
53234
  var C = this;
53235
53235
  var capability = newPromiseCapabilityModule.f(C);
53236
- var resolve4 = capability.resolve;
53236
+ var resolve5 = capability.resolve;
53237
53237
  var reject2 = capability.reject;
53238
53238
  var result2 = perform(function() {
53239
53239
  var $promiseResolve = aCallable(C.resolve);
@@ -53248,10 +53248,10 @@ var require_es_promise_all = __commonJS({
53248
53248
  if (alreadyCalled) return;
53249
53249
  alreadyCalled = true;
53250
53250
  values2[index] = value;
53251
- --remaining || resolve4(values2);
53251
+ --remaining || resolve5(values2);
53252
53252
  }, reject2);
53253
53253
  });
53254
- --remaining || resolve4(values2);
53254
+ --remaining || resolve5(values2);
53255
53255
  });
53256
53256
  if (result2.error) reject2(result2.value);
53257
53257
  return capability.promise;
@@ -53345,8 +53345,8 @@ var require_promise_resolve = __commonJS({
53345
53345
  anObject(C);
53346
53346
  if (isObject4(x) && x.constructor === C) return x;
53347
53347
  var promiseCapability = newPromiseCapability.f(C);
53348
- var resolve4 = promiseCapability.resolve;
53349
- resolve4(x);
53348
+ var resolve5 = promiseCapability.resolve;
53349
+ resolve5(x);
53350
53350
  return promiseCapability.promise;
53351
53351
  };
53352
53352
  }
@@ -53365,7 +53365,7 @@ var require_es_promise_resolve = __commonJS({
53365
53365
  var PromiseConstructorWrapper = getBuiltIn("Promise");
53366
53366
  var CHECK_WRAPPER = IS_PURE && !FORCED_PROMISE_CONSTRUCTOR;
53367
53367
  $2({ target: "Promise", stat: true, forced: IS_PURE || FORCED_PROMISE_CONSTRUCTOR }, {
53368
- resolve: function resolve4(x) {
53368
+ resolve: function resolve5(x) {
53369
53369
  return promiseResolve(CHECK_WRAPPER && this === PromiseConstructorWrapper ? NativePromiseConstructor : this, x);
53370
53370
  }
53371
53371
  });
@@ -54854,7 +54854,7 @@ var require_es_array_join = __commonJS({
54854
54854
  var ES3_STRINGS = IndexedObject !== Object;
54855
54855
  var FORCED = ES3_STRINGS || !arrayMethodIsStrict("join", ",");
54856
54856
  $2({ target: "Array", proto: true, forced: FORCED }, {
54857
- join: function join(separator) {
54857
+ join: function join2(separator) {
54858
54858
  return nativeJoin(toIndexedObject(this), separator === void 0 ? "," : separator);
54859
54859
  }
54860
54860
  });
@@ -58983,8 +58983,8 @@ var require_lib2 = __commonJS({
58983
58983
  var FRAMERATE = this.FRAMERATE, mouse = this.mouse;
58984
58984
  var frameDuration = 1e3 / FRAMERATE;
58985
58985
  this.frameDuration = frameDuration;
58986
- this.readyPromise = new Promise(function(resolve4) {
58987
- _this.resolveReady = resolve4;
58986
+ this.readyPromise = new Promise(function(resolve5) {
58987
+ _this.resolveReady = resolve5;
58988
58988
  });
58989
58989
  if (this.isReady()) {
58990
58990
  this.render(element, ignoreDimensions, ignoreClear, scaleWidth, scaleHeight, offsetX, offsetY);
@@ -65022,9 +65022,9 @@ var require_lib2 = __commonJS({
65022
65022
  if (anonymousCrossOrigin) {
65023
65023
  image2.crossOrigin = "Anonymous";
65024
65024
  }
65025
- return _context.abrupt("return", new Promise(function(resolve4, reject2) {
65025
+ return _context.abrupt("return", new Promise(function(resolve5, reject2) {
65026
65026
  image2.onload = function() {
65027
- resolve4(image2);
65027
+ resolve5(image2);
65028
65028
  };
65029
65029
  image2.onerror = function(_event, _source, _lineno, _colno, error) {
65030
65030
  reject2(error);
@@ -73320,7 +73320,7 @@ async function walk(rootPath, currentPath, files) {
73320
73320
  continue;
73321
73321
  }
73322
73322
  if (!entry.isFile()) continue;
73323
- const stat = await fs3.promises.stat(fullPath);
73323
+ const stat2 = await fs3.promises.stat(fullPath);
73324
73324
  const extension2 = path3.extname(entry.name).toLowerCase();
73325
73325
  const role = sensitivity.sensitive ? "secret_sensitive" : classifyFileRole(entry.name, relativePath);
73326
73326
  const ignored = sensitivity.sensitive;
@@ -73329,11 +73329,11 @@ async function walk(rootPath, currentPath, files) {
73329
73329
  relativePath,
73330
73330
  fileName: entry.name,
73331
73331
  extension: extension2,
73332
- sizeBytes: stat.size,
73332
+ sizeBytes: stat2.size,
73333
73333
  role,
73334
73334
  ignored,
73335
73335
  ignoreReason: sensitivity.reason,
73336
- textSnippet: ignored ? void 0 : await safeTextSnippet(fullPath, extension2, stat.size)
73336
+ textSnippet: ignored ? void 0 : await safeTextSnippet(fullPath, extension2, stat2.size)
73337
73337
  });
73338
73338
  }
73339
73339
  }
@@ -75408,8 +75408,8 @@ async function listDirs(root2) {
75408
75408
  }
75409
75409
  async function fileExists(filePath) {
75410
75410
  try {
75411
- const stat = await fs7.promises.stat(filePath);
75412
- return stat.isFile();
75411
+ const stat2 = await fs7.promises.stat(filePath);
75412
+ return stat2.isFile();
75413
75413
  } catch {
75414
75414
  return false;
75415
75415
  }
@@ -81720,7 +81720,7 @@ function buildThreadLedgerItemsFromTurn(input) {
81720
81720
  kind: "context_boundary",
81721
81721
  ts: event.ts,
81722
81722
  runId: input.runId,
81723
- summary: `Context compacted from ${event.tokensBefore} to ${event.tokensAfter} tokens.`,
81723
+ summary: `Context compacted from ${event.tokensBefore} to ${event.tokensAfter} tokens` + (event.targetTokens ? ` (target ${event.targetTokens}).` : "."),
81724
81724
  resultPreview: truncate3(event.summary, MAX_TEXT)
81725
81725
  });
81726
81726
  } else if (event.type === "live_card_end" && event.receipt) {
@@ -82300,6 +82300,8 @@ function sanitizePendingActionPayload(value) {
82300
82300
  }
82301
82301
  function parseContextCompaction(value) {
82302
82302
  if (!isRecord4(value)) return null;
82303
+ const compactBoundary = parseCompactBoundary(value.compactBoundary);
82304
+ const compactionReport = parseCompactionReport(value.compactionReport);
82303
82305
  return {
82304
82306
  compactConversation: value.compactConversation === true,
82305
82307
  dropNotFoundRows: value.dropNotFoundRows === true,
@@ -82310,9 +82312,38 @@ function parseContextCompaction(value) {
82310
82312
  autoCompactedAtIso: typeof value.autoCompactedAtIso === "string" ? value.autoCompactedAtIso : void 0,
82311
82313
  autoCompactedMessageCount: asFiniteNonNegativeInt(value.autoCompactedMessageCount) ?? void 0,
82312
82314
  compactedSummary: typeof value.compactedSummary === "string" ? value.compactedSummary : void 0,
82313
- effectiveContextLimitTokens: asFiniteNonNegativeInt(value.effectiveContextLimitTokens) ?? void 0
82315
+ effectiveContextLimitTokens: asFiniteNonNegativeInt(value.effectiveContextLimitTokens) ?? void 0,
82316
+ ...compactBoundary ? { compactBoundary } : {},
82317
+ ...compactionReport ? { compactionReport } : {}
82314
82318
  };
82315
82319
  }
82320
+ function parseCompactBoundary(value) {
82321
+ if (!isRecord4(value)) return void 0;
82322
+ const kind = value.kind === "manual" ? "manual" : value.kind === "auto" ? "auto" : null;
82323
+ const compactedAtIso = typeof value.compactedAtIso === "string" ? value.compactedAtIso : null;
82324
+ if (!kind || !compactedAtIso) return void 0;
82325
+ return {
82326
+ kind,
82327
+ compactedAtIso,
82328
+ compactedThroughMessageId: typeof value.compactedThroughMessageId === "string" ? value.compactedThroughMessageId : null,
82329
+ compactedThroughMessageCreatedAt: typeof value.compactedThroughMessageCreatedAt === "string" ? value.compactedThroughMessageCreatedAt : null,
82330
+ compactedThroughWireIndex: asFiniteNonNegativeInt(value.compactedThroughWireIndex)
82331
+ };
82332
+ }
82333
+ function parseCompactionReport(value) {
82334
+ if (!isRecord4(value)) return void 0;
82335
+ const parsed = {
82336
+ tokensBefore: asFiniteNonNegativeInt(value.tokensBefore) ?? void 0,
82337
+ tokensAfter: asFiniteNonNegativeInt(value.tokensAfter) ?? void 0,
82338
+ targetTokens: asFiniteNonNegativeInt(value.targetTokens) ?? void 0,
82339
+ summaryTokens: asFiniteNonNegativeInt(value.summaryTokens) ?? void 0,
82340
+ recentTailTokens: asFiniteNonNegativeInt(value.recentTailTokens) ?? void 0,
82341
+ recentTailMessages: asFiniteNonNegativeInt(value.recentTailMessages) ?? void 0,
82342
+ summarizedMessages: asFiniteNonNegativeInt(value.summarizedMessages) ?? void 0,
82343
+ restoredToolsOrFiles: asFiniteNonNegativeInt(value.restoredToolsOrFiles) ?? void 0
82344
+ };
82345
+ return Object.values(parsed).some((entry) => typeof entry === "number") ? parsed : void 0;
82346
+ }
82316
82347
  function parseWorkerRuns(value) {
82317
82348
  if (!Array.isArray(value)) return void 0;
82318
82349
  const parsed = [];
@@ -82425,6 +82456,7 @@ function parseContextMetrics(value) {
82425
82456
  const consecutiveAutoCompactFailures = asFiniteNonNegativeInt(value.consecutiveAutoCompactFailures);
82426
82457
  const lastApiUsage = parseModelUsage(value.lastApiUsage);
82427
82458
  const turnApiUsage = parseModelUsage(value.turnApiUsage);
82459
+ const compactionReport = parseCompactionReport(value.compactionReport);
82428
82460
  return {
82429
82461
  threadContextTokens,
82430
82462
  latestSendTokens,
@@ -82448,6 +82480,7 @@ function parseContextMetrics(value) {
82448
82480
  ...consecutiveAutoCompactFailures !== null ? { consecutiveAutoCompactFailures } : {},
82449
82481
  ...typeof value.autoCompactedAtIso === "string" ? { autoCompactedAtIso: value.autoCompactedAtIso } : {},
82450
82482
  ...asFiniteNonNegativeInt(value.autoCompactedMessageCount) !== null ? { autoCompactedMessageCount: asFiniteNonNegativeInt(value.autoCompactedMessageCount) } : {},
82483
+ ...compactionReport ? { compactionReport } : {},
82451
82484
  resetAtIso: typeof value.resetAtIso === "string" ? value.resetAtIso : null,
82452
82485
  updatedAtIso: typeof value.updatedAtIso === "string" ? value.updatedAtIso : (/* @__PURE__ */ new Date()).toISOString()
82453
82486
  };
@@ -82906,7 +82939,17 @@ function buildMessageHistory(recentMessages, currentInput, options) {
82906
82939
  const hasCompactedSummary = compactedSummary.length > 0;
82907
82940
  const shouldUseCompactedHistory = options?.compactConversation === true && hasCompactedSummary;
82908
82941
  const keepCount = options?.keepCount ?? (shouldUseCompactedHistory ? 12 : recentMessages.length);
82909
- const sourceMessages = shouldUseCompactedHistory ? recentMessages.slice(-keepCount) : recentMessages;
82942
+ const sourceMessages = shouldUseCompactedHistory ? selectRecentHistoryTail(
82943
+ filterMessagesAfterCompactBoundary(recentMessages, {
82944
+ compactedThroughMessageId: options?.compactedThroughMessageId,
82945
+ compactedThroughMessageCreatedAt: options?.compactedThroughMessageCreatedAt
82946
+ }),
82947
+ {
82948
+ tokenTarget: options?.recentTailTokenTarget,
82949
+ fallbackKeepCount: keepCount,
82950
+ minMessages: Math.min(DEFAULT_COMPACTED_MIN_RECENT_MESSAGES, keepCount)
82951
+ }
82952
+ ) : recentMessages;
82910
82953
  if (hasCompactedSummary) {
82911
82954
  history.push({
82912
82955
  role: "user",
@@ -82955,6 +82998,47 @@ ${compactedSummary}`
82955
82998
  history.push({ role: "user", content: currentInput });
82956
82999
  return history;
82957
83000
  }
83001
+ function filterMessagesAfterCompactBoundary(messages, boundary) {
83002
+ const boundaryId = boundary.compactedThroughMessageId?.trim();
83003
+ if (boundaryId) {
83004
+ const idx = messages.findIndex((message) => message.id === boundaryId);
83005
+ if (idx >= 0) return messages.slice(idx + 1);
83006
+ }
83007
+ const boundaryAt = boundary.compactedThroughMessageCreatedAt?.trim();
83008
+ if (boundaryAt) {
83009
+ const boundaryMs = Date.parse(boundaryAt);
83010
+ if (Number.isFinite(boundaryMs)) {
83011
+ return messages.filter((message) => {
83012
+ const createdMs = Date.parse(message.createdAt);
83013
+ return !Number.isFinite(createdMs) || createdMs > boundaryMs;
83014
+ });
83015
+ }
83016
+ }
83017
+ return messages;
83018
+ }
83019
+ function selectRecentHistoryTail(messages, input) {
83020
+ const tokenTarget = typeof input.tokenTarget === "number" && Number.isFinite(input.tokenTarget) && input.tokenTarget > 0 ? Math.floor(input.tokenTarget) : null;
83021
+ if (!tokenTarget) {
83022
+ return messages.slice(-Math.max(1, input.fallbackKeepCount));
83023
+ }
83024
+ const minMessages = Math.max(1, input.minMessages ?? DEFAULT_COMPACTED_MIN_RECENT_MESSAGES);
83025
+ let startIndex = messages.length;
83026
+ let tokens = 0;
83027
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
83028
+ const msgTokens = estimateHistoryMessageTokens(messages[i]);
83029
+ const selectedCount = messages.length - startIndex;
83030
+ const mustKeep = selectedCount === 0;
83031
+ if (!mustKeep && tokens + msgTokens > tokenTarget) break;
83032
+ tokens += msgTokens;
83033
+ startIndex = i;
83034
+ if (selectedCount + 1 >= minMessages && tokens >= tokenTarget) break;
83035
+ }
83036
+ return messages.slice(startIndex);
83037
+ }
83038
+ function estimateHistoryMessageTokens(message) {
83039
+ const text = message.kind === "text" ? messageContentForModelHistory(message) : `[Plan: ${message.plan.title}] ${message.plan.goal}`;
83040
+ return Math.max(1, Math.ceil(`${message.kind}:${text}`.length / 4));
83041
+ }
82958
83042
  function messageContentForModelHistory(msg) {
82959
83043
  if (msg.role !== "assistant" || !msg.operatorState?.events?.length) {
82960
83044
  return msg.text;
@@ -82966,11 +83050,14 @@ function messageContentForModelHistory(msg) {
82966
83050
  turnSummary: msg.turnSummary
82967
83051
  });
82968
83052
  }
83053
+ var DEFAULT_COMPACTED_RECENT_TAIL_TOKENS, DEFAULT_COMPACTED_MIN_RECENT_MESSAGES;
82969
83054
  var init_messageHistory = __esm({
82970
83055
  "features/perchTerminal/runtime/messageContext/messageHistory.ts"() {
82971
83056
  "use strict";
82972
83057
  init_operatorTruth();
82973
83058
  init_wireTranscript();
83059
+ DEFAULT_COMPACTED_RECENT_TAIL_TOKENS = 24e3;
83060
+ DEFAULT_COMPACTED_MIN_RECENT_MESSAGES = 4;
82974
83061
  }
82975
83062
  });
82976
83063
 
@@ -91612,6 +91699,9 @@ function assembleContext(input) {
91612
91699
  {
91613
91700
  compactConversation: contextCompaction?.compactConversation === true,
91614
91701
  compactedSummary: contextCompaction?.compactedSummary ?? null,
91702
+ compactedThroughMessageId: contextCompaction?.compactBoundary?.compactedThroughMessageId ?? null,
91703
+ compactedThroughMessageCreatedAt: contextCompaction?.compactBoundary?.compactedThroughMessageCreatedAt ?? null,
91704
+ recentTailTokenTarget: contextCompaction?.compactionReport?.recentTailTokens ?? DEFAULT_COMPACTED_RECENT_TAIL_TOKENS,
91615
91705
  wireReplayMessages: threadSession?.threadWireMessages ?? null,
91616
91706
  wireReplayContextLimitTokens: input.contextLimitTokens ?? threadSession?.contextMetrics?.effectiveLimitTokens ?? null,
91617
91707
  // Coordinator sessions reference planIds and worker completions from earlier
@@ -93571,11 +93661,11 @@ function __metadata(metadataKey, metadataValue) {
93571
93661
  }
93572
93662
  function __awaiter(thisArg, _arguments, P, generator) {
93573
93663
  function adopt(value) {
93574
- return value instanceof P ? value : new P(function(resolve4) {
93575
- resolve4(value);
93664
+ return value instanceof P ? value : new P(function(resolve5) {
93665
+ resolve5(value);
93576
93666
  });
93577
93667
  }
93578
- return new (P || (P = Promise))(function(resolve4, reject2) {
93668
+ return new (P || (P = Promise))(function(resolve5, reject2) {
93579
93669
  function fulfilled(value) {
93580
93670
  try {
93581
93671
  step(generator.next(value));
@@ -93591,7 +93681,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
93591
93681
  }
93592
93682
  }
93593
93683
  function step(result2) {
93594
- result2.done ? resolve4(result2.value) : adopt(result2.value).then(fulfilled, rejected);
93684
+ result2.done ? resolve5(result2.value) : adopt(result2.value).then(fulfilled, rejected);
93595
93685
  }
93596
93686
  step((generator = generator.apply(thisArg, _arguments || [])).next());
93597
93687
  });
@@ -93782,14 +93872,14 @@ function __asyncValues(o) {
93782
93872
  }, i);
93783
93873
  function verb(n) {
93784
93874
  i[n] = o[n] && function(v) {
93785
- return new Promise(function(resolve4, reject2) {
93786
- v = o[n](v), settle(resolve4, reject2, v.done, v.value);
93875
+ return new Promise(function(resolve5, reject2) {
93876
+ v = o[n](v), settle(resolve5, reject2, v.done, v.value);
93787
93877
  });
93788
93878
  };
93789
93879
  }
93790
- function settle(resolve4, reject2, d, v) {
93880
+ function settle(resolve5, reject2, d, v) {
93791
93881
  Promise.resolve(v).then(function(v2) {
93792
- resolve4({ value: v2, done: d });
93882
+ resolve5({ value: v2, done: d });
93793
93883
  }, reject2);
93794
93884
  }
93795
93885
  }
@@ -94404,18 +94494,18 @@ var require_dist = __commonJS({
94404
94494
  }
94405
94495
  };
94406
94496
  function sleep2(ms, signal) {
94407
- return new Promise((resolve4) => {
94497
+ return new Promise((resolve5) => {
94408
94498
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
94409
- resolve4();
94499
+ resolve5();
94410
94500
  return;
94411
94501
  }
94412
94502
  const id = setTimeout(() => {
94413
94503
  signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
94414
- resolve4();
94504
+ resolve5();
94415
94505
  }, ms);
94416
94506
  function onAbort() {
94417
94507
  clearTimeout(id);
94418
- resolve4();
94508
+ resolve5();
94419
94509
  }
94420
94510
  signal === null || signal === void 0 || signal.addEventListener("abort", onAbort);
94421
94511
  });
@@ -102425,15 +102515,15 @@ var require_RealtimeChannel = __commonJS({
102425
102515
  }
102426
102516
  }
102427
102517
  } else {
102428
- return new Promise((resolve4) => {
102518
+ return new Promise((resolve5) => {
102429
102519
  var _a2, _b2, _c;
102430
102520
  const push2 = this.channelAdapter.push(args.type, args, opts.timeout || this.timeout);
102431
102521
  if (args.type === "broadcast" && !((_c = (_b2 = (_a2 = this.params) === null || _a2 === void 0 ? void 0 : _a2.config) === null || _b2 === void 0 ? void 0 : _b2.broadcast) === null || _c === void 0 ? void 0 : _c.ack)) {
102432
- resolve4("ok");
102522
+ resolve5("ok");
102433
102523
  }
102434
- push2.receive("ok", () => resolve4("ok"));
102435
- push2.receive("error", () => resolve4("error"));
102436
- push2.receive("timeout", () => resolve4("timed out"));
102524
+ push2.receive("ok", () => resolve5("ok"));
102525
+ push2.receive("error", () => resolve5("error"));
102526
+ push2.receive("timeout", () => resolve5("timed out"));
102437
102527
  });
102438
102528
  }
102439
102529
  }
@@ -102458,8 +102548,8 @@ var require_RealtimeChannel = __commonJS({
102458
102548
  * @category Realtime
102459
102549
  */
102460
102550
  async unsubscribe(timeout = this.timeout) {
102461
- return new Promise((resolve4) => {
102462
- this.channelAdapter.unsubscribe(timeout).receive("ok", () => resolve4("ok")).receive("timeout", () => resolve4("timed out")).receive("error", () => resolve4("error"));
102551
+ return new Promise((resolve5) => {
102552
+ this.channelAdapter.unsubscribe(timeout).receive("ok", () => resolve5("ok")).receive("timeout", () => resolve5("timed out")).receive("error", () => resolve5("error"));
102463
102553
  });
102464
102554
  }
102465
102555
  /**
@@ -102540,8 +102630,8 @@ var require_RealtimeChannel = __commonJS({
102540
102630
  }
102541
102631
  /** @internal */
102542
102632
  _notThisChannelEvent(event, ref) {
102543
- const { close, error, leave, join } = constants_1.CHANNEL_EVENTS;
102544
- const events = [close, error, leave, join];
102633
+ const { close, error, leave, join: join2 } = constants_1.CHANNEL_EVENTS;
102634
+ const events = [close, error, leave, join2];
102545
102635
  return ref && events.includes(event) && ref !== this.joinPush.ref;
102546
102636
  }
102547
102637
  /** @internal */
@@ -102663,11 +102753,11 @@ var require_socketAdapter = __commonJS({
102663
102753
  this.socket.connect();
102664
102754
  }
102665
102755
  disconnect(callback, code, reason, timeout = 1e4) {
102666
- return new Promise((resolve4) => {
102667
- setTimeout(() => resolve4("timeout"), timeout);
102756
+ return new Promise((resolve5) => {
102757
+ setTimeout(() => resolve5("timeout"), timeout);
102668
102758
  this.socket.disconnect(() => {
102669
102759
  callback();
102670
- resolve4("ok");
102760
+ resolve5("ok");
102671
102761
  }, code, reason);
102672
102762
  });
102673
102763
  }
@@ -104213,7 +104303,7 @@ var require_dist3 = __commonJS({
104213
104303
  return _objectSpread24(_objectSpread24({}, params), parameters);
104214
104304
  };
104215
104305
  async function _handleRequest2(fetcher, method, url, options, parameters, body, namespace) {
104216
- return new Promise((resolve4, reject2) => {
104306
+ return new Promise((resolve5, reject2) => {
104217
104307
  fetcher(url, _getRequestParams2(method, options, parameters, body)).then((result2) => {
104218
104308
  if (!result2.ok) throw result2;
104219
104309
  if (options === null || options === void 0 ? void 0 : options.noResolveJson) return result2;
@@ -104223,7 +104313,7 @@ var require_dist3 = __commonJS({
104223
104313
  if (!contentType || !contentType.includes("application/json")) return {};
104224
104314
  }
104225
104315
  return result2.json();
104226
- }).then((data) => resolve4(data)).catch((error) => handleError2(error, reject2, options, namespace));
104316
+ }).then((data) => resolve5(data)).catch((error) => handleError2(error, reject2, options, namespace));
104227
104317
  });
104228
104318
  }
104229
104319
  function createFetchApi2(namespace = "storage") {
@@ -114757,11 +114847,11 @@ var require_dist4 = __commonJS({
114757
114847
  };
114758
114848
  function __awaiter3(thisArg, _arguments, P, generator) {
114759
114849
  function adopt(value) {
114760
- return value instanceof P ? value : new P(function(resolve4) {
114761
- resolve4(value);
114850
+ return value instanceof P ? value : new P(function(resolve5) {
114851
+ resolve5(value);
114762
114852
  });
114763
114853
  }
114764
- return new (P || (P = Promise))(function(resolve4, reject2) {
114854
+ return new (P || (P = Promise))(function(resolve5, reject2) {
114765
114855
  function fulfilled(value) {
114766
114856
  try {
114767
114857
  step(generator.next(value));
@@ -114777,7 +114867,7 @@ var require_dist4 = __commonJS({
114777
114867
  }
114778
114868
  }
114779
114869
  function step(result2) {
114780
- result2.done ? resolve4(result2.value) : adopt(result2.value).then(fulfilled, rejected);
114870
+ result2.done ? resolve5(result2.value) : adopt(result2.value).then(fulfilled, rejected);
114781
114871
  }
114782
114872
  step((generator = generator.apply(thisArg, _arguments || [])).next());
114783
114873
  });
@@ -118758,18 +118848,18 @@ var init_streamNormalizer = __esm({
118758
118848
 
118759
118849
  // node_modules/@supabase/postgrest-js/dist/index.mjs
118760
118850
  function sleep(ms, signal) {
118761
- return new Promise((resolve4) => {
118851
+ return new Promise((resolve5) => {
118762
118852
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
118763
- resolve4();
118853
+ resolve5();
118764
118854
  return;
118765
118855
  }
118766
118856
  const id = setTimeout(() => {
118767
118857
  signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
118768
- resolve4();
118858
+ resolve5();
118769
118859
  }, ms);
118770
118860
  function onAbort() {
118771
118861
  clearTimeout(id);
118772
- resolve4();
118862
+ resolve5();
118773
118863
  }
118774
118864
  signal === null || signal === void 0 || signal.addEventListener("abort", onAbort);
118775
118865
  });
@@ -124304,7 +124394,7 @@ function normalizeHeaders(headers) {
124304
124394
  return result2;
124305
124395
  }
124306
124396
  async function _handleRequest(fetcher, method, url, options, parameters, body, namespace) {
124307
- return new Promise((resolve4, reject2) => {
124397
+ return new Promise((resolve5, reject2) => {
124308
124398
  fetcher(url, _getRequestParams(method, options, parameters, body)).then((result2) => {
124309
124399
  if (!result2.ok) throw result2;
124310
124400
  if (options === null || options === void 0 ? void 0 : options.noResolveJson) return result2;
@@ -124314,7 +124404,7 @@ async function _handleRequest(fetcher, method, url, options, parameters, body, n
124314
124404
  if (!contentType || !contentType.includes("application/json")) return {};
124315
124405
  }
124316
124406
  return result2.json();
124317
- }).then((data) => resolve4(data)).catch((error) => handleError(error, reject2, options, namespace));
124407
+ }).then((data) => resolve5(data)).catch((error) => handleError(error, reject2, options, namespace));
124318
124408
  });
124319
124409
  }
124320
124410
  function createFetchApi(namespace = "storage") {
@@ -126932,11 +127022,11 @@ __export(dist_exports, {
126932
127022
  });
126933
127023
  function __awaiter2(thisArg, _arguments, P, generator) {
126934
127024
  function adopt(value) {
126935
- return value instanceof P ? value : new P(function(resolve4) {
126936
- resolve4(value);
127025
+ return value instanceof P ? value : new P(function(resolve5) {
127026
+ resolve5(value);
126937
127027
  });
126938
127028
  }
126939
- return new (P || (P = Promise))(function(resolve4, reject2) {
127029
+ return new (P || (P = Promise))(function(resolve5, reject2) {
126940
127030
  function fulfilled(value) {
126941
127031
  try {
126942
127032
  step(generator.next(value));
@@ -126952,7 +127042,7 @@ function __awaiter2(thisArg, _arguments, P, generator) {
126952
127042
  }
126953
127043
  }
126954
127044
  function step(result2) {
126955
- result2.done ? resolve4(result2.value) : adopt(result2.value).then(fulfilled, rejected);
127045
+ result2.done ? resolve5(result2.value) : adopt(result2.value).then(fulfilled, rejected);
126956
127046
  }
126957
127047
  step((generator = generator.apply(thisArg, _arguments || [])).next());
126958
127048
  });
@@ -128257,12 +128347,12 @@ async function callAutoRaced(opts, parallelism) {
128257
128347
  }
128258
128348
  async function firstSuccessful(promises) {
128259
128349
  const pending = new Set(promises);
128260
- return new Promise((resolve4, reject2) => {
128350
+ return new Promise((resolve5, reject2) => {
128261
128351
  promises.forEach((promise) => {
128262
128352
  promise.then((outcome) => {
128263
128353
  pending.delete(promise);
128264
128354
  if ("result" in outcome) {
128265
- resolve4(outcome);
128355
+ resolve5(outcome);
128266
128356
  } else if (pending.size === 0) {
128267
128357
  reject2(new Error(`All ${promises.length} racers failed`));
128268
128358
  }
@@ -130156,7 +130246,7 @@ function createProviderAbortSignal(parent, timeoutMs = AUTO_ROUTER_PROVIDER_TIME
130156
130246
  };
130157
130247
  }
130158
130248
  function readStreamChunkWithIdleTimeout(reader, idleMs, abortSignal) {
130159
- return new Promise((resolve4, reject2) => {
130249
+ return new Promise((resolve5, reject2) => {
130160
130250
  const timer = setTimeout(
130161
130251
  () => reject2(new Error(`stream idle timeout after ${idleMs}ms`)),
130162
130252
  idleMs
@@ -130177,7 +130267,7 @@ function readStreamChunkWithIdleTimeout(reader, idleMs, abortSignal) {
130177
130267
  (result2) => {
130178
130268
  clearTimeout(timer);
130179
130269
  if (abortSignal) abortSignal.removeEventListener("abort", onAbort);
130180
- resolve4(result2);
130270
+ resolve5(result2);
130181
130271
  },
130182
130272
  (err) => {
130183
130273
  clearTimeout(timer);
@@ -133824,8 +133914,8 @@ function validateArgs(name, args) {
133824
133914
  return `${name}.path must be a safe path.`;
133825
133915
  return null;
133826
133916
  case TOOL_NAMES.writeLocalFile:
133827
- if (!isSafeRelativePath(args.path))
133828
- return "writeLocalFile.path must be a workspace-relative path such as intelligence_center_mockup.html, not an absolute /Users/... path.";
133917
+ if (!isSafeFilePath(args.path))
133918
+ return "writeLocalFile.path must be a safe local path.";
133829
133919
  if (typeof args.content !== "string")
133830
133920
  return "writeLocalFile.content must be a string.";
133831
133921
  if (args.overwrite !== void 0 && typeof args.overwrite !== "boolean")
@@ -133833,20 +133923,20 @@ function validateArgs(name, args) {
133833
133923
  return null;
133834
133924
  case TOOL_NAMES.moveLocalFile:
133835
133925
  case TOOL_NAMES.copyLocalFile:
133836
- if (!isSafeRelativePath(args.from))
133837
- return `${name}.from must be a safe relative path.`;
133838
- if (!isSafeRelativePath(args.to))
133839
- return `${name}.to must be a safe relative path.`;
133926
+ if (!isSafeFilePath(args.from))
133927
+ return `${name}.from must be a safe local path.`;
133928
+ if (!isSafeFilePath(args.to))
133929
+ return `${name}.to must be a safe local path.`;
133840
133930
  return null;
133841
133931
  case TOOL_NAMES.createDirectory:
133842
133932
  case TOOL_NAMES.deleteLocalFile:
133843
133933
  case TOOL_NAMES.printFile:
133844
- if (!isSafeRelativePath(args.path))
133845
- return `${name}.path must be a safe relative path.`;
133934
+ if (!isSafeFilePath(args.path))
133935
+ return `${name}.path must be a safe local path.`;
133846
133936
  return null;
133847
133937
  case TOOL_NAMES.editLocalFile:
133848
- if (!isSafeRelativePath(args.path))
133849
- return "editLocalFile.path must be a safe relative path.";
133938
+ if (!isSafeFilePath(args.path))
133939
+ return "editLocalFile.path must be a safe local path.";
133850
133940
  if (typeof args.oldText !== "string" || !args.oldText)
133851
133941
  return "editLocalFile.oldText must be a non-empty string.";
133852
133942
  if (typeof args.newText !== "string")
@@ -133871,8 +133961,8 @@ function validateArgs(name, args) {
133871
133961
  return "listLocalSources.maxResults must be a positive number.";
133872
133962
  return null;
133873
133963
  case TOOL_NAMES.readLocalSourceFile:
133874
- if (!isSafeLocalSourceId(args.localSourceId) && !isSafeRelativePath(args.localSourceId))
133875
- return "readLocalSourceFile.localSourceId must be a valid local source id or safe relative path.";
133964
+ if (!isSafeLocalSourceId(args.localSourceId) && !isSafeFilePath(args.localSourceId))
133965
+ return "readLocalSourceFile.localSourceId must be a valid local source id or safe local path.";
133876
133966
  return null;
133877
133967
  case TOOL_NAMES.generateAPAuditPacket:
133878
133968
  if (typeof args.folderPath !== "string" || !args.folderPath.trim())
@@ -134140,7 +134230,7 @@ function isSafeFilePath(value) {
134140
134230
  if (typeof value !== "string") return false;
134141
134231
  const trimmed = value.trim();
134142
134232
  if (!trimmed) return false;
134143
- if (/^file:/i.test(trimmed) || trimmed.includes("::") || trimmed === "~" || trimmed.startsWith("~/"))
134233
+ if (/^file:/i.test(trimmed) || trimmed.includes("::"))
134144
134234
  return false;
134145
134235
  return !trimmed.split(/[\\/]+/).includes("..");
134146
134236
  }
@@ -134423,13 +134513,13 @@ function getDesktopToolDefinitions() {
134423
134513
  type: "function",
134424
134514
  function: {
134425
134515
  name: TOOL_NAMES.writeLocalFile,
134426
- description: "Create or overwrite a local UTF-8 text file. Permission mode controls whether it runs immediately or asks for approval.",
134516
+ description: "Create or overwrite a local UTF-8 text file. Accepts absolute paths, ~/ paths, or paths relative to the current local working directory. Permission mode controls whether it runs immediately or asks for approval.",
134427
134517
  parameters: {
134428
134518
  type: "object",
134429
134519
  properties: {
134430
134520
  path: {
134431
134521
  type: "string",
134432
- description: "Workspace-relative path to write, such as intelligence_center_mockup.html or reports/summary.md. Do not pass an absolute /Users/... path."
134522
+ description: "Path to write. May be absolute, ~/..., or relative to the current local working directory."
134433
134523
  },
134434
134524
  content: {
134435
134525
  type: "string",
@@ -134449,17 +134539,17 @@ function getDesktopToolDefinitions() {
134449
134539
  type: "function",
134450
134540
  function: {
134451
134541
  name: TOOL_NAMES.moveLocalFile,
134452
- description: "Move or rename a file within the approved workspace. Write-tier risk.",
134542
+ description: "Move or rename a local file. Accepts absolute paths, ~/ paths, or paths relative to the current local working directory. Write-tier risk.",
134453
134543
  parameters: {
134454
134544
  type: "object",
134455
134545
  properties: {
134456
134546
  from: {
134457
134547
  type: "string",
134458
- description: "Workspace-relative source path to move."
134548
+ description: "Source path to move."
134459
134549
  },
134460
134550
  to: {
134461
134551
  type: "string",
134462
- description: "Workspace-relative destination path."
134552
+ description: "Destination path."
134463
134553
  }
134464
134554
  },
134465
134555
  required: ["from", "to"],
@@ -134471,17 +134561,17 @@ function getDesktopToolDefinitions() {
134471
134561
  type: "function",
134472
134562
  function: {
134473
134563
  name: TOOL_NAMES.copyLocalFile,
134474
- description: "Copy a file within the approved workspace. Write-tier risk.",
134564
+ description: "Copy a local file. Accepts absolute paths, ~/ paths, or paths relative to the current local working directory. Write-tier risk.",
134475
134565
  parameters: {
134476
134566
  type: "object",
134477
134567
  properties: {
134478
134568
  from: {
134479
134569
  type: "string",
134480
- description: "Workspace-relative source path to copy."
134570
+ description: "Source path to copy."
134481
134571
  },
134482
134572
  to: {
134483
134573
  type: "string",
134484
- description: "Workspace-relative destination path."
134574
+ description: "Destination path."
134485
134575
  }
134486
134576
  },
134487
134577
  required: ["from", "to"],
@@ -134493,13 +134583,13 @@ function getDesktopToolDefinitions() {
134493
134583
  type: "function",
134494
134584
  function: {
134495
134585
  name: TOOL_NAMES.createDirectory,
134496
- description: "Create a workspace-local directory recursively (mkdir -p). Write-tier risk.",
134586
+ description: "Create a local directory recursively (mkdir -p). Accepts absolute paths, ~/ paths, or paths relative to the current local working directory. Write-tier risk.",
134497
134587
  parameters: {
134498
134588
  type: "object",
134499
134589
  properties: {
134500
134590
  path: {
134501
134591
  type: "string",
134502
- description: "Workspace-relative directory path to create."
134592
+ description: "Directory path to create."
134503
134593
  }
134504
134594
  },
134505
134595
  required: ["path"],
@@ -134511,13 +134601,13 @@ function getDesktopToolDefinitions() {
134511
134601
  type: "function",
134512
134602
  function: {
134513
134603
  name: TOOL_NAMES.deleteLocalFile,
134514
- description: "Delete a single workspace-local file. Lower permission modes ask for approval; Take the Wheel runs it without asking.",
134604
+ description: "Delete a single local file. Accepts absolute paths, ~/ paths, or paths relative to the current local working directory. Lower permission modes ask for approval; Take the Wheel runs it without asking.",
134515
134605
  parameters: {
134516
134606
  type: "object",
134517
134607
  properties: {
134518
134608
  path: {
134519
134609
  type: "string",
134520
- description: "Workspace-relative file path to delete."
134610
+ description: "File path to delete."
134521
134611
  }
134522
134612
  },
134523
134613
  required: ["path"],
@@ -134529,13 +134619,13 @@ function getDesktopToolDefinitions() {
134529
134619
  type: "function",
134530
134620
  function: {
134531
134621
  name: TOOL_NAMES.printFile,
134532
- description: "Send a workspace-local file to the default printer using the system print command. Write-tier risk.",
134622
+ description: "Send a local file to the default printer using the system print command. Write-tier risk.",
134533
134623
  parameters: {
134534
134624
  type: "object",
134535
134625
  properties: {
134536
134626
  path: {
134537
134627
  type: "string",
134538
- description: "Workspace-relative file path to print."
134628
+ description: "File path to print."
134539
134629
  }
134540
134630
  },
134541
134631
  required: ["path"],
@@ -134547,13 +134637,13 @@ function getDesktopToolDefinitions() {
134547
134637
  type: "function",
134548
134638
  function: {
134549
134639
  name: TOOL_NAMES.editLocalFile,
134550
- description: "Exact find/replace edit in a workspace-local UTF-8 text file. Lower permission modes ask for approval; Take the Wheel runs it without asking.",
134640
+ description: "Exact find/replace edit in a local UTF-8 text file. Accepts absolute paths, ~/ paths, or paths relative to the current local working directory. Lower permission modes ask for approval; Take the Wheel runs it without asking.",
134551
134641
  parameters: {
134552
134642
  type: "object",
134553
134643
  properties: {
134554
134644
  path: {
134555
134645
  type: "string",
134556
- description: "Workspace-relative path to edit."
134646
+ description: "Path to edit."
134557
134647
  },
134558
134648
  oldText: { type: "string", description: "Exact text to replace." },
134559
134649
  newText: { type: "string", description: "Replacement text." },
@@ -138738,8 +138828,8 @@ var init_capabilityBus = __esm({
138738
138828
  }
138739
138829
  let resolvePromise = () => {
138740
138830
  };
138741
- const promise = new Promise((resolve4) => {
138742
- resolvePromise = resolve4;
138831
+ const promise = new Promise((resolve5) => {
138832
+ resolvePromise = resolve5;
138743
138833
  });
138744
138834
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
138745
138835
  const request = {
@@ -138796,7 +138886,7 @@ var init_capabilityBus = __esm({
138796
138886
  return true;
138797
138887
  }
138798
138888
  getPending(sessionId) {
138799
- return Array.from(this.pending.values()).filter((entry) => !sessionId || entry.sessionId === sessionId).map(({ resolve: resolve4, promise, ...request }) => request);
138889
+ return Array.from(this.pending.values()).filter((entry) => !sessionId || entry.sessionId === sessionId).map(({ resolve: resolve5, promise, ...request }) => request);
138800
138890
  }
138801
138891
  dispatchBrowserEvent(type, payload) {
138802
138892
  if (typeof window === "undefined") return;
@@ -139236,7 +139326,7 @@ function createAbortableBrowserDriver(driver, signal, timeoutMs = DEFAULT_BROWSE
139236
139326
  };
139237
139327
  }
139238
139328
  function withAbortAndTimeout(promise, signal, timeoutMs, label) {
139239
- return new Promise((resolve4, reject2) => {
139329
+ return new Promise((resolve5, reject2) => {
139240
139330
  if (signal?.aborted) {
139241
139331
  reject2(abortError(`${label} cancelled.`));
139242
139332
  return;
@@ -139262,7 +139352,7 @@ function withAbortAndTimeout(promise, signal, timeoutMs, label) {
139262
139352
  );
139263
139353
  }
139264
139354
  promise.then(
139265
- (value) => finish(() => resolve4(value)),
139355
+ (value) => finish(() => resolve5(value)),
139266
139356
  (error) => finish(() => reject2(error))
139267
139357
  );
139268
139358
  });
@@ -140021,7 +140111,7 @@ async function maybeWaitForCapabilityAndRetry(failureKind, input, runtime, snaps
140021
140111
  function waitForAbortable(promise, signal) {
140022
140112
  if (!signal) return promise;
140023
140113
  if (signal.aborted) return Promise.reject(new DOMException("Browser action cancelled.", "AbortError"));
140024
- return new Promise((resolve4, reject2) => {
140114
+ return new Promise((resolve5, reject2) => {
140025
140115
  const onAbort = () => {
140026
140116
  signal.removeEventListener("abort", onAbort);
140027
140117
  reject2(new DOMException("Browser action cancelled.", "AbortError"));
@@ -140030,7 +140120,7 @@ function waitForAbortable(promise, signal) {
140030
140120
  promise.then(
140031
140121
  (value) => {
140032
140122
  signal.removeEventListener("abort", onAbort);
140033
- resolve4(value);
140123
+ resolve5(value);
140034
140124
  },
140035
140125
  (error) => {
140036
140126
  signal.removeEventListener("abort", onAbort);
@@ -140726,8 +140816,8 @@ function pasteShortcut() {
140726
140816
  }
140727
140817
  function browserShortcut(key) {
140728
140818
  const override = (typeof process !== "undefined" ? process.env?.PERCH_PLAYWRIGHT_OS : "")?.toLowerCase();
140729
- const platform2 = (typeof process !== "undefined" ? process.platform : "") ?? "";
140730
- const isMac = override === "mac" || override !== "windows" && override !== "linux" && platform2 === "darwin";
140819
+ const platform3 = (typeof process !== "undefined" ? process.platform : "") ?? "";
140820
+ const isMac = override === "mac" || override !== "windows" && override !== "linux" && platform3 === "darwin";
140731
140821
  return isMac ? `Meta+${key}` : `Control+${key}`;
140732
140822
  }
140733
140823
  function failureMessage(kind) {
@@ -140766,12 +140856,12 @@ function isAbortError(err) {
140766
140856
  return err instanceof DOMException && err.name === "AbortError";
140767
140857
  }
140768
140858
  function delay(ms, signal) {
140769
- return new Promise((resolve4, reject2) => {
140859
+ return new Promise((resolve5, reject2) => {
140770
140860
  if (signal?.aborted) {
140771
140861
  reject2(new DOMException("Browser action cancelled.", "AbortError"));
140772
140862
  return;
140773
140863
  }
140774
- const timeout = globalThis.setTimeout(resolve4, ms);
140864
+ const timeout = globalThis.setTimeout(resolve5, ms);
140775
140865
  signal?.addEventListener(
140776
140866
  "abort",
140777
140867
  () => {
@@ -141197,12 +141287,12 @@ function toStringArray2(value) {
141197
141287
  return result2.length > 0 ? result2 : void 0;
141198
141288
  }
141199
141289
  function pause(ms, signal) {
141200
- return new Promise((resolve4, reject2) => {
141290
+ return new Promise((resolve5, reject2) => {
141201
141291
  if (signal?.aborted) {
141202
141292
  reject2(new DOMException("Browser research cancelled.", "AbortError"));
141203
141293
  return;
141204
141294
  }
141205
- const timer = setTimeout(resolve4, ms);
141295
+ const timer = setTimeout(resolve5, ms);
141206
141296
  signal?.addEventListener(
141207
141297
  "abort",
141208
141298
  () => {
@@ -141589,7 +141679,7 @@ async function fetchData(url, type = "text") {
141589
141679
  }
141590
141680
  return response.text();
141591
141681
  }
141592
- return new Promise((resolve4, reject2) => {
141682
+ return new Promise((resolve5, reject2) => {
141593
141683
  const request = new XMLHttpRequest();
141594
141684
  request.open("GET", url, true);
141595
141685
  request.responseType = type;
@@ -141602,10 +141692,10 @@ async function fetchData(url, type = "text") {
141602
141692
  case "arraybuffer":
141603
141693
  case "blob":
141604
141694
  case "json":
141605
- resolve4(request.response);
141695
+ resolve5(request.response);
141606
141696
  return;
141607
141697
  }
141608
- resolve4(request.responseText);
141698
+ resolve5(request.responseText);
141609
141699
  return;
141610
141700
  }
141611
141701
  reject2(new Error(request.statusText));
@@ -144612,7 +144702,7 @@ var init_pdf = __esm({
144612
144702
  var defineProperty = Object.defineProperty;
144613
144703
  var stringSlice = uncurryThis("".slice);
144614
144704
  var replace = uncurryThis("".replace);
144615
- var join = uncurryThis([].join);
144705
+ var join2 = uncurryThis([].join);
144616
144706
  var CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function() {
144617
144707
  return defineProperty(function() {
144618
144708
  }, "length", { value: 8 }).length !== 8;
@@ -144639,7 +144729,7 @@ var init_pdf = __esm({
144639
144729
  }
144640
144730
  var state = enforceInternalState(value);
144641
144731
  if (!hasOwn(state, "source")) {
144642
- state.source = join(TEMPLATE, typeof name == "string" ? name : "");
144732
+ state.source = join2(TEMPLATE, typeof name == "string" ? name : "");
144643
144733
  }
144644
144734
  return value;
144645
144735
  };
@@ -144667,13 +144757,13 @@ var init_pdf = __esm({
144667
144757
  var aCallable = __webpack_require__2(9306);
144668
144758
  var $TypeError = TypeError;
144669
144759
  var PromiseCapability = function(C) {
144670
- var resolve4, reject2;
144760
+ var resolve5, reject2;
144671
144761
  this.promise = new C(function($$resolve, $$reject) {
144672
- if (resolve4 !== void 0 || reject2 !== void 0) throw new $TypeError("Bad Promise constructor");
144673
- resolve4 = $$resolve;
144762
+ if (resolve5 !== void 0 || reject2 !== void 0) throw new $TypeError("Bad Promise constructor");
144763
+ resolve5 = $$resolve;
144674
144764
  reject2 = $$reject;
144675
144765
  });
144676
- this.resolve = aCallable(resolve4);
144766
+ this.resolve = aCallable(resolve5);
144677
144767
  this.reject = aCallable(reject2);
144678
144768
  };
144679
144769
  module2.exports.f = function(C) {
@@ -147791,11 +147881,11 @@ var init_pdf = __esm({
147791
147881
  const mustRemoveAspectRatioPromise = _ImageManager._isSVGFittingCanvas;
147792
147882
  const fileReader = new FileReader();
147793
147883
  const imageElement = new Image();
147794
- const imagePromise = new Promise((resolve4, reject2) => {
147884
+ const imagePromise = new Promise((resolve5, reject2) => {
147795
147885
  imageElement.onload = () => {
147796
147886
  data.bitmap = imageElement;
147797
147887
  data.isSvg = true;
147798
- resolve4();
147888
+ resolve5();
147799
147889
  };
147800
147890
  fileReader.onload = async () => {
147801
147891
  const url = data.svgUrl = fileReader.result;
@@ -151644,8 +151734,8 @@ var init_pdf = __esm({
151644
151734
  if (this.isSyncFontLoadingSupported) {
151645
151735
  return;
151646
151736
  }
151647
- await new Promise((resolve4) => {
151648
- const request = this._queueLoadingCallback(resolve4);
151737
+ await new Promise((resolve5) => {
151738
+ const request = this._queueLoadingCallback(resolve5);
151649
151739
  this._prepareFontLoadEvent(font, request);
151650
151740
  });
151651
151741
  }
@@ -155985,8 +156075,8 @@ var init_pdf = __esm({
155985
156075
  this._readCapability = Promise.withResolvers();
155986
156076
  this._headersCapability = Promise.withResolvers();
155987
156077
  const fs14 = process.getBuiltinModule("fs");
155988
- fs14.promises.lstat(this._url).then((stat) => {
155989
- this._contentLength = stat.size;
156078
+ fs14.promises.lstat(this._url).then((stat2) => {
156079
+ this._contentLength = stat2.size;
155990
156080
  this._setReadableStream(fs14.createReadStream(this._url));
155991
156081
  this._headersCapability.resolve();
155992
156082
  }, (error) => {
@@ -157002,14 +157092,14 @@ var init_pdf = __esm({
157002
157092
  return this.getXfa().then((xfa) => XfaText.textContent(xfa));
157003
157093
  }
157004
157094
  const readableStream = this.streamTextContent(params);
157005
- return new Promise(function(resolve4, reject2) {
157095
+ return new Promise(function(resolve5, reject2) {
157006
157096
  function pump() {
157007
157097
  reader.read().then(function({
157008
157098
  value,
157009
157099
  done
157010
157100
  }) {
157011
157101
  if (done) {
157012
- resolve4(textContent);
157102
+ resolve5(textContent);
157013
157103
  return;
157014
157104
  }
157015
157105
  textContent.lang ??= value.lang;
@@ -165106,7 +165196,7 @@ var init_pdf = __esm({
165106
165196
  input.type = "file";
165107
165197
  input.accept = _StampEditor.supportedTypesStr;
165108
165198
  const signal = this._uiManager._signal;
165109
- this.#bitmapPromise = new Promise((resolve4) => {
165199
+ this.#bitmapPromise = new Promise((resolve5) => {
165110
165200
  input.addEventListener("change", async () => {
165111
165201
  if (!input.files || input.files.length === 0) {
165112
165202
  this.remove();
@@ -165121,13 +165211,13 @@ var init_pdf = __esm({
165121
165211
  });
165122
165212
  this.#getBitmapFetched(data);
165123
165213
  }
165124
- resolve4();
165214
+ resolve5();
165125
165215
  }, {
165126
165216
  signal
165127
165217
  });
165128
165218
  input.addEventListener("cancel", () => {
165129
165219
  this.remove();
165130
- resolve4();
165220
+ resolve5();
165131
165221
  }, {
165132
165222
  signal
165133
165223
  });
@@ -170434,13 +170524,13 @@ var require_thenables = __commonJS({
170434
170524
  promise._captureStackTrace();
170435
170525
  if (context) context._popContext();
170436
170526
  var synchronous = true;
170437
- var result2 = util.tryCatch(then).call(x, resolve4, reject2);
170527
+ var result2 = util.tryCatch(then).call(x, resolve5, reject2);
170438
170528
  synchronous = false;
170439
170529
  if (promise && result2 === errorObj2) {
170440
170530
  promise._rejectCallback(result2.e, true, true);
170441
170531
  promise = null;
170442
170532
  }
170443
- function resolve4(value) {
170533
+ function resolve5(value) {
170444
170534
  if (!promise) return;
170445
170535
  promise._resolveCallback(value);
170446
170536
  promise = null;
@@ -170975,9 +171065,9 @@ var require_debuggability = __commonJS({
170975
171065
  return false;
170976
171066
  }
170977
171067
  Promise2.prototype._fireEvent = defaultFireEvent;
170978
- Promise2.prototype._execute = function(executor, resolve4, reject2) {
171068
+ Promise2.prototype._execute = function(executor, resolve5, reject2) {
170979
171069
  try {
170980
- executor(resolve4, reject2);
171070
+ executor(resolve5, reject2);
170981
171071
  } catch (e) {
170982
171072
  return e;
170983
171073
  }
@@ -171000,10 +171090,10 @@ var require_debuggability = __commonJS({
171000
171090
  ;
171001
171091
  ;
171002
171092
  };
171003
- function cancellationExecute(executor, resolve4, reject2) {
171093
+ function cancellationExecute(executor, resolve5, reject2) {
171004
171094
  var promise = this;
171005
171095
  try {
171006
- executor(resolve4, reject2, function(onCancel) {
171096
+ executor(resolve5, reject2, function(onCancel) {
171007
171097
  if (typeof onCancel !== "function") {
171008
171098
  throw new TypeError("onCancel must be a function, got: " + util.toString(onCancel));
171009
171099
  }
@@ -173026,15 +173116,15 @@ var require_generators = __commonJS({
173026
173116
  var stack = new Error().stack;
173027
173117
  return function() {
173028
173118
  var generator = generatorFunction.apply(this, arguments);
173029
- var spawn2 = new PromiseSpawn$(
173119
+ var spawn3 = new PromiseSpawn$(
173030
173120
  void 0,
173031
173121
  void 0,
173032
173122
  yieldHandler,
173033
173123
  stack
173034
173124
  );
173035
- var ret2 = spawn2.promise();
173036
- spawn2._generator = generator;
173037
- spawn2._promiseFulfilled(void 0);
173125
+ var ret2 = spawn3.promise();
173126
+ spawn3._generator = generator;
173127
+ spawn3._promiseFulfilled(void 0);
173038
173128
  return ret2;
173039
173129
  };
173040
173130
  };
@@ -173049,9 +173139,9 @@ var require_generators = __commonJS({
173049
173139
  if (typeof generatorFunction !== "function") {
173050
173140
  return apiRejection("generatorFunction must be a function\n\n See http://goo.gl/MqrFmX\n");
173051
173141
  }
173052
- var spawn2 = new PromiseSpawn(generatorFunction, this);
173053
- var ret2 = spawn2.promise();
173054
- spawn2._run(Promise2.spawn);
173142
+ var spawn3 = new PromiseSpawn(generatorFunction, this);
173143
+ var ret2 = spawn3.promise();
173144
+ spawn3._run(Promise2.spawn);
173055
173145
  return ret2;
173056
173146
  };
173057
173147
  };
@@ -174649,14 +174739,14 @@ var require_promises = __commonJS({
174649
174739
  });
174650
174740
  };
174651
174741
  function defer() {
174652
- var resolve4;
174742
+ var resolve5;
174653
174743
  var reject2;
174654
174744
  var promise = new bluebird.Promise(function(resolveArg, rejectArg) {
174655
- resolve4 = resolveArg;
174745
+ resolve5 = resolveArg;
174656
174746
  reject2 = rejectArg;
174657
174747
  });
174658
174748
  return {
174659
- resolve: resolve4,
174749
+ resolve: resolve5,
174660
174750
  reject: reject2,
174661
174751
  promise
174662
174752
  };
@@ -175345,7 +175435,7 @@ var require_BufferList = __commonJS({
175345
175435
  this.head = this.tail = null;
175346
175436
  this.length = 0;
175347
175437
  };
175348
- BufferList.prototype.join = function join(s) {
175438
+ BufferList.prototype.join = function join2(s) {
175349
175439
  if (this.length === 0) return "";
175350
175440
  var p = this.head;
175351
175441
  var ret2 = "" + p.data;
@@ -177320,8 +177410,8 @@ var require_lib4 = __commonJS({
177320
177410
  return this;
177321
177411
  }
177322
177412
  var p = this.constructor;
177323
- return this.then(resolve5, reject3);
177324
- function resolve5(value) {
177413
+ return this.then(resolve6, reject3);
177414
+ function resolve6(value) {
177325
177415
  function yes() {
177326
177416
  return value;
177327
177417
  }
@@ -177474,8 +177564,8 @@ var require_lib4 = __commonJS({
177474
177564
  }
177475
177565
  return out;
177476
177566
  }
177477
- Promise2.resolve = resolve4;
177478
- function resolve4(value) {
177567
+ Promise2.resolve = resolve5;
177568
+ function resolve5(value) {
177479
177569
  if (value instanceof this) {
177480
177570
  return value;
177481
177571
  }
@@ -178006,10 +178096,10 @@ var require_utils2 = __commonJS({
178006
178096
  var promise = external.Promise.resolve(inputData).then(function(data) {
178007
178097
  var isBlob = support.blob && (data instanceof Blob || ["[object File]", "[object Blob]"].indexOf(Object.prototype.toString.call(data)) !== -1);
178008
178098
  if (isBlob && typeof FileReader !== "undefined") {
178009
- return new external.Promise(function(resolve4, reject2) {
178099
+ return new external.Promise(function(resolve5, reject2) {
178010
178100
  var reader = new FileReader();
178011
178101
  reader.onload = function(e) {
178012
- resolve4(e.target.result);
178102
+ resolve5(e.target.result);
178013
178103
  };
178014
178104
  reader.onerror = function(e) {
178015
178105
  reject2(e.target.error);
@@ -178564,7 +178654,7 @@ var require_StreamHelper = __commonJS({
178564
178654
  }
178565
178655
  }
178566
178656
  function accumulate(helper, updateCallback) {
178567
- return new external.Promise(function(resolve4, reject2) {
178657
+ return new external.Promise(function(resolve5, reject2) {
178568
178658
  var dataArray = [];
178569
178659
  var chunkType = helper._internalType, resultType = helper._outputType, mimeType = helper._mimeType;
178570
178660
  helper.on("data", function(data, meta) {
@@ -178578,7 +178668,7 @@ var require_StreamHelper = __commonJS({
178578
178668
  }).on("end", function() {
178579
178669
  try {
178580
178670
  var result2 = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
178581
- resolve4(result2);
178671
+ resolve5(result2);
178582
178672
  } catch (e) {
178583
178673
  reject2(e);
178584
178674
  }
@@ -183370,7 +183460,7 @@ var require_ZipFileWorker = __commonJS({
183370
183460
  var generateDosExternalFileAttr = function(dosPermissions) {
183371
183461
  return (dosPermissions || 0) & 63;
183372
183462
  };
183373
- var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform2, encodeFileName) {
183463
+ var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform3, encodeFileName) {
183374
183464
  var file = streamInfo["file"], compression = streamInfo["compression"], useCustomEncoding = encodeFileName !== utf8.utf8encode, encodedFileName = utils3.transformTo("string", encodeFileName(file.name)), utfEncodedFileName = utils3.transformTo("string", utf8.utf8encode(file.name)), comment = file.comment, encodedComment = utils3.transformTo("string", encodeFileName(comment)), utfEncodedComment = utils3.transformTo("string", utf8.utf8encode(comment)), useUTF8ForFileName = utfEncodedFileName.length !== file.name.length, useUTF8ForComment = utfEncodedComment.length !== comment.length, dosTime, dosDate, extraFields = "", unicodePathExtraField = "", unicodeCommentExtraField = "", dir = file.dir, date = file.date;
183375
183465
  var dataInfo = {
183376
183466
  crc32: 0,
@@ -183394,7 +183484,7 @@ var require_ZipFileWorker = __commonJS({
183394
183484
  if (dir) {
183395
183485
  extFileAttr |= 16;
183396
183486
  }
183397
- if (platform2 === "UNIX") {
183487
+ if (platform3 === "UNIX") {
183398
183488
  versionMadeBy = 798;
183399
183489
  extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
183400
183490
  } else {
@@ -183479,11 +183569,11 @@ var require_ZipFileWorker = __commonJS({
183479
183569
  decToHex(streamInfo["uncompressedSize"], 4);
183480
183570
  return descriptor;
183481
183571
  };
183482
- function ZipFileWorker(streamFiles, comment, platform2, encodeFileName) {
183572
+ function ZipFileWorker(streamFiles, comment, platform3, encodeFileName) {
183483
183573
  GenericWorker.call(this, "ZipFileWorker");
183484
183574
  this.bytesWritten = 0;
183485
183575
  this.zipComment = comment;
183486
- this.zipPlatform = platform2;
183576
+ this.zipPlatform = platform3;
183487
183577
  this.encodeFileName = encodeFileName;
183488
183578
  this.streamFiles = streamFiles;
183489
183579
  this.accumulate = false;
@@ -184691,7 +184781,7 @@ var require_load = __commonJS({
184691
184781
  var Crc32Probe = require_Crc32Probe();
184692
184782
  var nodejsUtils = require_nodejsUtils();
184693
184783
  function checkEntryCRC32(zipEntry) {
184694
- return new external.Promise(function(resolve4, reject2) {
184784
+ return new external.Promise(function(resolve5, reject2) {
184695
184785
  var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
184696
184786
  worker.on("error", function(e) {
184697
184787
  reject2(e);
@@ -184699,7 +184789,7 @@ var require_load = __commonJS({
184699
184789
  if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
184700
184790
  reject2(new Error("Corrupted zip : CRC32 mismatch"));
184701
184791
  } else {
184702
- resolve4();
184792
+ resolve5();
184703
184793
  }
184704
184794
  }).resume();
184705
184795
  });
@@ -194523,7 +194613,7 @@ var require_files = __commonJS({
194523
194613
  var fs14 = __require("fs");
194524
194614
  var url = __require("url");
194525
194615
  var os6 = __require("os");
194526
- var dirname = __require("path").dirname;
194616
+ var dirname2 = __require("path").dirname;
194527
194617
  var resolvePath2 = __require("path").resolve;
194528
194618
  var isAbsolutePath2 = require_path_is_absolute();
194529
194619
  var promises = require_promises();
@@ -194538,10 +194628,10 @@ var require_files = __commonJS({
194538
194628
  }
194539
194629
  };
194540
194630
  }
194541
- var base = options.relativeToFile ? dirname(options.relativeToFile) : null;
194631
+ var base = options.relativeToFile ? dirname2(options.relativeToFile) : null;
194542
194632
  function read(uri, encoding) {
194543
194633
  return resolveUri(uri).then(function(path14) {
194544
- return readFile3(path14, encoding).caught(function(error) {
194634
+ return readFile4(path14, encoding).caught(function(error) {
194545
194635
  var message = "could not open external image: '" + uri + "' (document directory: '" + base + "')\n" + error.message;
194546
194636
  return promises.reject(new Error(message));
194547
194637
  });
@@ -194561,15 +194651,15 @@ var require_files = __commonJS({
194561
194651
  read
194562
194652
  };
194563
194653
  }
194564
- var readFile3 = promises.promisify(fs14.readFile.bind(fs14));
194565
- function uriToPath(uriString, platform2) {
194566
- if (!platform2) {
194567
- platform2 = os6.platform();
194654
+ var readFile4 = promises.promisify(fs14.readFile.bind(fs14));
194655
+ function uriToPath(uriString, platform3) {
194656
+ if (!platform3) {
194657
+ platform3 = os6.platform();
194568
194658
  }
194569
194659
  var uri = url.parse(uriString);
194570
194660
  if (isLocalFileUri(uri) || isRelativeUri(uri)) {
194571
194661
  var path14 = decodeURIComponent(uri.path);
194572
- if (platform2 === "win32" && /^\/[a-z]:/i.test(path14)) {
194662
+ if (platform3 === "win32" && /^\/[a-z]:/i.test(path14)) {
194573
194663
  return path14.slice(1);
194574
194664
  } else {
194575
194665
  return path14;
@@ -197278,10 +197368,10 @@ var require_unzip = __commonJS({
197278
197368
  var promises = require_promises();
197279
197369
  var zipfile = require_zipfile();
197280
197370
  exports2.openZip = openZip;
197281
- var readFile3 = promises.promisify(fs14.readFile);
197371
+ var readFile4 = promises.promisify(fs14.readFile);
197282
197372
  function openZip(options) {
197283
197373
  if (options.path) {
197284
- return readFile3(options.path).then(zipfile.openArrayBuffer);
197374
+ return readFile4(options.path).then(zipfile.openArrayBuffer);
197285
197375
  } else if (options.buffer) {
197286
197376
  return promises.resolve(zipfile.openArrayBuffer(options.buffer));
197287
197377
  } else if (options.file) {
@@ -198417,9 +198507,9 @@ async function resolveScopedLocalSourceId(localSourceIdOrRelativePath, scopedWor
198417
198507
  if (localSourceIdOrRelativePath.includes("::")) return localSourceIdOrRelativePath;
198418
198508
  const scope = await resolveLocalSourceScope(scopedWorkspaceRoot);
198419
198509
  if (!scope) return localSourceIdOrRelativePath;
198420
- const relative = normalizeRelativePath(localSourceIdOrRelativePath);
198510
+ const relative2 = normalizeRelativePath(localSourceIdOrRelativePath);
198421
198511
  const prefix = normalizeRelativePath(scope.relativePrefix);
198422
- const scopedRelative = [prefix, relative].filter(Boolean).join("/");
198512
+ const scopedRelative = [prefix, relative2].filter(Boolean).join("/");
198423
198513
  return `${scope.rootId}::${scopedRelative}`;
198424
198514
  }
198425
198515
  async function resolveApprovedLocalSourceIdForAbsolutePath(pathValue) {
@@ -201827,8 +201917,8 @@ function buildWorkerSourceManifest(input) {
201827
201917
  const exactNames = extractExplicitFileNames(input.objective);
201828
201918
  const maxEntries = Math.max(1, Math.min(200, input.maxEntries ?? 80));
201829
201919
  const candidates = sources.filter((source) => !isExcludedPath(source.relativePath)).filter((source) => {
201830
- const relative = normalize2(source.relativePath);
201831
- if (selectedPrefixes.length > 0 && selectedPrefixes.some((prefix) => relative.startsWith(`${prefix}/`) || relative === prefix)) {
201920
+ const relative2 = normalize2(source.relativePath);
201921
+ if (selectedPrefixes.length > 0 && selectedPrefixes.some((prefix) => relative2.startsWith(`${prefix}/`) || relative2 === prefix)) {
201832
201922
  return true;
201833
201923
  }
201834
201924
  if (exactNames.size > 0 && exactNames.has(source.fileName.toLowerCase())) {
@@ -201893,17 +201983,17 @@ function extractExplicitFileNames(objective) {
201893
201983
  return names;
201894
201984
  }
201895
201985
  function sourceSortScore(source, objective) {
201896
- const relative = normalize2(source.relativePath);
201986
+ const relative2 = normalize2(source.relativePath);
201897
201987
  const file = source.fileName.toLowerCase();
201898
201988
  let score = 0;
201899
201989
  if (objective.includes(file)) score += 100;
201900
- if (relative.includes("perch-output") || relative.includes("perch-artifacts")) score -= 1e3;
201990
+ if (relative2.includes("perch-output") || relative2.includes("perch-artifacts")) score -= 1e3;
201901
201991
  if (/_\d{3}\.(?:txt|csv|md)$/i.test(file)) score -= 15;
201902
201992
  if (file.endsWith(".csv")) score += 8;
201903
201993
  if (file.endsWith(".txt") || file.endsWith(".md")) score += 5;
201904
201994
  if (file.endsWith(".pdf")) score -= 3;
201905
201995
  for (const token of objective.split(/[^a-z0-9]+/i).filter((t) => t.length >= 4)) {
201906
- if (relative.includes(token.toLowerCase())) score += 2;
201996
+ if (relative2.includes(token.toLowerCase())) score += 2;
201907
201997
  }
201908
201998
  return score;
201909
201999
  }
@@ -202331,13 +202421,13 @@ function clamp2(value, min2, max2) {
202331
202421
  return Math.max(min2, Math.min(max2, value));
202332
202422
  }
202333
202423
  function loadImage(dataUrl) {
202334
- return new Promise((resolve4, reject2) => {
202424
+ return new Promise((resolve5, reject2) => {
202335
202425
  if (typeof Image === "undefined") {
202336
202426
  reject2(new Error("Image API unavailable"));
202337
202427
  return;
202338
202428
  }
202339
202429
  const image2 = new Image();
202340
- image2.onload = () => resolve4(image2);
202430
+ image2.onload = () => resolve5(image2);
202341
202431
  image2.onerror = () => reject2(new Error("Failed to decode screenshot"));
202342
202432
  image2.src = dataUrl;
202343
202433
  });
@@ -202465,9 +202555,9 @@ function fitInside(width, height, maxWidth, maxHeight) {
202465
202555
  };
202466
202556
  }
202467
202557
  async function loadImageElement(dataUrl) {
202468
- return new Promise((resolve4, reject2) => {
202558
+ return new Promise((resolve5, reject2) => {
202469
202559
  const img = new Image();
202470
- img.onload = () => resolve4(img);
202560
+ img.onload = () => resolve5(img);
202471
202561
  img.onerror = () => reject2(new Error("Failed to decode screenshot data URL"));
202472
202562
  img.src = dataUrl;
202473
202563
  });
@@ -203689,14 +203779,14 @@ function adapterDriver(ctx) {
203689
203779
  );
203690
203780
  }
203691
203781
  function adapterDelay(ms, signal) {
203692
- return new Promise((resolve4, reject2) => {
203782
+ return new Promise((resolve5, reject2) => {
203693
203783
  if (signal?.aborted) {
203694
203784
  reject2(new DOMException("Adapter wait cancelled.", "AbortError"));
203695
203785
  return;
203696
203786
  }
203697
203787
  const timeout = setTimeout(() => {
203698
203788
  signal?.removeEventListener("abort", onAbort);
203699
- resolve4();
203789
+ resolve5();
203700
203790
  }, ms);
203701
203791
  const onAbort = () => {
203702
203792
  clearTimeout(timeout);
@@ -205289,8 +205379,8 @@ var init_GoogleDocsAdapter = __esm({
205289
205379
 
205290
205380
  // features/perchTerminal/runtime/playwright/adapters/GoogleSheetsAdapter.ts
205291
205381
  function shortcut2(key) {
205292
- const platform2 = typeof navigator === "undefined" ? "" : navigator.platform;
205293
- return /mac/i.test(platform2) ? `Meta+${key}` : `Control+${key}`;
205382
+ const platform3 = typeof navigator === "undefined" ? "" : navigator.platform;
205383
+ return /mac/i.test(platform3) ? `Meta+${key}` : `Control+${key}`;
205294
205384
  }
205295
205385
  var GoogleSheetsAdapter;
205296
205386
  var init_GoogleSheetsAdapter = __esm({
@@ -210307,6 +210397,7 @@ function buildContextSnapshotFromMetrics(threadId, metrics) {
210307
210397
  consecutiveAutoCompactFailures: metrics.consecutiveAutoCompactFailures ?? 0,
210308
210398
  autoCompactedAtIso: metrics.autoCompactedAtIso ?? null,
210309
210399
  autoCompactedMessageCount: metrics.autoCompactedMessageCount,
210400
+ compactionReport: metrics.compactionReport,
210310
210401
  contextResetAtIso: metrics.resetAtIso ?? null,
210311
210402
  builtAtIso: builtAt,
210312
210403
  createdAt: builtAt
@@ -212685,6 +212776,29 @@ var init_localSources = __esm({
212685
212776
  classification: { native: false },
212686
212777
  handler: async (args, ctx) => {
212687
212778
  const workspaceRoot = effectiveWorkspaceRoot(ctx);
212779
+ const explicitPath = typeof args.path === "string" && args.path.trim() ? args.path.trim() : "";
212780
+ if (explicitPath) {
212781
+ const query2 = typeof args.query === "string" ? args.query.trim().toLowerCase() : "";
212782
+ const maxResults2 = sanitizeListLocalSourcesMaxResults(args.maxResults);
212783
+ const globbed = await listWorkspaceFilesGlob({
212784
+ workspaceRoot: explicitPath,
212785
+ pattern: "*",
212786
+ maxResults: MAX_LIST_LOCAL_SOURCES_MAX_RESULTS,
212787
+ fsAccessOverride: "allow_once"
212788
+ });
212789
+ const allMatches = globbed?.matches ?? [];
212790
+ const filtered = (query2 ? allMatches.filter((m) => m.toLowerCase().includes(query2)) : allMatches).slice(0, maxResults2);
212791
+ return {
212792
+ ok: true,
212793
+ scopedFolderPath: explicitPath,
212794
+ viaGlob: true,
212795
+ query: query2 || null,
212796
+ sources: filtered,
212797
+ matches: filtered,
212798
+ totalFound: globbed?.totalFound ?? allMatches.length,
212799
+ truncated: allMatches.length > maxResults2
212800
+ };
212801
+ }
212688
212802
  const scope = await resolveLocalSourceScope(workspaceRoot);
212689
212803
  if (!scope) {
212690
212804
  const globRoot = typeof args.path === "string" && args.path.trim() || workspaceRoot?.trim() || "";
@@ -212746,7 +212860,7 @@ var init_localSources = __esm({
212746
212860
  const requestedLocalSourceId = String(
212747
212861
  args.localSourceId ?? args.path ?? args.filePath ?? ""
212748
212862
  );
212749
- const looksLikePath = isAbsolute3(requestedLocalSourceId.trim()) || !requestedLocalSourceId.includes("::") && requestedLocalSourceId.includes("/");
212863
+ const looksLikePath = isAbsolute3(requestedLocalSourceId.trim()) || requestedLocalSourceId.trim().startsWith("~/") || !requestedLocalSourceId.includes("::") && requestedLocalSourceId.includes("/");
212750
212864
  if (looksLikePath) {
212751
212865
  const r = await readWorkspaceFile({
212752
212866
  workspaceRoot,
@@ -214051,7 +214165,7 @@ async function resolveFsAccessForTool(input) {
214051
214165
  function waitForCapabilityResolution(promise, signal) {
214052
214166
  if (!signal) return promise;
214053
214167
  if (signal.aborted) return Promise.reject(new DOMException("Capability wait cancelled.", "AbortError"));
214054
- return new Promise((resolve4, reject2) => {
214168
+ return new Promise((resolve5, reject2) => {
214055
214169
  const onAbort = () => {
214056
214170
  signal.removeEventListener("abort", onAbort);
214057
214171
  reject2(new DOMException("Capability wait cancelled.", "AbortError"));
@@ -214060,7 +214174,7 @@ function waitForCapabilityResolution(promise, signal) {
214060
214174
  promise.then(
214061
214175
  (value) => {
214062
214176
  signal.removeEventListener("abort", onAbort);
214063
- resolve4(value);
214177
+ resolve5(value);
214064
214178
  },
214065
214179
  (error) => {
214066
214180
  signal.removeEventListener("abort", onAbort);
@@ -214799,14 +214913,103 @@ function pruneBrowserArtifactsInMessages(messages) {
214799
214913
  }
214800
214914
  return pruned;
214801
214915
  }
214802
- function splitMessagesForCompaction(wireMessages, keepCount) {
214803
- if (wireMessages.length <= keepCount) {
214804
- return { toSummarize: [], recent: wireMessages };
214916
+ function resolvePostCompactTargetTokens(contextLimitTokens) {
214917
+ const limit = Number.isFinite(contextLimitTokens) && contextLimitTokens > 0 ? Math.floor(contextLimitTokens) : 128e3;
214918
+ const windowScaled = Math.floor(limit * 0.25);
214919
+ return Math.max(
214920
+ POST_COMPACT_MIN_TARGET_TOKENS,
214921
+ Math.min(
214922
+ POST_COMPACT_MAX_TARGET_TOKENS,
214923
+ POST_COMPACT_TARGET_TOKENS,
214924
+ windowScaled
214925
+ )
214926
+ );
214927
+ }
214928
+ function estimateSingleMessageTokens(message, modelContext) {
214929
+ const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? "");
214930
+ let tokens = tokenizeText(`${message.role}: ${content}`, modelContext).tokenCount;
214931
+ if (message.tool_calls?.length) {
214932
+ tokens += tokenizeText(JSON.stringify(message.tool_calls), modelContext).tokenCount;
214933
+ }
214934
+ return Math.max(1, tokens);
214935
+ }
214936
+ function adjustTailStartForToolPairs(messages, startIndex) {
214937
+ if (startIndex <= 0) return startIndex;
214938
+ const missingToolCallIds = /* @__PURE__ */ new Set();
214939
+ for (let i = startIndex; i < messages.length; i += 1) {
214940
+ const msg = messages[i];
214941
+ if (msg?.role === "tool" && msg.tool_call_id) {
214942
+ missingToolCallIds.add(msg.tool_call_id);
214943
+ }
214944
+ if (msg?.role === "assistant" && msg.tool_calls?.length) {
214945
+ for (const call of msg.tool_calls) missingToolCallIds.delete(call.id);
214946
+ }
214947
+ }
214948
+ if (missingToolCallIds.size === 0) return startIndex;
214949
+ let nextStart = startIndex;
214950
+ for (let i = startIndex - 1; i >= 0 && missingToolCallIds.size > 0; i -= 1) {
214951
+ const msg = messages[i];
214952
+ if (msg.role !== "assistant" || !msg.tool_calls?.length) continue;
214953
+ const matches = msg.tool_calls.some((call) => missingToolCallIds.has(call.id));
214954
+ if (!matches) continue;
214955
+ nextStart = i;
214956
+ for (const call of msg.tool_calls) missingToolCallIds.delete(call.id);
214957
+ }
214958
+ return nextStart;
214959
+ }
214960
+ function selectRecentWireMessagesByTokenBudget(wireMessages, input) {
214961
+ if (wireMessages.length === 0) {
214962
+ return { recent: [], recentTailTokens: 0, startIndex: 0 };
214963
+ }
214964
+ const minMessages = Math.max(1, input.minMessages ?? RECENT_MESSAGES_KEEP_COUNT);
214965
+ const tokenBudget = Math.max(1, Math.floor(input.tokenBudget));
214966
+ let startIndex = wireMessages.length;
214967
+ let tokens = 0;
214968
+ for (let i = wireMessages.length - 1; i >= 0; i -= 1) {
214969
+ const messageTokens = estimateSingleMessageTokens(wireMessages[i], input.modelContext);
214970
+ const selectedCount = wireMessages.length - startIndex;
214971
+ const mustKeep = selectedCount === 0;
214972
+ if (!mustKeep && tokens + messageTokens > tokenBudget) break;
214973
+ tokens += messageTokens;
214974
+ startIndex = i;
214975
+ if (selectedCount + 1 >= minMessages && tokens >= tokenBudget) break;
214976
+ }
214977
+ const adjustedStart = adjustTailStartForToolPairs(wireMessages, startIndex);
214978
+ if (adjustedStart !== startIndex) {
214979
+ startIndex = adjustedStart;
214980
+ tokens = wireMessages.slice(startIndex).reduce(
214981
+ (sum, message) => sum + estimateSingleMessageTokens(message, input.modelContext),
214982
+ 0
214983
+ );
214805
214984
  }
214806
- const splitAt = wireMessages.length - keepCount;
214807
214985
  return {
214808
- toSummarize: wireMessages.slice(0, splitAt),
214809
- recent: wireMessages.slice(splitAt)
214986
+ recent: wireMessages.slice(startIndex),
214987
+ recentTailTokens: tokens,
214988
+ startIndex
214989
+ };
214990
+ }
214991
+ function resolveRecentTailBudgetTokens(input) {
214992
+ const nonMessageTokens = input.breakdownAfterPrune.system + input.breakdownAfterPrune.tools + input.breakdownAfterPrune.overhead;
214993
+ const available = input.postCompactTargetTokens - nonMessageTokens - COMPACT_SUMMARY_RESERVE_TOKENS;
214994
+ return Math.max(
214995
+ POST_COMPACT_RECENT_TAIL_MIN_TOKENS,
214996
+ Math.min(POST_COMPACT_RECENT_TAIL_MAX_TOKENS, available)
214997
+ );
214998
+ }
214999
+ function splitMessagesForCompaction(wireMessages, input) {
215000
+ const { recent, recentTailTokens, startIndex } = selectRecentWireMessagesByTokenBudget(
215001
+ wireMessages,
215002
+ {
215003
+ modelContext: input.modelContext,
215004
+ tokenBudget: input.recentTailTokenBudget,
215005
+ minMessages: input.minMessages
215006
+ }
215007
+ );
215008
+ return {
215009
+ toSummarize: wireMessages.slice(0, startIndex),
215010
+ recent,
215011
+ recentTailTokens,
215012
+ compactedThroughWireIndex: startIndex > 0 ? startIndex - 1 : null
214810
215013
  };
214811
215014
  }
214812
215015
  function formatMessagesForSummary(messages) {
@@ -214901,6 +215104,11 @@ async function prepareLoopMessagesForSend(input) {
214901
215104
  });
214902
215105
  const contextLimitTokens = resolveEffectiveContextLimit(input.option);
214903
215106
  const compactThreshold = getAutoCompactThreshold(input.option);
215107
+ const postCompactTargetTokens = resolvePostCompactTargetTokens(contextLimitTokens);
215108
+ const recentTailTokenBudget = resolveRecentTailBudgetTokens({
215109
+ postCompactTargetTokens,
215110
+ breakdownAfterPrune
215111
+ });
214904
215112
  let breakdown = breakdownAfterPrune;
214905
215113
  const calibrationRatio = typeof input.priorApiContextTokens === "number" && input.priorApiContextTokens > 0 && typeof input.priorEstimateTokens === "number" && input.priorEstimateTokens > 0 ? Math.min(4, Math.max(1, input.priorApiContextTokens / input.priorEstimateTokens)) : 1;
214906
215114
  const anchoredTokens = (estimateTotal) => Math.ceil(estimateTotal * calibrationRatio);
@@ -214929,10 +215137,16 @@ async function prepareLoopMessagesForSend(input) {
214929
215137
  }
214930
215138
  }
214931
215139
  if (autoCompactEnabled && anchoredTokens(breakdown.total) >= compactThreshold) {
214932
- const { toSummarize, recent } = splitMessagesForCompaction(
214933
- working,
214934
- RECENT_MESSAGES_KEEP_COUNT
214935
- );
215140
+ const {
215141
+ toSummarize,
215142
+ recent,
215143
+ recentTailTokens,
215144
+ compactedThroughWireIndex
215145
+ } = splitMessagesForCompaction(working, {
215146
+ modelContext,
215147
+ recentTailTokenBudget,
215148
+ minMessages: RECENT_MESSAGES_KEEP_COUNT
215149
+ });
214936
215150
  if (toSummarize.length > 0) {
214937
215151
  const summary = await summarizeMessagesForCompaction({
214938
215152
  priorSummary: input.compactedSummary,
@@ -214946,6 +215160,7 @@ async function prepareLoopMessagesForSend(input) {
214946
215160
  signal: input.signal
214947
215161
  });
214948
215162
  if (summary) {
215163
+ const compactedAtIso = (/* @__PURE__ */ new Date()).toISOString();
214949
215164
  const compactedMessages = buildCompactedWireMessages(summary, recent);
214950
215165
  working.length = 0;
214951
215166
  working.push(...compactedMessages);
@@ -214956,20 +215171,45 @@ async function prepareLoopMessagesForSend(input) {
214956
215171
  toolDefinitionsJson,
214957
215172
  modelContext
214958
215173
  });
215174
+ const tokensAfter = anchoredTokens(breakdown.total);
215175
+ const summaryTokens = tokenizeText(summary, modelContext).tokenCount;
215176
+ const compactionReport = {
215177
+ tokensBefore: tokensBeforeCompact,
215178
+ tokensAfter,
215179
+ targetTokens: postCompactTargetTokens,
215180
+ summaryTokens,
215181
+ recentTailTokens,
215182
+ recentTailMessages: recent.length,
215183
+ summarizedMessages: toSummarize.length,
215184
+ restoredToolsOrFiles: 0
215185
+ };
214959
215186
  compactionState = {
214960
215187
  compactedSummary: summary,
214961
215188
  tokensBefore: tokensBeforeCompact,
214962
- tokensAfter: anchoredTokens(breakdown.total),
215189
+ tokensAfter,
214963
215190
  autoCompactEnabled: true,
214964
215191
  effectiveContextLimitTokens: contextLimitTokens,
214965
- compactedAtIso: (/* @__PURE__ */ new Date()).toISOString()
215192
+ compactedAtIso,
215193
+ compactBoundary: {
215194
+ kind: "auto",
215195
+ compactedAtIso,
215196
+ compactedThroughWireIndex
215197
+ },
215198
+ compactionReport
214966
215199
  };
214967
215200
  input.onEvent?.({
214968
215201
  type: "context_compacted",
214969
215202
  tokensBefore: tokensBeforeCompact,
214970
- tokensAfter: anchoredTokens(breakdown.total),
215203
+ tokensAfter,
214971
215204
  summary,
214972
- ts: (/* @__PURE__ */ new Date()).toISOString()
215205
+ compactionKind: "auto",
215206
+ targetTokens: postCompactTargetTokens,
215207
+ summaryTokens,
215208
+ recentTailTokens,
215209
+ recentTailMessages: recent.length,
215210
+ summarizedMessages: toSummarize.length,
215211
+ restoredToolsOrFiles: 0,
215212
+ ts: compactedAtIso
214973
215213
  });
214974
215214
  }
214975
215215
  }
@@ -215005,7 +215245,7 @@ async function prepareLoopMessagesForSend(input) {
215005
215245
  microcompactedToolResults
215006
215246
  };
215007
215247
  }
215008
- var OPERATOR_SCREENSHOT_MARKER2, KEPT_SNAPSHOT_MAX_CHARS, KEPT_SNAPSHOT_TRUNCATED_MARKER, BROWSER_TOOL_NAMES, RECENT_MESSAGES_KEEP_COUNT, RECENT_TOOL_RESULTS_KEEP_COUNT, MICROCOMPACT_CLEARED_MARKER, COMPACT_SUMMARY_RESERVE_TOKENS, COMPACT_SYSTEM_PROMPT;
215248
+ var OPERATOR_SCREENSHOT_MARKER2, KEPT_SNAPSHOT_MAX_CHARS, KEPT_SNAPSHOT_TRUNCATED_MARKER, BROWSER_TOOL_NAMES, RECENT_MESSAGES_KEEP_COUNT, RECENT_TOOL_RESULTS_KEEP_COUNT, POST_COMPACT_TARGET_TOKENS, POST_COMPACT_MIN_TARGET_TOKENS, POST_COMPACT_MAX_TARGET_TOKENS, POST_COMPACT_RECENT_TAIL_MIN_TOKENS, POST_COMPACT_RECENT_TAIL_MAX_TOKENS, MICROCOMPACT_CLEARED_MARKER, COMPACT_SUMMARY_RESERVE_TOKENS, COMPACT_SYSTEM_PROMPT;
215009
215249
  var init_contextLoopCompaction = __esm({
215010
215250
  "features/perchTerminal/runtime/services/contextLoopCompaction.ts"() {
215011
215251
  "use strict";
@@ -215035,6 +215275,11 @@ var init_contextLoopCompaction = __esm({
215035
215275
  ]);
215036
215276
  RECENT_MESSAGES_KEEP_COUNT = 12;
215037
215277
  RECENT_TOOL_RESULTS_KEEP_COUNT = 6;
215278
+ POST_COMPACT_TARGET_TOKENS = 32e3;
215279
+ POST_COMPACT_MIN_TARGET_TOKENS = 2e4;
215280
+ POST_COMPACT_MAX_TARGET_TOKENS = 4e4;
215281
+ POST_COMPACT_RECENT_TAIL_MIN_TOKENS = 4e3;
215282
+ POST_COMPACT_RECENT_TAIL_MAX_TOKENS = 24e3;
215038
215283
  MICROCOMPACT_CLEARED_MARKER = "[Old tool result content cleared to free context]";
215039
215284
  COMPACT_SUMMARY_RESERVE_TOKENS = 4096;
215040
215285
  COMPACT_SYSTEM_PROMPT = `You summarize conversation history for a delivery assistant using Google Docs, Gmail, Calendar, and browser tools.
@@ -217109,15 +217354,15 @@ async function dumpModelPayloadIfRequested(input) {
217109
217354
  const dumpDir = process.env.PERCH_MODEL_PAYLOAD_DUMP_DIR;
217110
217355
  if (process.env.PERCH_DUMP_MODEL_PAYLOAD !== "1" && !dumpDir) return;
217111
217356
  try {
217112
- const [{ mkdir, writeFile: writeFile2 }, { join }] = await Promise.all([
217357
+ const [{ mkdir: mkdir2, writeFile: writeFile3 }, { join: join2 }] = await Promise.all([
217113
217358
  import("node:fs/promises"),
217114
217359
  import("node:path")
217115
217360
  ]);
217116
- const dir = dumpDir || join(process.cwd(), ".perch", "debug", "model-payloads");
217117
- await mkdir(dir, { recursive: true });
217118
- const safeRunId = (input.runId || "local").replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 80);
217119
- await writeFile2(
217120
- join(dir, `${safeRunId}-iter-${input.iteration}.json`),
217361
+ const dir = dumpDir || join2(process.cwd(), ".perch", "debug", "model-payloads");
217362
+ await mkdir2(dir, { recursive: true });
217363
+ const safeRunId2 = (input.runId || "local").replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 80);
217364
+ await writeFile3(
217365
+ join2(dir, `${safeRunId2}-iter-${input.iteration}.json`),
217121
217366
  JSON.stringify({
217122
217367
  dumpedAt: (/* @__PURE__ */ new Date()).toISOString(),
217123
217368
  runId: input.runId,
@@ -217605,13 +217850,18 @@ async function runLiveAgentsLoop(input) {
217605
217850
  effectiveContextLimitTokens: state.effectiveContextLimitTokens,
217606
217851
  compactedAtIso: state.compactedAtIso,
217607
217852
  autoCompactedAtIso: state.compactedAtIso,
217853
+ autoCompactedMessageCount: state.compactionReport.summarizedMessages,
217854
+ compactBoundary: state.compactBoundary,
217855
+ compactionReport: state.compactionReport,
217608
217856
  consecutiveAutoCompactFailures: 0
217609
217857
  },
217610
217858
  contextMetrics: latest?.contextMetrics ? {
217611
217859
  ...latest.contextMetrics,
217612
217860
  effectiveLimitTokens: state.effectiveContextLimitTokens,
217613
217861
  autoCompactEnabled: state.autoCompactEnabled,
217614
- autoCompactedAtIso: state.compactedAtIso
217862
+ autoCompactedAtIso: state.compactedAtIso,
217863
+ autoCompactedMessageCount: state.compactionReport.summarizedMessages,
217864
+ compactionReport: state.compactionReport
217615
217865
  } : latest?.contextMetrics,
217616
217866
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
217617
217867
  },
@@ -219818,7 +220068,13 @@ function emitProgressForRuntimeEvent(event, emit, state = {
219818
220068
  case "context_compacted":
219819
220069
  emitInstantItem(emit, state, turnId, "context", itemId(turnId, "context-compacted", ts), "Context compacted", event.summary, ts, {
219820
220070
  tokensBefore: event.tokensBefore,
219821
- tokensAfter: event.tokensAfter
220071
+ tokensAfter: event.tokensAfter,
220072
+ targetTokens: event.targetTokens,
220073
+ summaryTokens: event.summaryTokens,
220074
+ recentTailTokens: event.recentTailTokens,
220075
+ recentTailMessages: event.recentTailMessages,
220076
+ summarizedMessages: event.summarizedMessages,
220077
+ restoredToolsOrFiles: event.restoredToolsOrFiles
219822
220078
  });
219823
220079
  break;
219824
220080
  case "plan_card_generated": {
@@ -221003,6 +221259,7 @@ function resolveThreadContextAccounting(input) {
221003
221259
  consecutiveAutoCompactFailures: priorCompaction?.consecutiveAutoCompactFailures ?? priorMetrics?.consecutiveAutoCompactFailures ?? 0,
221004
221260
  autoCompactedAtIso: priorCompaction?.autoCompactedAtIso ?? priorMetrics?.autoCompactedAtIso ?? null,
221005
221261
  autoCompactedMessageCount: priorCompaction?.autoCompactedMessageCount ?? priorMetrics?.autoCompactedMessageCount,
221262
+ compactionReport: priorCompaction?.compactionReport ?? priorMetrics?.compactionReport,
221006
221263
  tokenCountMethod: fill.tokenCountMethod,
221007
221264
  accountingMethod: "thread_fill_v4: headline = retained window fill high-water until compaction; provider usage is last-call detail; workers off-thread only.",
221008
221265
  resetAtIso: compactedAtIso,
@@ -221126,6 +221383,7 @@ function buildClientContextSnapshot(input) {
221126
221383
  consecutiveAutoCompactFailures: accounting.consecutiveAutoCompactFailures ?? 0,
221127
221384
  autoCompactedAtIso: accounting.autoCompactedAtIso ?? null,
221128
221385
  autoCompactedMessageCount: accounting.autoCompactedMessageCount,
221386
+ compactionReport: accounting.compactionReport,
221129
221387
  builtAtIso: input.builtAtIso,
221130
221388
  createdAt: input.builtAtIso,
221131
221389
  ...wireDisplay ? {
@@ -222671,8 +222929,849 @@ var init_runOperatorTurn = __esm({
222671
222929
  }
222672
222930
  });
222673
222931
 
222674
- // features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts
222932
+ // electron/localSandboxHost.ts
222933
+ import { copyFile, mkdir, mkdtemp, readdir, readFile as readFile3, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
222934
+ import { createHash as createHash2, randomUUID } from "node:crypto";
222935
+ import { basename, dirname, extname, join, relative, resolve as resolve4 } from "node:path";
222936
+ import { homedir, platform, tmpdir } from "node:os";
222675
222937
  import { spawn } from "node:child_process";
222938
+ function buildSandboxRuntimeInfo(networkPolicy) {
222939
+ return {
222940
+ networkPolicy,
222941
+ packageInstalls: networkPolicy === "disabled" ? "disabled" : "enabled",
222942
+ pythonCommand: "python3",
222943
+ pipCommand: "python3 -m pip",
222944
+ inputManifestPath: "input_manifest.json",
222945
+ helperModule: "perch_helpers",
222946
+ helperFunctions: [
222947
+ "input_files",
222948
+ "load_tabular",
222949
+ "extract_pdf_text",
222950
+ "extract_invoice_fields",
222951
+ "extract_pdf_tables",
222952
+ "find_duplicates",
222953
+ "write_report",
222954
+ "sandbox_runtime_info"
222955
+ ],
222956
+ notes: [
222957
+ "Read copied sources from paths listed in input_manifest.json.",
222958
+ networkPolicy === "disabled" ? "Package installs and network egress are disabled for this run." : "Package installs are allowed for this run; use python3 -m pip install <package> when needed.",
222959
+ "perch_helpers.extract_pdf_text has a stdlib raw-stream fallback for simple invoice PDFs, so PDF parsing should not require pdfplumber/pypdf for text fields."
222960
+ ]
222961
+ };
222962
+ }
222963
+ async function writeSandboxManifestFiles(input) {
222964
+ const payload = {
222965
+ ...input.inputManifest,
222966
+ runtime: input.runtimeInfo
222967
+ };
222968
+ const text = JSON.stringify(payload, null, 2);
222969
+ await writeFile2(join(input.workspaceRoot, "input_manifest.json"), text, "utf-8");
222970
+ await writeFile2(join(input.workspaceRoot, "sandbox_runtime.json"), JSON.stringify(input.runtimeInfo, null, 2), "utf-8");
222971
+ }
222972
+ async function runDesktopSandboxCodeJob(spec, deps) {
222973
+ const runId = safeRunId(spec.runId) ?? makeRunId2("sandbox-code");
222974
+ const startedAt = Date.now();
222975
+ const now13 = () => (deps.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
222976
+ const emitLive = (type, payload) => {
222977
+ deps.onEvent?.({ runId, type, payload, ts: now13() });
222978
+ };
222979
+ const commandText = typeof spec.command === "string" ? spec.command.trim() : "";
222980
+ const code = typeof spec.code === "string" ? spec.code : "";
222981
+ const language = spec.language === "node" ? "node" : "python";
222982
+ const executionMode = commandText ? "command" : "script";
222983
+ const executableText = executionMode === "command" ? commandText : code;
222984
+ const codeSha256 = createHash2("sha256").update(executableText).digest("hex");
222985
+ const networkPolicy = spec.networkPolicy ?? "disabled";
222986
+ if (!executableText.trim()) {
222987
+ return { ok: false, kind: "blocked", message: "run_sandbox_code requires either command or code.", executionHost: "electron-main", runId, codeSha256 };
222988
+ }
222989
+ emitLive("sandbox_started", {
222990
+ executionHost: "electron-main",
222991
+ runnerKind: "dev-local",
222992
+ networkPolicy,
222993
+ label: spec.label ?? null,
222994
+ codeSha256,
222995
+ language: executionMode === "command" ? "shell" : language
222996
+ });
222997
+ const validationReasons = executionMode === "script" ? validateScript(code, language) : [];
222998
+ if (validationReasons.length > 0) {
222999
+ emitLive("sandbox_failed", {
223000
+ stopReason: "validation_rejected",
223001
+ message: "The model-written sandbox code was rejected by validation.",
223002
+ validationReasons,
223003
+ codeSha256
223004
+ });
223005
+ return {
223006
+ ok: false,
223007
+ kind: "failed",
223008
+ message: "The model-written sandbox code was rejected by validation.",
223009
+ runnerKind: "dev-local",
223010
+ executionHost: "electron-main",
223011
+ runId,
223012
+ error: validationReasons.join("; "),
223013
+ validationReasons,
223014
+ codeSha256
223015
+ };
223016
+ }
223017
+ let resolved = [];
223018
+ if (Array.isArray(spec.sources) && spec.sources.length > 0) {
223019
+ try {
223020
+ emitLive("inputs_preparing", { sourceCount: spec.sources.length });
223021
+ resolved = await resolveApprovedInputs(spec.sources, deps);
223022
+ } catch (err) {
223023
+ const message = err instanceof Error ? err.message : String(err);
223024
+ emitLive("sandbox_failed", { message, stopReason: "input_resolution_failed" });
223025
+ return { ok: false, kind: "blocked", message, executionHost: "electron-main", runId, error: message, codeSha256 };
223026
+ }
223027
+ }
223028
+ const workspaceKey = safeWorkspaceKey(spec.workspaceKey) ?? runId;
223029
+ const workspaceRoot = await ensureCodeWorkspace(workspaceKey);
223030
+ const scriptName = language === "python" ? `run-${runId}.py` : `run-${runId}.js`;
223031
+ const command = executionMode === "command" ? commandText : language === "python" ? buildPythonCommand(scriptName) : `"$PERCH_ELECTRON_EXEC" "${scriptName}"`;
223032
+ emitLive("script_planned", {
223033
+ source: "model",
223034
+ fallbackReason: null,
223035
+ script: redactUserVisibleText(executableText),
223036
+ publicPlan: spec.label ?? (executionMode === "command" ? "Sandbox shell command." : "Model-written sandbox code."),
223037
+ plannerContent: null,
223038
+ provider: null,
223039
+ codeSha256,
223040
+ language: executionMode === "command" ? "shell" : language
223041
+ });
223042
+ emitLive("command_started", { command: redactUserVisibleText(command), language: executionMode === "command" ? "shell" : language, codeSha256 });
223043
+ const runResult = await runCodeSandbox({
223044
+ runId,
223045
+ command,
223046
+ workspaceRoot,
223047
+ inputs: resolved,
223048
+ timeoutMs: Math.min(spec.timeoutMs ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2),
223049
+ onEvent: emitLive,
223050
+ networkPolicy,
223051
+ scriptFile: executionMode === "script" ? { name: scriptName, content: code } : null
223052
+ });
223053
+ const outputBundle = buildOutputBundle(runResult);
223054
+ const hasReport = runResult.structuredOutput?.reportJson != null;
223055
+ const completedWithoutReport = runResult.status === "completed" && !hasReport;
223056
+ const eventKind = runResult.status === "completed" ? "completed" : "failed";
223057
+ const stopReason = hasReport ? "completed" : completedWithoutReport ? "completed_unstructured" : runResult.status === "timed_out" ? "run_timed_out" : "run_failed";
223058
+ emitLive(eventKind === "completed" ? "sandbox_completed" : "sandbox_failed", {
223059
+ stopReason,
223060
+ runnerKind: "dev-local",
223061
+ executionHost: "electron-main",
223062
+ codeSha256,
223063
+ outputSummary: {
223064
+ files: outputBundle?.files.length ?? 0,
223065
+ tables: outputBundle?.tables.length ?? 0,
223066
+ charts: outputBundle?.charts.length ?? 0,
223067
+ logs: outputBundle?.logs.length ?? 0,
223068
+ reportJsonPresent: hasReport
223069
+ }
223070
+ });
223071
+ if (runResult.status !== "completed") {
223072
+ return {
223073
+ ok: false,
223074
+ kind: "failed",
223075
+ message: "Model-written sandbox code failed.",
223076
+ runnerKind: "dev-local",
223077
+ executionHost: "electron-main",
223078
+ runId,
223079
+ error: runResult.stderr || stopReason,
223080
+ codeSha256,
223081
+ status: runResult.status,
223082
+ exitCode: runResult.exitCode,
223083
+ durationMs: Date.now() - startedAt,
223084
+ stdout: runResult.stdout,
223085
+ stderr: runResult.stderr,
223086
+ stdoutTruncated: runResult.stdoutTruncated,
223087
+ stderrTruncated: runResult.stderrTruncated,
223088
+ producedFiles: runResult.producedFiles,
223089
+ inputManifest: runResult.inputManifest,
223090
+ runtimeInfo: runResult.runtimeInfo,
223091
+ structuredOutput: runResult.structuredOutput,
223092
+ workspacePath: runResult.workspacePath,
223093
+ language,
223094
+ reportJsonPresent: hasReport,
223095
+ warnings: runResult.warnings
223096
+ };
223097
+ }
223098
+ const kind = "completed";
223099
+ return {
223100
+ ok: true,
223101
+ kind,
223102
+ message: hasReport ? "Sandbox execution completed and emitted output/report.json." : "Sandbox execution completed; stdout/stderr and produced files were captured.",
223103
+ runnerKind: "dev-local",
223104
+ executionHost: "electron-main",
223105
+ runId,
223106
+ status: runResult.status,
223107
+ exitCode: runResult.exitCode,
223108
+ durationMs: Date.now() - startedAt,
223109
+ stdout: runResult.stdout,
223110
+ stderr: runResult.stderr,
223111
+ stdoutTruncated: runResult.stdoutTruncated,
223112
+ stderrTruncated: runResult.stderrTruncated,
223113
+ producedFiles: runResult.producedFiles,
223114
+ inputManifest: runResult.inputManifest,
223115
+ runtimeInfo: runResult.runtimeInfo,
223116
+ structuredOutput: runResult.structuredOutput,
223117
+ workspacePath: runResult.workspacePath,
223118
+ codeSha256,
223119
+ language: executionMode === "command" ? "shell" : language,
223120
+ reportJsonPresent: hasReport,
223121
+ warnings: runResult.warnings
223122
+ };
223123
+ }
223124
+ async function resolveApprovedInputs(sources, deps) {
223125
+ const out = [];
223126
+ const seen = /* @__PURE__ */ new Set();
223127
+ for (const source of sources.slice(0, MAX_SOURCES)) {
223128
+ if (!source.localSourceId || seen.has(source.localSourceId)) continue;
223129
+ seen.add(source.localSourceId);
223130
+ const resolved = resolveSandboxInputRef(source.localSourceId, deps);
223131
+ if (!resolved) continue;
223132
+ const { absPath, relPath } = resolved;
223133
+ const fileName = basename(absPath);
223134
+ if (deps.shouldIgnore(fileName)) continue;
223135
+ const s = await stat(absPath);
223136
+ if (!s.isFile()) continue;
223137
+ if (s.size > MAX_SOURCE_BYTES) continue;
223138
+ out.push({
223139
+ localSourceId: source.localSourceId,
223140
+ fileName: safeRelativeName(source.fileName || fileName),
223141
+ fileType: source.fileType || guessFileTypeFromExt(extname(fileName).toLowerCase()),
223142
+ sizeBytes: s.size,
223143
+ description: source.description,
223144
+ relativePath: safeRelativeName(relPath),
223145
+ sourcePath: absPath
223146
+ });
223147
+ }
223148
+ return out;
223149
+ }
223150
+ function resolveSandboxInputRef(inputRef, deps) {
223151
+ const sep = inputRef.indexOf("::");
223152
+ if (sep >= 0) {
223153
+ const rootId = inputRef.slice(0, sep);
223154
+ const relPath2 = inputRef.slice(sep + 2);
223155
+ const root3 = deps.getApprovedRoot(rootId);
223156
+ if (!root3) return null;
223157
+ if (!deps.isPathSafe(root3.path, relPath2)) return null;
223158
+ const absPath2 = resolve4(root3.path, relPath2);
223159
+ if (!deps.isInsideApprovedRoot(absPath2)) return null;
223160
+ return { absPath: absPath2, relPath: relPath2 };
223161
+ }
223162
+ const absPath = resolve4(inputRef);
223163
+ const root2 = deps.isInsideApprovedRoot(absPath);
223164
+ if (!root2) return null;
223165
+ const relPath = relative(root2.path, absPath);
223166
+ if (!deps.isPathSafe(root2.path, relPath)) return null;
223167
+ return { absPath, relPath };
223168
+ }
223169
+ function validateScript(script, language = "node") {
223170
+ if (language === "python") return validatePythonScript(script);
223171
+ const reasons = [];
223172
+ const blocked3 = [
223173
+ "child_process",
223174
+ "exec(",
223175
+ "spawn(",
223176
+ "process.env",
223177
+ "require('http')",
223178
+ 'require("http")',
223179
+ "require('https')",
223180
+ 'require("https")',
223181
+ "fetch(",
223182
+ "eval(",
223183
+ "Function("
223184
+ ];
223185
+ for (const token of blocked3) {
223186
+ if (script.includes(token)) reasons.push(`blocked token: ${token}`);
223187
+ }
223188
+ return reasons;
223189
+ }
223190
+ function validatePythonScript(script) {
223191
+ const reasons = [];
223192
+ const blocked3 = [
223193
+ "subprocess",
223194
+ "os.system(",
223195
+ "os.popen(",
223196
+ "os.exec",
223197
+ "shutil.rmtree",
223198
+ "__import__(",
223199
+ "importlib",
223200
+ "ctypes",
223201
+ "requests.get",
223202
+ "requests.post",
223203
+ "urllib.request",
223204
+ "http.client",
223205
+ "socket.",
223206
+ "open('/etc",
223207
+ "open('/proc",
223208
+ "open('/sys"
223209
+ ];
223210
+ for (const token of blocked3) {
223211
+ if (script.includes(token)) reasons.push(`blocked token: ${token}`);
223212
+ }
223213
+ return reasons;
223214
+ }
223215
+ function buildPythonCommand(scriptPath) {
223216
+ return `python3 "${scriptPath}"`;
223217
+ }
223218
+ async function ensureCodeWorkspace(workspaceKey) {
223219
+ const existing = codeWorkspaces.get(workspaceKey);
223220
+ if (existing) {
223221
+ try {
223222
+ const s = await stat(existing);
223223
+ if (s.isDirectory()) return existing;
223224
+ } catch {
223225
+ codeWorkspaces.delete(workspaceKey);
223226
+ }
223227
+ }
223228
+ const workspaceRoot = await mkdtemp(join(tmpdir(), `perch-code-${workspaceKey.slice(0, 16)}-`));
223229
+ await mkdir(join(workspaceRoot, "input"), { recursive: true });
223230
+ await mkdir(join(workspaceRoot, "output"), { recursive: true });
223231
+ await writeFile2(join(workspaceRoot, "perch_helpers.py"), PYTHON_HELPERS_SOURCE, "utf-8");
223232
+ codeWorkspaces.set(workspaceKey, workspaceRoot);
223233
+ return workspaceRoot;
223234
+ }
223235
+ async function runCodeSandbox(opts) {
223236
+ const warnings = [];
223237
+ const copiedInputFiles = [];
223238
+ const skippedInputFiles = [];
223239
+ const inputManifestFiles = [];
223240
+ const skippedManifestEntries = [];
223241
+ const runtimeInfo = buildSandboxRuntimeInfo(opts.networkPolicy);
223242
+ const inputDir = join(opts.workspaceRoot, "input");
223243
+ const outputDir = join(opts.workspaceRoot, "output");
223244
+ await rm(inputDir, { recursive: true, force: true });
223245
+ await rm(outputDir, { recursive: true, force: true });
223246
+ await mkdir(inputDir, { recursive: true });
223247
+ await mkdir(outputDir, { recursive: true });
223248
+ if (opts.scriptFile) {
223249
+ await writeFile2(join(opts.workspaceRoot, opts.scriptFile.name), opts.scriptFile.content, "utf-8");
223250
+ }
223251
+ await writeFile2(join(opts.workspaceRoot, "perch_helpers.py"), PYTHON_HELPERS_SOURCE, "utf-8");
223252
+ for (const input of opts.inputs) {
223253
+ const safeRel = safeRelativeName(input.relativePath || input.fileName);
223254
+ if (!safeRel) {
223255
+ skippedInputFiles.push(input.fileName);
223256
+ skippedManifestEntries.push({
223257
+ sourcePath: input.sourcePath,
223258
+ localSourceId: input.localSourceId,
223259
+ fileName: input.fileName,
223260
+ sizeBytes: input.sizeBytes,
223261
+ fileType: input.fileType,
223262
+ reason: "invalid_sandbox_relative_path"
223263
+ });
223264
+ continue;
223265
+ }
223266
+ try {
223267
+ const dest = join(inputDir, safeRel);
223268
+ await mkdir(dirname(dest), { recursive: true });
223269
+ await copyFile(input.sourcePath, dest);
223270
+ copiedInputFiles.push(safeRel);
223271
+ inputManifestFiles.push({
223272
+ sandboxPath: `input/${safeRel}`,
223273
+ sourcePath: input.sourcePath,
223274
+ localSourceId: input.localSourceId,
223275
+ fileName: input.fileName || basename(safeRel),
223276
+ sizeBytes: input.sizeBytes,
223277
+ fileType: input.fileType || null
223278
+ });
223279
+ opts.onEvent?.("input_copied", {
223280
+ relativePath: safeRel,
223281
+ fileName: input.fileName,
223282
+ fileType: input.fileType,
223283
+ sizeBytes: input.sizeBytes
223284
+ });
223285
+ } catch (err) {
223286
+ const msg = err instanceof Error ? err.message : String(err);
223287
+ skippedInputFiles.push(safeRel);
223288
+ skippedManifestEntries.push({
223289
+ sandboxPath: `input/${safeRel}`,
223290
+ sourcePath: input.sourcePath,
223291
+ localSourceId: input.localSourceId,
223292
+ fileName: input.fileName || basename(safeRel),
223293
+ sizeBytes: input.sizeBytes,
223294
+ fileType: input.fileType,
223295
+ reason: msg
223296
+ });
223297
+ warnings.push(`Failed to copy input ${safeRel}: ${msg}`);
223298
+ }
223299
+ }
223300
+ opts.onEvent?.("inputs_ready", {
223301
+ requestedInputFileCount: opts.inputs.length,
223302
+ copiedInputFiles,
223303
+ skippedInputFiles,
223304
+ workspacePath: redactUserVisibleText(opts.workspaceRoot),
223305
+ inputManifestPath: "input_manifest.json",
223306
+ runtimeInfo
223307
+ });
223308
+ const inputManifest = {
223309
+ root: "input",
223310
+ files: inputManifestFiles,
223311
+ skipped: skippedManifestEntries
223312
+ };
223313
+ await writeSandboxManifestFiles({
223314
+ workspaceRoot: opts.workspaceRoot,
223315
+ inputManifest,
223316
+ runtimeInfo
223317
+ });
223318
+ const execResult = await executeCommand(
223319
+ opts.command,
223320
+ opts.workspaceRoot,
223321
+ opts.timeoutMs,
223322
+ opts.networkPolicy,
223323
+ opts.onEvent
223324
+ );
223325
+ const producedFiles = await collectOutputFiles(outputDir, warnings, opts.onEvent);
223326
+ opts.onEvent?.("outputs_collected", { producedFiles });
223327
+ const structuredOutput = await readStructuredOutput(outputDir, execResult.stdout);
223328
+ if (structuredOutput?.reportJson) opts.onEvent?.("report_detected", { source: "report.json" });
223329
+ return {
223330
+ runId: opts.runId,
223331
+ status: execResult.timedOut ? "timed_out" : execResult.exitCode === 0 ? "completed" : "failed",
223332
+ exitCode: execResult.exitCode,
223333
+ stdout: execResult.stdout,
223334
+ stderr: execResult.stderr,
223335
+ stdoutTruncated: execResult.stdoutTruncated,
223336
+ stderrTruncated: execResult.stderrTruncated,
223337
+ producedFiles,
223338
+ inputManifest,
223339
+ runtimeInfo,
223340
+ structuredOutput,
223341
+ warnings,
223342
+ durationMs: 0,
223343
+ workspacePath: opts.workspaceRoot,
223344
+ cleanedUp: false,
223345
+ executionEvidence: {
223346
+ requestedInputFileCount: opts.inputs.length,
223347
+ copiedInputFiles,
223348
+ skippedInputFiles,
223349
+ command: opts.command
223350
+ }
223351
+ };
223352
+ }
223353
+ async function executeCommand(command, cwd2, timeoutMs, networkPolicy, onEvent) {
223354
+ const launch = await prepareSandboxLaunch(command, cwd2, networkPolicy);
223355
+ if ("error" in launch) {
223356
+ return {
223357
+ stdout: "",
223358
+ stderr: launch.error,
223359
+ exitCode: 126,
223360
+ timedOut: false,
223361
+ stdoutTruncated: false,
223362
+ stderrTruncated: false
223363
+ };
223364
+ }
223365
+ return new Promise((resolvePromise) => {
223366
+ let stdout = "";
223367
+ let stderr = "";
223368
+ let stdoutBytes = 0;
223369
+ let stderrBytes = 0;
223370
+ let stdoutTruncated = false;
223371
+ let stderrTruncated = false;
223372
+ let timedOut = false;
223373
+ const childEnv = {
223374
+ NODE_ENV: "sandbox",
223375
+ PERCH_SANDBOX_NETWORK: networkPolicy,
223376
+ LANG: "en_US.UTF-8",
223377
+ HOME: cwd2,
223378
+ TMPDIR: join(cwd2, "tmp"),
223379
+ PYTHONDONTWRITEBYTECODE: "1",
223380
+ PATH: process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin",
223381
+ ELECTRON_RUN_AS_NODE: "1",
223382
+ PERCH_ELECTRON_EXEC: process.execPath,
223383
+ ...launch.env
223384
+ };
223385
+ const child = spawn(launch.command, launch.args, {
223386
+ cwd: cwd2,
223387
+ env: childEnv,
223388
+ stdio: ["ignore", "pipe", "pipe"]
223389
+ });
223390
+ const timer = setTimeout(() => {
223391
+ timedOut = true;
223392
+ child.kill("SIGKILL");
223393
+ }, timeoutMs);
223394
+ child.stdout?.on("data", (data) => {
223395
+ stdoutBytes += data.length;
223396
+ const chunk2 = data.toString("utf8");
223397
+ onEvent?.("stdout", { chunk: redactUserVisibleText(chunk2) });
223398
+ if (stdoutBytes <= MAX_STDOUT_BYTES) stdout += chunk2;
223399
+ else stdoutTruncated = true;
223400
+ });
223401
+ child.stderr?.on("data", (data) => {
223402
+ stderrBytes += data.length;
223403
+ const chunk2 = data.toString("utf8");
223404
+ onEvent?.("stderr", { chunk: redactUserVisibleText(chunk2) });
223405
+ if (stderrBytes <= MAX_STDERR_BYTES) stderr += chunk2;
223406
+ else stderrTruncated = true;
223407
+ });
223408
+ child.on("close", (code) => {
223409
+ clearTimeout(timer);
223410
+ resolvePromise({ stdout, stderr, exitCode: code, timedOut, stdoutTruncated, stderrTruncated });
223411
+ });
223412
+ child.on("error", (err) => {
223413
+ clearTimeout(timer);
223414
+ resolvePromise({ stdout, stderr: err.message, exitCode: 1, timedOut: false, stdoutTruncated, stderrTruncated });
223415
+ });
223416
+ });
223417
+ }
223418
+ async function prepareSandboxLaunch(command, cwd2, networkPolicy) {
223419
+ await mkdir(join(cwd2, "tmp"), { recursive: true });
223420
+ if (networkPolicy !== "disabled") {
223421
+ return { command: "bash", args: ["-c", command] };
223422
+ }
223423
+ if (platform() !== "darwin") {
223424
+ return {
223425
+ error: "Sandbox execution requires macOS sandbox-exec for disabled-network local code runs in this build."
223426
+ };
223427
+ }
223428
+ const sandboxExec = "/usr/bin/sandbox-exec";
223429
+ try {
223430
+ const s = await stat(sandboxExec);
223431
+ if (!s.isFile()) throw new Error("not a file");
223432
+ } catch {
223433
+ return {
223434
+ error: "macOS sandbox-exec is unavailable; refusing to run model-written code without OS-level isolation."
223435
+ };
223436
+ }
223437
+ const profilePath = join(cwd2, ".perch-sandbox.sb");
223438
+ await writeFile2(profilePath, buildSeatbeltProfile(cwd2), "utf-8");
223439
+ return {
223440
+ command: sandboxExec,
223441
+ args: ["-f", profilePath, "/bin/bash", "-c", command],
223442
+ env: { PERCH_SANDBOX_PROFILE: profilePath }
223443
+ };
223444
+ }
223445
+ function buildSeatbeltProfile(workspaceRoot) {
223446
+ const home = homedir();
223447
+ const deniedUserDataRoots = Array.from(/* @__PURE__ */ new Set([
223448
+ home,
223449
+ "/Users/Shared",
223450
+ "/Volumes"
223451
+ ])).map((path14) => `(subpath ${seatbeltString(path14)})`).join("\n ");
223452
+ return `
223453
+ (version 1)
223454
+ (allow default)
223455
+ (deny network*)
223456
+ (deny file-read*
223457
+ ${deniedUserDataRoots})
223458
+ (deny file-write*
223459
+ ${deniedUserDataRoots})
223460
+ (allow file-read*
223461
+ (subpath ${seatbeltString(workspaceRoot)})
223462
+ (subpath ${seatbeltString(dirname(process.execPath))}))
223463
+ (allow file-write*
223464
+ (subpath ${seatbeltString(workspaceRoot)}))
223465
+ `.trim();
223466
+ }
223467
+ function seatbeltString(value) {
223468
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
223469
+ }
223470
+ async function collectOutputFiles(outputDir, warnings, onEvent) {
223471
+ const files = [];
223472
+ let totalBytes = 0;
223473
+ async function walk2(dir, base) {
223474
+ let entries;
223475
+ try {
223476
+ entries = await readdir(dir, { withFileTypes: true });
223477
+ } catch {
223478
+ return;
223479
+ }
223480
+ for (const entry of entries) {
223481
+ if (files.length >= MAX_OUTPUT_FILES) return;
223482
+ const rel = base ? `${base}/${entry.name}` : entry.name;
223483
+ const full = join(dir, entry.name);
223484
+ if (entry.isDirectory()) {
223485
+ await walk2(full, rel);
223486
+ continue;
223487
+ }
223488
+ const s = await stat(full);
223489
+ totalBytes += s.size;
223490
+ if (totalBytes > MAX_OUTPUT_BYTES) {
223491
+ warnings.push("Produced file byte limit exceeded");
223492
+ return;
223493
+ }
223494
+ const file = { relativePath: rel, sizeBytes: s.size, mimeType: guessMimeTypeFromName(entry.name) };
223495
+ files.push(file);
223496
+ onEvent?.("output_detected", file);
223497
+ }
223498
+ }
223499
+ await walk2(outputDir, "");
223500
+ return files;
223501
+ }
223502
+ async function readStructuredOutput(outputDir, stdout) {
223503
+ let parsedStdout = null;
223504
+ const line = stdout.trim().split(/\r?\n/).reverse().find((candidate) => candidate.trim().startsWith("{"));
223505
+ if (line) {
223506
+ try {
223507
+ parsedStdout = JSON.parse(line);
223508
+ } catch {
223509
+ parsedStdout = null;
223510
+ }
223511
+ }
223512
+ let reportJson = parsedStdout?.reportJson;
223513
+ if (!reportJson) {
223514
+ try {
223515
+ reportJson = JSON.parse(await readFile3(join(outputDir, "report.json"), "utf8"));
223516
+ } catch {
223517
+ reportJson = void 0;
223518
+ }
223519
+ }
223520
+ const tables = Array.isArray(parsedStdout?.tables) ? parsedStdout.tables : [];
223521
+ const charts = Array.isArray(parsedStdout?.charts) ? parsedStdout.charts : [];
223522
+ const logs = Array.isArray(parsedStdout?.logs) ? parsedStdout.logs.filter((x) => typeof x === "string") : [];
223523
+ if (!reportJson && tables.length === 0 && charts.length === 0 && logs.length === 0) return null;
223524
+ return { reportJson, tables, charts, logs };
223525
+ }
223526
+ function buildOutputBundle(run) {
223527
+ const structured = run.structuredOutput;
223528
+ if (!structured && run.producedFiles.length === 0) return null;
223529
+ return {
223530
+ status: run.status,
223531
+ files: run.producedFiles.map((file) => ({
223532
+ fileName: safeRelativeName(file.relativePath),
223533
+ sizeBytes: file.sizeBytes,
223534
+ mimeType: file.mimeType
223535
+ })),
223536
+ tables: structured?.tables ?? [],
223537
+ charts: structured?.charts ?? [],
223538
+ logs: structured?.logs ?? [],
223539
+ reportJson: structured?.reportJson ?? null
223540
+ };
223541
+ }
223542
+ function makeRunId2(prefix) {
223543
+ try {
223544
+ return `${prefix}-${randomUUID().slice(0, 12)}`;
223545
+ } catch {
223546
+ return `${prefix}-${Date.now().toString(36)}`;
223547
+ }
223548
+ }
223549
+ function safeRunId(value) {
223550
+ if (!value) return null;
223551
+ return /^[a-zA-Z0-9_-]{1,80}$/.test(value) ? value : null;
223552
+ }
223553
+ function safeWorkspaceKey(value) {
223554
+ if (!value) return null;
223555
+ const cleaned = value.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 80);
223556
+ return cleaned || null;
223557
+ }
223558
+ function safeRelativeName(value) {
223559
+ return value.split(/[\\/]+/).filter((part) => part && part !== "." && part !== "..").join("/") || "unnamed";
223560
+ }
223561
+ function redactUserVisibleText(text) {
223562
+ if (!text) return "";
223563
+ return text.replace(/\/Users\/[^/\s"'`]+/g, "[home]").replace(/\/home\/[^/\s"'`]+/g, "[home]").replace(/\/private\/var\/folders\/[^\s"'`]+/g, "[temp]").replace(/\/var\/folders\/[^\s"'`]+/g, "[temp]").replace(/\/tmp\/perch-sandbox-[^\s"'`]+/g, "[sandbox]").replace(/\/var\/tmp\/perch-sandbox-[^\s"'`]+/g, "[sandbox]");
223564
+ }
223565
+ function guessFileTypeFromExt(ext) {
223566
+ if ([".xlsx", ".xls"].includes(ext)) return "spreadsheet";
223567
+ if (ext === ".csv" || ext === ".tsv") return "csv";
223568
+ if (ext === ".pdf") return "pdf";
223569
+ if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) return "image";
223570
+ if ([".doc", ".docx", ".txt", ".md", ".rtf"].includes(ext)) return "document";
223571
+ return "unknown";
223572
+ }
223573
+ function guessMimeTypeFromName(fileName) {
223574
+ const ext = extname(fileName).toLowerCase();
223575
+ const map2 = {
223576
+ ".json": "application/json",
223577
+ ".csv": "text/csv",
223578
+ ".tsv": "text/tab-separated-values",
223579
+ ".txt": "text/plain",
223580
+ ".md": "text/markdown",
223581
+ ".html": "text/html"
223582
+ };
223583
+ return map2[ext] ?? "application/octet-stream";
223584
+ }
223585
+ var MAX_SOURCES, MAX_SOURCE_BYTES, MAX_STDOUT_BYTES, MAX_STDERR_BYTES, MAX_OUTPUT_FILES, MAX_OUTPUT_BYTES, DEFAULT_TIMEOUT_MS2, codeWorkspaces, PYTHON_HELPERS_SOURCE;
223586
+ var init_localSandboxHost = __esm({
223587
+ "electron/localSandboxHost.ts"() {
223588
+ MAX_SOURCES = 20;
223589
+ MAX_SOURCE_BYTES = 20 * 1024 * 1024;
223590
+ MAX_STDOUT_BYTES = 512 * 1024;
223591
+ MAX_STDERR_BYTES = 128 * 1024;
223592
+ MAX_OUTPUT_FILES = 50;
223593
+ MAX_OUTPUT_BYTES = 50 * 1024 * 1024;
223594
+ DEFAULT_TIMEOUT_MS2 = 3e4;
223595
+ codeWorkspaces = /* @__PURE__ */ new Map();
223596
+ PYTHON_HELPERS_SOURCE = `
223597
+ import csv
223598
+ import json
223599
+ import re
223600
+ import sys
223601
+ import zlib
223602
+ from pathlib import Path
223603
+ from collections import Counter
223604
+
223605
+ def sandbox_runtime_info(path="sandbox_runtime.json"):
223606
+ p = Path(path)
223607
+ if p.exists():
223608
+ return json.loads(p.read_text(encoding="utf-8"))
223609
+ return {
223610
+ "networkPolicy": "unknown",
223611
+ "packageInstalls": "unknown",
223612
+ "pythonCommand": sys.executable,
223613
+ "helperModule": "perch_helpers",
223614
+ }
223615
+
223616
+ def input_manifest(path="input_manifest.json"):
223617
+ p = Path(path)
223618
+ if not p.exists():
223619
+ return {"root": "input", "files": [], "skipped": []}
223620
+ return json.loads(p.read_text(encoding="utf-8"))
223621
+
223622
+ def input_files(root="input"):
223623
+ base = Path(root)
223624
+ if not base.exists():
223625
+ return []
223626
+ return [str(p) for p in base.rglob("*") if p.is_file()]
223627
+
223628
+ def load_tabular(path):
223629
+ p = Path(path)
223630
+ suffix = p.suffix.lower()
223631
+ if suffix in {".csv", ".tsv"}:
223632
+ try:
223633
+ import pandas as pd
223634
+ return pd.read_csv(p, sep="\\t" if suffix == ".tsv" else ",")
223635
+ except Exception:
223636
+ with p.open(newline="", encoding="utf-8-sig") as fh:
223637
+ return list(csv.DictReader(fh, delimiter="\\t" if suffix == ".tsv" else ","))
223638
+ if suffix in {".xlsx", ".xls"}:
223639
+ try:
223640
+ import pandas as pd
223641
+ return pd.read_excel(p)
223642
+ except Exception as exc:
223643
+ raise RuntimeError("Excel loading requires pandas/openpyxl in the local Python environment") from exc
223644
+ raise ValueError(f"Unsupported tabular file type: {p.suffix}")
223645
+
223646
+ def extract_pdf_tables(path):
223647
+ try:
223648
+ import pdfplumber
223649
+ except Exception as exc:
223650
+ raise RuntimeError("PDF table extraction requires pdfplumber in the local Python environment") from exc
223651
+ rows = []
223652
+ with pdfplumber.open(path) as pdf:
223653
+ for page_index, page in enumerate(pdf.pages, start=1):
223654
+ for table in page.extract_tables() or []:
223655
+ rows.append({"page": page_index, "table": table})
223656
+ return rows
223657
+
223658
+ def _pdf_literal_unescape(value):
223659
+ out = []
223660
+ i = 0
223661
+ while i < len(value):
223662
+ ch = value[i]
223663
+ if ch != "\\\\":
223664
+ out.append(ch)
223665
+ i += 1
223666
+ continue
223667
+ i += 1
223668
+ if i >= len(value):
223669
+ break
223670
+ esc = value[i]
223671
+ i += 1
223672
+ mapping = {"n": "\\n", "r": "\\r", "t": "\\t", "b": "\\b", "f": "\\f", "\\\\": "\\\\", "(": "(", ")": ")"}
223673
+ if esc in mapping:
223674
+ out.append(mapping[esc])
223675
+ elif esc in "01234567":
223676
+ octal = esc
223677
+ for _ in range(2):
223678
+ if i < len(value) and value[i] in "01234567":
223679
+ octal += value[i]
223680
+ i += 1
223681
+ else:
223682
+ break
223683
+ try:
223684
+ out.append(chr(int(octal, 8)))
223685
+ except Exception:
223686
+ pass
223687
+ else:
223688
+ out.append(esc)
223689
+ return "".join(out)
223690
+
223691
+ def _extract_pdf_text_raw(path):
223692
+ data = Path(path).read_bytes()
223693
+ chunks = []
223694
+ for match in re.finditer(rb"stream\\r?\\n(.*?)\\r?\\nendstream", data, re.S):
223695
+ raw = match.group(1)
223696
+ candidates = [raw]
223697
+ try:
223698
+ candidates.insert(0, zlib.decompress(raw))
223699
+ except Exception:
223700
+ pass
223701
+ for candidate in candidates:
223702
+ try:
223703
+ decoded = candidate.decode("latin-1", errors="ignore")
223704
+ except Exception:
223705
+ continue
223706
+ literals = [_pdf_literal_unescape(x) for x in re.findall(r"\\((.*?)\\)", decoded, re.S)]
223707
+ if literals:
223708
+ chunks.extend(literals)
223709
+ break
223710
+ return " ".join(part.strip() for part in chunks if part and part.strip())
223711
+
223712
+ def extract_pdf_text(path):
223713
+ p = Path(path)
223714
+ if not p.exists():
223715
+ raise FileNotFoundError(str(p))
223716
+ errors = []
223717
+ for module_name in ("pypdf", "PyPDF2"):
223718
+ try:
223719
+ module = __import__(module_name)
223720
+ reader_cls = getattr(module, "PdfReader")
223721
+ reader = reader_cls(str(p))
223722
+ text = "\\n".join((page.extract_text() or "") for page in reader.pages)
223723
+ if text.strip():
223724
+ return text
223725
+ except Exception as exc:
223726
+ errors.append(f"{module_name}: {exc}")
223727
+ try:
223728
+ from pdfminer.high_level import extract_text
223729
+ text = extract_text(str(p))
223730
+ if text.strip():
223731
+ return text
223732
+ except Exception as exc:
223733
+ errors.append(f"pdfminer: {exc}")
223734
+ raw = _extract_pdf_text_raw(p)
223735
+ if raw.strip():
223736
+ return raw
223737
+ raise RuntimeError("No extractable PDF text found; tried pypdf, PyPDF2, pdfminer, and raw PDF streams. " + "; ".join(errors))
223738
+
223739
+ def extract_invoice_fields(path):
223740
+ text = extract_pdf_text(path)
223741
+ def find(pattern):
223742
+ m = re.search(pattern, text, re.I)
223743
+ return m.group(1).strip() if m else None
223744
+ amount = find(r"Amount\\s+Due:\\s*(?:USD\\s*)?([0-9,]+(?:\\.\\d+)?)")
223745
+ return {
223746
+ "file": str(path),
223747
+ "text": text,
223748
+ "vendor_name": text.split(" EIN")[0].strip() if " EIN" in text else None,
223749
+ "ein": find(r"EIN:\\s*([0-9-]+)"),
223750
+ "invoice_number": find(r"Invoice\\s+Number:\\s*(INV-[0-9]+)"),
223751
+ "invoice_date": find(r"Date:\\s*([0-9]{4}-[0-9]{2}-[0-9]{2})"),
223752
+ "po_number": find(r"Purchase\\s+Order:\\s*(PO-[0-9]+)"),
223753
+ "amount_due": float(amount.replace(",", "")) if amount else None,
223754
+ }
223755
+
223756
+ def find_duplicates(rows, keys):
223757
+ if hasattr(rows, "to_dict"):
223758
+ records = rows.to_dict("records")
223759
+ else:
223760
+ records = list(rows)
223761
+ counts = Counter(tuple(str(row.get(k, "")).strip().lower() for k in keys) for row in records)
223762
+ return [row for row in records if counts[tuple(str(row.get(k, "")).strip().lower() for k in keys)] > 1]
223763
+
223764
+ def write_report(report, path="output/report.json"):
223765
+ out = Path(path)
223766
+ out.parent.mkdir(parents=True, exist_ok=True)
223767
+ out.write_text(json.dumps(report, indent=2, default=str), encoding="utf-8")
223768
+ return str(out)
223769
+ `.trim();
223770
+ }
223771
+ });
223772
+
223773
+ // features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts
223774
+ import { spawn as spawn2 } from "node:child_process";
222676
223775
  import fs9 from "node:fs";
222677
223776
  import fsp from "node:fs/promises";
222678
223777
  import os from "node:os";
@@ -222692,6 +223791,12 @@ function installCliNodeLocalBridge(input) {
222692
223791
  function createCliNodeLocalBridge(input) {
222693
223792
  const workspaceRoot = path10.resolve(input.workspaceRoot);
222694
223793
  const now13 = () => (/* @__PURE__ */ new Date()).toISOString();
223794
+ const sandboxHandlers = /* @__PURE__ */ new Map();
223795
+ const emitSandboxEvent = (event) => {
223796
+ const handlers = sandboxHandlers.get(event.runId);
223797
+ if (!handlers) return;
223798
+ for (const handler of handlers) handler(event);
223799
+ };
222695
223800
  const terminalSession = () => ({
222696
223801
  sessionId: "cli-terminal",
222697
223802
  title: "Perch CLI",
@@ -222726,17 +223831,9 @@ function createCliNodeLocalBridge(input) {
222726
223831
  }),
222727
223832
  checkFsAccess: async (request) => {
222728
223833
  const absolutePath = path10.resolve(expandHome4(request.absolutePath));
222729
- if (request.fsAccessOverride === "allow_once" || isInside(workspaceRoot, absolutePath)) {
222730
- return {
222731
- decision: "allow",
222732
- reason: "Allowed by Perch CLI local workspace.",
222733
- absolutePath,
222734
- approvalPath: absolutePath
222735
- };
222736
- }
222737
223834
  return {
222738
- decision: "block",
222739
- reason: `Path is outside the Perch CLI workspace: ${workspaceRoot}`,
223835
+ decision: "allow",
223836
+ reason: "Allowed by Perch CLI local filesystem access.",
222740
223837
  absolutePath,
222741
223838
  approvalPath: absolutePath
222742
223839
  };
@@ -222762,7 +223859,7 @@ function createCliNodeLocalBridge(input) {
222762
223859
  executionHost: "electron_desktop"
222763
223860
  }),
222764
223861
  runLocalBash: async (request) => runShellCommand({
222765
- workspaceRoot,
223862
+ workspaceRoot: resolveRequestRoot(workspaceRoot, request.workspaceRoot),
222766
223863
  command: request.command,
222767
223864
  cwd: request.cwd,
222768
223865
  timeoutMs: request.timeoutMs
@@ -222798,45 +223895,45 @@ function createCliNodeLocalBridge(input) {
222798
223895
  offBashTerminalEvent: () => {
222799
223896
  },
222800
223897
  readWorkspaceFile: async (request) => {
222801
- const target = resolveReadPath(workspaceRoot, request.relativePath);
222802
- if (!isInside(workspaceRoot, target)) return { ok: false, error: `Path is outside the CLI workspace: ${target}` };
222803
- const stat = await safeStat(target);
222804
- if (!stat?.isFile()) return { ok: false, error: `File does not exist: ${target}` };
222805
- if (stat.size > MAX_READ_BYTES && request.encoding !== "base64") {
222806
- return { ok: false, error: `File is too large for direct text read (${stat.size} bytes). Use grep or a narrower file.` };
223898
+ const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
223899
+ const target = resolveReadPath(baseRoot, request.relativePath);
223900
+ const stat2 = await safeStat(target);
223901
+ if (!stat2?.isFile()) return { ok: false, error: `File does not exist: ${target}` };
223902
+ if (stat2.size > MAX_READ_BYTES && request.encoding !== "base64") {
223903
+ return { ok: false, error: `File is too large for direct text read (${stat2.size} bytes). Use grep or a narrower file.` };
222807
223904
  }
222808
223905
  const buffer = await fsp.readFile(target);
222809
223906
  return {
222810
223907
  ok: true,
222811
- relativePath: path10.relative(workspaceRoot, target) || path10.basename(target),
223908
+ relativePath: displayPath(baseRoot, target, request.relativePath),
222812
223909
  content: request.encoding === "base64" ? buffer.toString("base64") : buffer.toString("utf8"),
222813
- sizeBytes: stat.size,
223910
+ sizeBytes: stat2.size,
222814
223911
  ...request.encoding === "base64" ? { encoding: "base64" } : {}
222815
223912
  };
222816
223913
  },
222817
223914
  writeWorkspaceFile: async (request) => {
222818
- const target = resolveWritePath(workspaceRoot, request.relativePath);
222819
- if (!isInside(workspaceRoot, target)) return { ok: false, error: `Refusing to write outside CLI workspace: ${target}` };
223915
+ const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
223916
+ const target = resolveWritePath(baseRoot, request.relativePath);
222820
223917
  await fsp.mkdir(path10.dirname(target), { recursive: true });
222821
223918
  const flag = request.overwrite === true ? "w" : "wx";
223919
+ const payload = request.encoding === "base64" && typeof request.contentBase64 === "string" ? Buffer.from(request.contentBase64, "base64") : request.content ?? "";
222822
223920
  try {
222823
- await fsp.writeFile(target, request.content ?? "", { encoding: "utf8", flag });
223921
+ await fsp.writeFile(target, payload, typeof payload === "string" ? { encoding: "utf8", flag } : { flag });
222824
223922
  } catch (error) {
222825
223923
  if (error.code === "EEXIST") {
222826
- return { ok: false, error: `File already exists: ${path10.relative(workspaceRoot, target)}. Pass overwrite=true to replace it.` };
223924
+ return { ok: false, error: `File already exists: ${displayPath(baseRoot, target, request.relativePath)}. Pass overwrite=true to replace it.` };
222827
223925
  }
222828
223926
  throw error;
222829
223927
  }
222830
223928
  return {
222831
223929
  ok: true,
222832
- relativePath: path10.relative(workspaceRoot, target),
222833
- bytesWritten: Buffer.byteLength(request.content ?? "", "utf8")
223930
+ relativePath: displayPath(baseRoot, target, request.relativePath),
223931
+ bytesWritten: Buffer.byteLength(payload)
222834
223932
  };
222835
223933
  },
222836
223934
  moveLocalFile: async (request) => {
222837
223935
  const src = resolveReadPath(workspaceRoot, request.src);
222838
223936
  const dest = resolveWritePath(workspaceRoot, request.dest);
222839
- if (!isInside(workspaceRoot, src) || !isInside(workspaceRoot, dest)) return { ok: false, error: "Move paths must stay inside the CLI workspace." };
222840
223937
  await fsp.mkdir(path10.dirname(dest), { recursive: true });
222841
223938
  await fsp.rename(src, dest);
222842
223939
  return { ok: true, fromPath: src, toPath: dest };
@@ -222844,37 +223941,32 @@ function createCliNodeLocalBridge(input) {
222844
223941
  copyLocalFile: async (request) => {
222845
223942
  const src = resolveReadPath(workspaceRoot, request.src);
222846
223943
  const dest = resolveWritePath(workspaceRoot, request.dest);
222847
- if (!isInside(workspaceRoot, src) || !isInside(workspaceRoot, dest)) return { ok: false, error: "Copy paths must stay inside the CLI workspace." };
222848
223944
  await fsp.mkdir(path10.dirname(dest), { recursive: true });
222849
223945
  await fsp.copyFile(src, dest);
222850
223946
  return { ok: true, fromPath: src, toPath: dest };
222851
223947
  },
222852
223948
  createDirectory: async (request) => {
222853
223949
  const target = resolveWritePath(workspaceRoot, request.path);
222854
- if (!isInside(workspaceRoot, target)) return { ok: false, error: `Refusing to create directory outside CLI workspace: ${target}` };
222855
223950
  await fsp.mkdir(target, { recursive: true });
222856
223951
  return { ok: true, path: target };
222857
223952
  },
222858
223953
  deleteLocalFile: async (request) => {
222859
223954
  const target = resolveWritePath(workspaceRoot, request.path);
222860
- if (!isInside(workspaceRoot, target)) return { ok: false, error: `Refusing to delete outside CLI workspace: ${target}` };
222861
- const stat = await safeStat(target);
222862
- if (!stat) return { ok: false, error: `File does not exist: ${target}` };
222863
- if (!stat.isFile() && !stat.isSymbolicLink()) return { ok: false, error: "deleteLocalFile only deletes files, not directories." };
223955
+ const stat2 = await safeStat(target);
223956
+ if (!stat2) return { ok: false, error: `File does not exist: ${target}` };
223957
+ if (!stat2.isFile() && !stat2.isSymbolicLink()) return { ok: false, error: "deleteLocalFile only deletes files, not directories." };
222864
223958
  await fsp.rm(target);
222865
223959
  return { ok: true, path: target };
222866
223960
  },
222867
223961
  printFile: async () => ({ ok: false, error: "Printing is not supported from Perch CLI local mode." }),
222868
223962
  listWorkspaceFilesGlob: async (request) => {
222869
- const root2 = request.path ? resolveReadPath(workspaceRoot, request.path) : workspaceRoot;
222870
- if (!isInside(workspaceRoot, root2)) {
222871
- return { ok: false, matches: [], totalFound: 0, truncated: false, resolvedRoot: workspaceRoot, executionHost: "electron_desktop", errorCode: "path_outside_workspace", errorMessage: `Path is outside CLI workspace: ${root2}` };
222872
- }
223963
+ const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
223964
+ const root2 = request.path ? resolveReadPath(baseRoot, request.path) : baseRoot;
222873
223965
  const maxResults = sanitizeMaxResults(request.maxResults);
222874
223966
  const pattern = request.pattern?.trim() || "**/*";
222875
223967
  const regex2 = globToRegex(pattern);
222876
223968
  const files = await collectFiles(root2, { maxVisits: Math.max(maxResults * 20, 1e3) });
222877
- const matches = files.map((file) => path10.relative(root2, file).replace(/\\/g, "/")).filter((relative) => regex2.test(relative)).slice(0, maxResults);
223969
+ const matches = files.map((file) => path10.relative(root2, file).replace(/\\/g, "/")).filter((relative2) => regex2.test(relative2)).slice(0, maxResults);
222878
223970
  return {
222879
223971
  ok: true,
222880
223972
  matches,
@@ -222884,23 +223976,21 @@ function createCliNodeLocalBridge(input) {
222884
223976
  dirsVisited: 0,
222885
223977
  pattern,
222886
223978
  searchPath: root2,
222887
- resolvedRoot: workspaceRoot,
223979
+ resolvedRoot: root2,
222888
223980
  executionHost: "electron_desktop"
222889
223981
  };
222890
223982
  },
222891
223983
  searchWorkspaceFilesGrep: async (request) => {
222892
- const root2 = request.path ? resolveReadPath(workspaceRoot, request.path) : workspaceRoot;
222893
- if (!isInside(workspaceRoot, root2)) {
222894
- return { ok: false, matches: [], totalMatches: 0, truncated: false, resolvedRoot: workspaceRoot, executionHost: "electron_desktop", errorCode: "path_outside_workspace", errorMessage: `Path is outside CLI workspace: ${root2}` };
222895
- }
223984
+ const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
223985
+ const root2 = request.path ? resolveReadPath(baseRoot, request.path) : baseRoot;
222896
223986
  const maxResults = sanitizeMaxResults(request.maxResults);
222897
223987
  const includeRegex = request.include ? globToRegex(request.include) : null;
222898
223988
  const queryRegex = compileQueryRegex(request.query, request.caseSensitive !== false);
222899
223989
  const files = await collectFiles(root2, { maxVisits: 5e3 });
222900
223990
  const matches = [];
222901
223991
  for (const file of files) {
222902
- const relative = path10.relative(root2, file).replace(/\\/g, "/");
222903
- if (includeRegex && !includeRegex.test(relative)) continue;
223992
+ const relative2 = path10.relative(root2, file).replace(/\\/g, "/");
223993
+ if (includeRegex && !includeRegex.test(relative2)) continue;
222904
223994
  const text = await readTextFileIfReasonable(file);
222905
223995
  if (text === null) continue;
222906
223996
  const lines = text.split(/\r?\n/);
@@ -222910,7 +224000,7 @@ function createCliNodeLocalBridge(input) {
222910
224000
  queryRegex.lastIndex = 0;
222911
224001
  if (!match) continue;
222912
224002
  matches.push({
222913
- file: relative,
224003
+ file: relative2,
222914
224004
  line: index + 1,
222915
224005
  column: match.index + 1,
222916
224006
  match: match[0],
@@ -222928,35 +224018,34 @@ function createCliNodeLocalBridge(input) {
222928
224018
  filesSearched: files.length,
222929
224019
  query: request.query,
222930
224020
  searchPath: root2,
222931
- resolvedRoot: workspaceRoot,
224021
+ resolvedRoot: root2,
222932
224022
  executionHost: "electron_desktop"
222933
224023
  };
222934
224024
  },
222935
224025
  statWorkspacePath: async (request) => {
222936
- const target = resolveReadPath(workspaceRoot, request.relativePath);
222937
- if (!isInside(workspaceRoot, target)) {
222938
- return { ok: false, relativePath: request.relativePath, exists: false, executionHost: "electron_desktop", errorCode: "path_outside_workspace", errorMessage: `Path is outside CLI workspace: ${target}` };
222939
- }
222940
- const stat = await safeStat(target);
224026
+ const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
224027
+ const target = resolveReadPath(baseRoot, request.relativePath);
224028
+ const stat2 = await safeStat(target);
222941
224029
  return {
222942
224030
  ok: true,
222943
- relativePath: path10.relative(workspaceRoot, target) || ".",
222944
- exists: Boolean(stat),
222945
- isFile: stat?.isFile() ?? false,
222946
- isDirectory: stat?.isDirectory() ?? false,
222947
- sizeBytes: stat?.size,
222948
- modifiedAt: stat?.mtime.toISOString(),
224031
+ relativePath: path10.isAbsolute(request.relativePath) || request.relativePath.startsWith("~") ? target : path10.relative(baseRoot, target) || ".",
224032
+ exists: Boolean(stat2),
224033
+ isFile: stat2?.isFile() ?? false,
224034
+ isDirectory: stat2?.isDirectory() ?? false,
224035
+ sizeBytes: stat2?.size,
224036
+ modifiedAt: stat2?.mtime.toISOString(),
222949
224037
  executionHost: "electron_desktop"
222950
224038
  };
222951
224039
  },
222952
224040
  listLocalSources: async (request) => {
222953
224041
  const query = typeof request === "object" && request ? request.query?.toLowerCase().trim() ?? "" : "";
222954
224042
  const maxResults = typeof request === "object" && request ? sanitizeMaxResults(request.maxResults) : DEFAULT_MAX_RESULTS;
222955
- const files = await collectFiles(workspaceRoot, { maxVisits: Math.max(maxResults * 20, 1e3) });
222956
- const entries = files.map((file) => localSourceEntry(workspaceRoot, file)).filter((entry) => !query || entry.relativePath.toLowerCase().includes(query) || entry.fileName.toLowerCase().includes(query)).slice(0, maxResults);
224043
+ const root2 = typeof request === "object" && request?.path ? resolveReadPath(workspaceRoot, request.path) : workspaceRoot;
224044
+ const files = await collectFiles(root2, { maxVisits: Math.max(maxResults * 20, 1e3) });
224045
+ const entries = files.map((file) => localSourceEntry(root2, file, root2 !== workspaceRoot)).filter((entry) => !query || entry.relativePath.toLowerCase().includes(query) || entry.fileName.toLowerCase().includes(query)).slice(0, maxResults);
222957
224046
  return {
222958
224047
  ok: true,
222959
- rootId: CLI_ROOT_ID,
224048
+ rootId: root2 === workspaceRoot ? CLI_ROOT_ID : "cli-absolute-root",
222960
224049
  entries,
222961
224050
  totalFound: files.length,
222962
224051
  truncated: files.length > maxResults,
@@ -222967,16 +224056,15 @@ function createCliNodeLocalBridge(input) {
222967
224056
  },
222968
224057
  readLocalFile: async (localSourceId) => {
222969
224058
  const target = resolveLocalSourceId(workspaceRoot, localSourceId);
222970
- if (!isInside(workspaceRoot, target)) return { ok: false, error: `Path is outside CLI workspace: ${target}` };
222971
- const stat = await safeStat(target);
222972
- if (!stat?.isFile()) return { ok: false, error: `File does not exist: ${target}` };
224059
+ const stat2 = await safeStat(target);
224060
+ if (!stat2?.isFile()) return { ok: false, error: `File does not exist: ${target}` };
222973
224061
  const buffer = await fsp.readFile(target);
222974
224062
  return {
222975
224063
  ok: true,
222976
224064
  data: buffer.toString("base64"),
222977
224065
  fileName: path10.relative(workspaceRoot, target) || path10.basename(target),
222978
224066
  mimeType: inferMimeType(target),
222979
- sizeBytes: stat.size,
224067
+ sizeBytes: stat2.size,
222980
224068
  encoding: "base64"
222981
224069
  };
222982
224070
  },
@@ -222992,34 +224080,282 @@ function createCliNodeLocalBridge(input) {
222992
224080
  listMcpTools: async () => [],
222993
224081
  callMcpTool: async () => ({ ok: false, error: "MCP tools are not available in CLI local mode." }),
222994
224082
  reconnectMcp: async () => ({ ok: false, error: "MCP tools are not available in CLI local mode." }),
222995
- prepareSandboxInputs: async () => ({ ok: false, error: "Sandbox input staging is not available in CLI local mode." }),
222996
- onSandboxEvent: () => () => {
224083
+ runLocalAPAuditPacket: async (request) => runCliAPAuditPacket(workspaceRoot, request),
224084
+ runLocalPrepareAPEvidence: async (request) => runCliPrepareAPEvidence(workspaceRoot, request),
224085
+ runLocalQueryAPCases: async (request) => runCliQueryAPCases(workspaceRoot, request),
224086
+ runLocalRenderAPControlGraph: async (request) => runCliRenderAPControlGraph(workspaceRoot, request),
224087
+ prepareSandboxInputs: async (request) => prepareCliSandboxInputs(workspaceRoot, request),
224088
+ runSandboxCodeJob: async (request) => runCliSandboxCodeJob(workspaceRoot, request, emitSandboxEvent),
224089
+ onSandboxEvent: (runId, handler) => {
224090
+ const handlers = sandboxHandlers.get(runId) ?? /* @__PURE__ */ new Set();
224091
+ handlers.add(handler);
224092
+ sandboxHandlers.set(runId, handlers);
224093
+ return () => {
224094
+ handlers.delete(handler);
224095
+ if (handlers.size === 0) sandboxHandlers.delete(runId);
224096
+ };
222997
224097
  },
222998
224098
  offSandboxEvent: () => {
222999
224099
  }
223000
224100
  };
223001
224101
  }
223002
- async function runShellCommand(input) {
223003
- const startedAt = Date.now();
223004
- const cwd2 = input.cwd ? resolveReadPath(input.workspaceRoot, input.cwd) : input.workspaceRoot;
223005
- if (!isInside(input.workspaceRoot, cwd2)) {
224102
+ async function runCliAPAuditPacket(workspaceRoot, request) {
224103
+ const startMs = Date.now();
224104
+ try {
224105
+ const result2 = await generateAPCorePacket({
224106
+ folderPath: resolveReadPath(workspaceRoot, request.folderPath),
224107
+ outputRoot: request.outputRoot ? resolveWritePath(workspaceRoot, request.outputRoot) : void 0,
224108
+ timestamp: request.timestamp,
224109
+ writingStudioNarrativeHtml: request.writingStudioNarrativeHtml,
224110
+ writingStudioNarrativeText: request.writingStudioNarrativeText,
224111
+ writingStudioNarrativeDiagnostic: request.writingStudioNarrativeDiagnostic,
224112
+ requireWritingStudioNarrative: request.requireWritingStudioNarrative
224113
+ });
223006
224114
  return {
223007
- ok: false,
223008
- stdout: "",
223009
- stderr: "",
223010
- exitCode: null,
223011
- durationMs: Date.now() - startedAt,
223012
- timedOut: false,
223013
- command: input.command,
223014
- cwd: cwd2,
224115
+ ok: true,
224116
+ data: {
224117
+ packet: result2.payload.packet,
224118
+ outputDir: result2.run.outputDir,
224119
+ outputFiles: result2.payload.packet.outputFiles,
224120
+ summary: result2.summary
224121
+ },
223015
224122
  executionHost: "electron_desktop",
223016
- errorCode: "cwd_outside_workspace",
223017
- errorMessage: `Command cwd is outside CLI workspace: ${cwd2}`
224123
+ durationMs: Date.now() - startMs
223018
224124
  };
224125
+ } catch (error) {
224126
+ return cliAPAuditError(startMs, error);
223019
224127
  }
224128
+ }
224129
+ async function runCliPrepareAPEvidence(workspaceRoot, request) {
224130
+ const startMs = Date.now();
224131
+ try {
224132
+ const result2 = await prepareAPCoreEvidence({
224133
+ folderPath: resolveReadPath(workspaceRoot, request.folderPath),
224134
+ artifactRoot: request.artifactRoot ? resolveWritePath(workspaceRoot, request.artifactRoot) : void 0,
224135
+ timestamp: request.timestamp,
224136
+ mode: request.mode
224137
+ });
224138
+ const artifact = result2.payload.artifact;
224139
+ return {
224140
+ ok: true,
224141
+ data: {
224142
+ artifactId: artifact.artifactId,
224143
+ outputDir: artifact.outputDir,
224144
+ outputFiles: artifact.outputFiles,
224145
+ coverage: artifact.coverage,
224146
+ metrics: artifact.metrics,
224147
+ caseSummary: summarizeCliCases(artifact.cases ?? []),
224148
+ topCases: (artifact.cases ?? []).slice(0, 12),
224149
+ duplicateCases: result2.payload.duplicateCases,
224150
+ controlGraph: result2.payload.controlGraph,
224151
+ relativeOutputDir: typeof artifact.outputDir === "string" ? path10.basename(artifact.outputDir) : null,
224152
+ approvedRootMatch: true
224153
+ },
224154
+ executionHost: "electron_desktop",
224155
+ durationMs: Date.now() - startMs
224156
+ };
224157
+ } catch (error) {
224158
+ return cliAPError(startMs, error);
224159
+ }
224160
+ }
224161
+ async function runCliQueryAPCases(workspaceRoot, request) {
224162
+ const startMs = Date.now();
224163
+ try {
224164
+ const result2 = await queryAPCases(
224165
+ normalizeCliArtifactRequest(workspaceRoot, request)
224166
+ );
224167
+ return {
224168
+ ok: result2.ok,
224169
+ data: result2,
224170
+ error: result2.ok ? void 0 : result2.error,
224171
+ errorCode: result2.ok ? void 0 : result2.errorCode,
224172
+ executionHost: "electron_desktop",
224173
+ durationMs: Date.now() - startMs
224174
+ };
224175
+ } catch (error) {
224176
+ return cliAPError(startMs, error);
224177
+ }
224178
+ }
224179
+ async function runCliRenderAPControlGraph(workspaceRoot, request) {
224180
+ const startMs = Date.now();
224181
+ try {
224182
+ const result2 = await renderAPControlGraph(normalizeCliArtifactRequest(workspaceRoot, request));
224183
+ return {
224184
+ ok: result2.ok,
224185
+ data: result2,
224186
+ error: result2.ok ? void 0 : result2.error,
224187
+ errorCode: result2.ok ? void 0 : result2.errorCode,
224188
+ executionHost: "electron_desktop",
224189
+ durationMs: Date.now() - startMs
224190
+ };
224191
+ } catch (error) {
224192
+ return cliAPError(startMs, error);
224193
+ }
224194
+ }
224195
+ function normalizeCliArtifactRequest(workspaceRoot, request) {
224196
+ return {
224197
+ ...request,
224198
+ folderPath: request.folderPath ? resolveReadPath(workspaceRoot, request.folderPath) : void 0,
224199
+ artifactRoot: request.artifactRoot ? resolveWritePath(workspaceRoot, request.artifactRoot) : void 0
224200
+ };
224201
+ }
224202
+ function cliAPError(startMs, error) {
224203
+ const message = error instanceof Error ? error.message : String(error);
224204
+ return {
224205
+ ok: false,
224206
+ error: message,
224207
+ errorCode: classifyCliAPError(message),
224208
+ executionHost: "electron_desktop",
224209
+ durationMs: Date.now() - startMs
224210
+ };
224211
+ }
224212
+ function cliAPAuditError(startMs, error) {
224213
+ const message = error instanceof Error ? error.message : String(error);
224214
+ return {
224215
+ ok: false,
224216
+ error: message,
224217
+ errorCode: classifyCliAPError(message),
224218
+ executionHost: "electron_desktop",
224219
+ durationMs: Date.now() - startMs
224220
+ };
224221
+ }
224222
+ function classifyCliAPError(message) {
224223
+ if (/folderPath is required|invalid/i.test(message)) return "invalid_input";
224224
+ if (/not a directory/i.test(message)) return "invalid_folder";
224225
+ if (/not found|ENOENT/i.test(message)) return "folder_not_found";
224226
+ return "execution_failed";
224227
+ }
224228
+ function summarizeCliCases(cases) {
224229
+ const counts = {};
224230
+ for (const item of cases) {
224231
+ const severity = item.severity ?? "unknown";
224232
+ counts[severity] = (counts[severity] ?? 0) + 1;
224233
+ }
224234
+ return counts;
224235
+ }
224236
+ async function prepareCliSandboxInputs(workspaceRoot, request) {
224237
+ const jobId = request.jobId?.trim() || `cli-sandbox-${Date.now()}`;
224238
+ const maxFiles = Math.max(1, Math.min(request.maxFiles ?? 20, 100));
224239
+ const maxBytesPerFile = Math.max(1, Math.min(request.maxBytesPerFile ?? 20 * 1024 * 1024, 50 * 1024 * 1024));
224240
+ const tempDir = await fsp.mkdtemp(path10.join(os.tmpdir(), `${jobId}-`));
224241
+ const entries = [];
224242
+ const skipped = [];
224243
+ for (const localSourceId of request.localSourceIds.slice(0, maxFiles)) {
224244
+ const sourcePath = resolveLocalSourceId(workspaceRoot, localSourceId);
224245
+ const stat2 = await safeStat(sourcePath);
224246
+ if (!stat2?.isFile()) {
224247
+ skipped.push({ localSourceId, reason: `File does not exist: ${sourcePath}` });
224248
+ continue;
224249
+ }
224250
+ if (stat2.size > maxBytesPerFile) {
224251
+ skipped.push({ localSourceId, reason: `File is too large: ${stat2.size} bytes.` });
224252
+ continue;
224253
+ }
224254
+ const relativePath = safeSandboxRelativePath(path10.isAbsolute(localSourceId) || localSourceId.startsWith("~") ? path10.basename(sourcePath) : path10.relative(workspaceRoot, sourcePath));
224255
+ const tempPath = path10.join(tempDir, relativePath);
224256
+ await fsp.mkdir(path10.dirname(tempPath), { recursive: true });
224257
+ await fsp.copyFile(sourcePath, tempPath);
224258
+ entries.push({
224259
+ localSourceId,
224260
+ fileName: path10.basename(sourcePath),
224261
+ relativePath,
224262
+ tempPath,
224263
+ sizeBytes: stat2.size,
224264
+ mimeType: inferMimeType(sourcePath),
224265
+ fileType: guessCliFileType(sourcePath)
224266
+ });
224267
+ }
224268
+ return { ok: true, jobId, tempDir, entries, skipped };
224269
+ }
224270
+ async function runCliSandboxCodeJob(workspaceRoot, request, onEvent) {
224271
+ const hasCommand = typeof request?.command === "string" && request.command.trim().length > 0;
224272
+ const hasCode = typeof request?.code === "string" && request.code.trim().length > 0;
224273
+ if (!request || !hasCommand && !hasCode) {
224274
+ return {
224275
+ ok: false,
224276
+ kind: "blocked",
224277
+ message: "run_sandbox_code requires either command or code.",
224278
+ error: "invalid_input",
224279
+ executionHost: "electron-main"
224280
+ };
224281
+ }
224282
+ const hostResult = await runDesktopSandboxCodeJob(
224283
+ {
224284
+ runId: request.runId,
224285
+ workspaceKey: request.workspaceKey ?? request.runId ?? null,
224286
+ command: request.command,
224287
+ language: request.language,
224288
+ code: request.code,
224289
+ label: request.label ?? null,
224290
+ sources: Array.isArray(request.sources) ? request.sources : [],
224291
+ timeoutMs: request.timeoutMs,
224292
+ networkPolicy: request.networkPolicy ?? "disabled"
224293
+ },
224294
+ {
224295
+ getApprovedRoot: (rootId) => rootId === CLI_ROOT_ID ? { id: CLI_ROOT_ID, path: workspaceRoot, approvedAt: (/* @__PURE__ */ new Date()).toISOString() } : null,
224296
+ isPathSafe: (_rootPath, targetPath) => !targetPath.split(/[\\/]+/).includes(".."),
224297
+ isInsideApprovedRoot: (filePath) => {
224298
+ const absolute = path10.resolve(expandHome4(filePath));
224299
+ if (isInside(workspaceRoot, absolute)) {
224300
+ return { id: CLI_ROOT_ID, path: workspaceRoot, approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
224301
+ }
224302
+ return { id: "cli-absolute-root", path: path10.dirname(absolute), approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
224303
+ },
224304
+ shouldIgnore: (fileName) => IGNORED_DIRS.has(fileName),
224305
+ guessMimeType: inferMimeType,
224306
+ onEvent
224307
+ }
224308
+ );
224309
+ const data = sandboxHostData(hostResult);
224310
+ return {
224311
+ ok: hostResult.ok,
224312
+ kind: hostResult.kind,
224313
+ message: hostResult.message,
224314
+ error: hostResult.ok ? void 0 : "execution_failed",
224315
+ executionHost: "electron-main",
224316
+ runId: hostResult.runId,
224317
+ data
224318
+ };
224319
+ }
224320
+ function sandboxHostData(result2) {
224321
+ return {
224322
+ status: result2.status,
224323
+ exitCode: result2.exitCode,
224324
+ durationMs: result2.durationMs,
224325
+ stdout: result2.stdout,
224326
+ stderr: result2.stderr,
224327
+ stdoutTruncated: result2.stdoutTruncated,
224328
+ stderrTruncated: result2.stderrTruncated,
224329
+ producedFiles: result2.producedFiles,
224330
+ inputManifest: result2.inputManifest,
224331
+ runtimeInfo: result2.runtimeInfo,
224332
+ structuredOutput: result2.structuredOutput,
224333
+ workspacePath: result2.workspacePath,
224334
+ language: result2.language,
224335
+ reportJsonPresent: result2.reportJsonPresent,
224336
+ codeSha256: result2.codeSha256,
224337
+ warnings: result2.warnings
224338
+ };
224339
+ }
224340
+ function safeSandboxRelativePath(value) {
224341
+ const clean = value.replace(/\\/g, "/").split("/").filter((part) => part && part !== "." && part !== "..").join("/");
224342
+ return clean || "input";
224343
+ }
224344
+ function guessCliFileType(filePath) {
224345
+ const ext = path10.extname(filePath).toLowerCase();
224346
+ if ([".xlsx", ".xls"].includes(ext)) return "spreadsheet";
224347
+ if (ext === ".csv" || ext === ".tsv") return "csv";
224348
+ if (ext === ".pdf") return "pdf";
224349
+ if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) return "image";
224350
+ if ([".doc", ".docx", ".txt", ".md", ".rtf"].includes(ext)) return "document";
224351
+ return "unknown";
224352
+ }
224353
+ async function runShellCommand(input) {
224354
+ const startedAt = Date.now();
224355
+ const cwd2 = input.cwd ? resolveReadPath(input.workspaceRoot, input.cwd) : input.workspaceRoot;
223020
224356
  const timeoutMs = Math.max(1e3, Math.min(input.timeoutMs ?? 3e4, 12e4));
223021
- return new Promise((resolve4) => {
223022
- const child = spawn(process.env.SHELL || "/bin/zsh", ["-lc", input.command], {
224357
+ return new Promise((resolve5) => {
224358
+ const child = spawn2(process.env.SHELL || "/bin/zsh", ["-lc", input.command], {
223023
224359
  cwd: cwd2,
223024
224360
  env: process.env,
223025
224361
  stdio: ["ignore", "pipe", "pipe"]
@@ -223039,7 +224375,7 @@ async function runShellCommand(input) {
223039
224375
  });
223040
224376
  child.on("error", (error) => {
223041
224377
  clearTimeout(timer);
223042
- resolve4({
224378
+ resolve5({
223043
224379
  ok: false,
223044
224380
  stdout,
223045
224381
  stderr,
@@ -223056,7 +224392,7 @@ async function runShellCommand(input) {
223056
224392
  child.on("close", (code, signal) => {
223057
224393
  clearTimeout(timer);
223058
224394
  const capped = capOutput(stdout, stderr);
223059
- resolve4({
224395
+ resolve5({
223060
224396
  ok: code === 0 && !timedOut,
223061
224397
  stdout: capped.stdout,
223062
224398
  stderr: capped.stderr,
@@ -223077,8 +224413,18 @@ function resolveReadPath(root2, inputPath) {
223077
224413
  const expanded = expandHome4(inputPath || ".");
223078
224414
  return path10.resolve(path10.isAbsolute(expanded) ? expanded : path10.join(root2, expanded));
223079
224415
  }
224416
+ function resolveRequestRoot(defaultRoot, requestRoot) {
224417
+ const trimmed = requestRoot?.trim();
224418
+ return trimmed ? resolveReadPath(defaultRoot, trimmed) : defaultRoot;
224419
+ }
223080
224420
  function resolveWritePath(root2, inputPath) {
223081
- return path10.resolve(root2, inputPath || ".");
224421
+ const expanded = expandHome4(inputPath || ".");
224422
+ return path10.resolve(path10.isAbsolute(expanded) ? expanded : path10.join(root2, expanded));
224423
+ }
224424
+ function displayPath(root2, target, requestedPath) {
224425
+ const expanded = expandHome4(requestedPath || ".");
224426
+ if (path10.isAbsolute(expanded) || requestedPath.startsWith("~")) return target;
224427
+ return path10.relative(root2, target) || path10.basename(target);
223082
224428
  }
223083
224429
  function resolveLocalSourceId(root2, localSourceId) {
223084
224430
  const raw = localSourceId.includes("::") ? localSourceId.split("::").slice(1).join("::") : localSourceId;
@@ -223090,8 +224436,8 @@ function expandHome4(inputPath) {
223090
224436
  return inputPath;
223091
224437
  }
223092
224438
  function isInside(root2, candidate) {
223093
- const relative = path10.relative(path10.resolve(root2), path10.resolve(candidate));
223094
- return relative === "" || !relative.startsWith("..") && !path10.isAbsolute(relative);
224439
+ const relative2 = path10.relative(path10.resolve(root2), path10.resolve(candidate));
224440
+ return relative2 === "" || !relative2.startsWith("..") && !path10.isAbsolute(relative2);
223095
224441
  }
223096
224442
  async function safeStat(target) {
223097
224443
  try {
@@ -223154,8 +224500,8 @@ function escapeRegex2(value) {
223154
224500
  return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
223155
224501
  }
223156
224502
  async function readTextFileIfReasonable(file) {
223157
- const stat = await safeStat(file);
223158
- if (!stat?.isFile() || stat.size > 1e6) return null;
224503
+ const stat2 = await safeStat(file);
224504
+ if (!stat2?.isFile() || stat2.size > 1e6) return null;
223159
224505
  const buffer = await fsp.readFile(file);
223160
224506
  if (buffer.includes(0)) return null;
223161
224507
  return buffer.toString("utf8");
@@ -223163,18 +224509,18 @@ async function readTextFileIfReasonable(file) {
223163
224509
  function sanitizeMaxResults(value) {
223164
224510
  return Math.max(1, Math.min(typeof value === "number" ? Math.floor(value) : DEFAULT_MAX_RESULTS, 1e3));
223165
224511
  }
223166
- function localSourceEntry(root2, file) {
223167
- const stat = fs9.statSync(file);
224512
+ function localSourceEntry(root2, file, absoluteId = false) {
224513
+ const stat2 = fs9.statSync(file);
223168
224514
  const relativePath = path10.relative(root2, file);
223169
224515
  const extension2 = path10.extname(file).toLowerCase();
223170
224516
  return {
223171
- localSourceId: `${CLI_ROOT_ID}::${relativePath}`,
223172
- rootId: CLI_ROOT_ID,
224517
+ localSourceId: absoluteId ? file : `${CLI_ROOT_ID}::${relativePath}`,
224518
+ rootId: absoluteId ? "cli-absolute-root" : CLI_ROOT_ID,
223173
224519
  relativePath,
223174
224520
  fileName: path10.basename(file),
223175
224521
  extension: extension2,
223176
- sizeBytes: stat.size,
223177
- modifiedAt: stat.mtime.toISOString(),
224522
+ sizeBytes: stat2.size,
224523
+ modifiedAt: stat2.mtime.toISOString(),
223178
224524
  mimeType: inferMimeType(file),
223179
224525
  isDirectory: false
223180
224526
  };
@@ -223214,6 +224560,9 @@ var CLI_ROOT_ID, DEFAULT_MAX_RESULTS, MAX_READ_BYTES, IGNORED_DIRS;
223214
224560
  var init_nodeLocalBridge = __esm({
223215
224561
  "features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts"() {
223216
224562
  "use strict";
224563
+ init_perchCore();
224564
+ init_perchBusinessTools();
224565
+ init_localSandboxHost();
223217
224566
  CLI_ROOT_ID = "cli-root";
223218
224567
  DEFAULT_MAX_RESULTS = 200;
223219
224568
  MAX_READ_BYTES = 2e6;
@@ -223867,8 +225216,8 @@ function createMemoryAuthStorage() {
223867
225216
  }
223868
225217
  async function createOAuthCallbackServer(input) {
223869
225218
  let resolveResult = null;
223870
- const resultPromise = new Promise((resolve4) => {
223871
- resolveResult = resolve4;
225219
+ const resultPromise = new Promise((resolve5) => {
225220
+ resolveResult = resolve5;
223872
225221
  });
223873
225222
  const server = http.createServer((request, response) => {
223874
225223
  const requestUrl = new URL(request.url ?? "/", `http://${input.host}`);
@@ -223895,11 +225244,11 @@ async function createOAuthCallbackServer(input) {
223895
225244
  resolveResult?.({ ok: true, code });
223896
225245
  resolveResult = null;
223897
225246
  });
223898
- await new Promise((resolve4, reject2) => {
225247
+ await new Promise((resolve5, reject2) => {
223899
225248
  server.once("error", reject2);
223900
225249
  server.listen(0, input.host, () => {
223901
225250
  server.off("error", reject2);
223902
- resolve4();
225251
+ resolve5();
223903
225252
  });
223904
225253
  });
223905
225254
  const address = server.address();
@@ -223917,7 +225266,7 @@ async function createOAuthCallbackServer(input) {
223917
225266
  waitForCode: async () => resultPromise,
223918
225267
  close: async () => {
223919
225268
  clearTimeout(timeout);
223920
- await new Promise((resolve4) => server.close(() => resolve4()));
225269
+ await new Promise((resolve5) => server.close(() => resolve5()));
223921
225270
  }
223922
225271
  };
223923
225272
  }
@@ -224871,21 +226220,21 @@ var require_react_development = __commonJS({
224871
226220
  );
224872
226221
  actScopeDepth = prevActScopeDepth;
224873
226222
  }
224874
- function recursivelyFlushAsyncActWork(returnValue, resolve4, reject2) {
226223
+ function recursivelyFlushAsyncActWork(returnValue, resolve5, reject2) {
224875
226224
  var queue2 = ReactSharedInternals.actQueue;
224876
226225
  if (null !== queue2)
224877
226226
  if (0 !== queue2.length)
224878
226227
  try {
224879
226228
  flushActQueue(queue2);
224880
226229
  enqueueTask(function() {
224881
- return recursivelyFlushAsyncActWork(returnValue, resolve4, reject2);
226230
+ return recursivelyFlushAsyncActWork(returnValue, resolve5, reject2);
224882
226231
  });
224883
226232
  return;
224884
226233
  } catch (error) {
224885
226234
  ReactSharedInternals.thrownErrors.push(error);
224886
226235
  }
224887
226236
  else ReactSharedInternals.actQueue = null;
224888
- 0 < ReactSharedInternals.thrownErrors.length ? (queue2 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject2(queue2)) : resolve4(returnValue);
226237
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue2 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject2(queue2)) : resolve5(returnValue);
224889
226238
  }
224890
226239
  function flushActQueue(queue2) {
224891
226240
  if (!isFlushing) {
@@ -225072,7 +226421,7 @@ var require_react_development = __commonJS({
225072
226421
  ));
225073
226422
  });
225074
226423
  return {
225075
- then: function(resolve4, reject2) {
226424
+ then: function(resolve5, reject2) {
225076
226425
  didAwaitActCall = true;
225077
226426
  thenable.then(
225078
226427
  function(returnValue) {
@@ -225082,7 +226431,7 @@ var require_react_development = __commonJS({
225082
226431
  flushActQueue(queue2), enqueueTask(function() {
225083
226432
  return recursivelyFlushAsyncActWork(
225084
226433
  returnValue,
225085
- resolve4,
226434
+ resolve5,
225086
226435
  reject2
225087
226436
  );
225088
226437
  });
@@ -225096,7 +226445,7 @@ var require_react_development = __commonJS({
225096
226445
  ReactSharedInternals.thrownErrors.length = 0;
225097
226446
  reject2(_thrownError);
225098
226447
  }
225099
- } else resolve4(returnValue);
226448
+ } else resolve5(returnValue);
225100
226449
  },
225101
226450
  function(error) {
225102
226451
  popActScope(prevActQueue, prevActScopeDepth);
@@ -225118,15 +226467,15 @@ var require_react_development = __commonJS({
225118
226467
  if (0 < ReactSharedInternals.thrownErrors.length)
225119
226468
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
225120
226469
  return {
225121
- then: function(resolve4, reject2) {
226470
+ then: function(resolve5, reject2) {
225122
226471
  didAwaitActCall = true;
225123
226472
  0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue2, enqueueTask(function() {
225124
226473
  return recursivelyFlushAsyncActWork(
225125
226474
  returnValue$jscomp$0,
225126
- resolve4,
226475
+ resolve5,
225127
226476
  reject2
225128
226477
  );
225129
- })) : resolve4(returnValue$jscomp$0);
226478
+ })) : resolve5(returnValue$jscomp$0);
225130
226479
  }
225131
226480
  };
225132
226481
  };
@@ -225553,7 +226902,7 @@ var init_compat = __esm({
225553
226902
  });
225554
226903
 
225555
226904
  // node_modules/environment/index.js
225556
- var isBrowser, isNode2, isBun, isDeno, isElectron, isJsDom, isWebWorker, isDedicatedWorker, isSharedWorker, isServiceWorker, platform, isMacOs, isWindows, isLinux, isIos, isAndroid;
226905
+ var isBrowser, isNode2, isBun, isDeno, isElectron, isJsDom, isWebWorker, isDedicatedWorker, isSharedWorker, isServiceWorker, platform2, isMacOs, isWindows, isLinux, isIos, isAndroid;
225557
226906
  var init_environment = __esm({
225558
226907
  "node_modules/environment/index.js"() {
225559
226908
  isBrowser = globalThis.window?.document !== void 0;
@@ -225566,12 +226915,12 @@ var init_environment = __esm({
225566
226915
  isDedicatedWorker = typeof DedicatedWorkerGlobalScope !== "undefined" && globalThis instanceof DedicatedWorkerGlobalScope;
225567
226916
  isSharedWorker = typeof SharedWorkerGlobalScope !== "undefined" && globalThis instanceof SharedWorkerGlobalScope;
225568
226917
  isServiceWorker = typeof ServiceWorkerGlobalScope !== "undefined" && globalThis instanceof ServiceWorkerGlobalScope;
225569
- platform = globalThis.navigator?.userAgentData?.platform;
225570
- isMacOs = platform === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform === "darwin";
225571
- isWindows = platform === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform === "win32";
225572
- isLinux = platform === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform === "linux";
225573
- isIos = platform === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
225574
- isAndroid = platform === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform === "android";
226918
+ platform2 = globalThis.navigator?.userAgentData?.platform;
226919
+ isMacOs = platform2 === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform === "darwin";
226920
+ isWindows = platform2 === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform === "win32";
226921
+ isLinux = platform2 === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform === "linux";
226922
+ isIos = platform2 === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
226923
+ isAndroid = platform2 === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform === "android";
225575
226924
  }
225576
226925
  });
225577
226926
 
@@ -229888,8 +231237,8 @@ var require_react_reconciler_production = __commonJS({
229888
231237
  currentEntangledActionThenable = {
229889
231238
  status: "pending",
229890
231239
  value: void 0,
229891
- then: function(resolve4) {
229892
- entangledListeners.push(resolve4);
231240
+ then: function(resolve5) {
231241
+ entangledListeners.push(resolve5);
229893
231242
  }
229894
231243
  };
229895
231244
  }
@@ -229912,8 +231261,8 @@ var require_react_reconciler_production = __commonJS({
229912
231261
  status: "pending",
229913
231262
  value: null,
229914
231263
  reason: null,
229915
- then: function(resolve4) {
229916
- listeners.push(resolve4);
231264
+ then: function(resolve5) {
231265
+ listeners.push(resolve5);
229917
231266
  }
229918
231267
  };
229919
231268
  thenable.then(
@@ -239512,8 +240861,8 @@ var require_react_reconciler_development = __commonJS({
239512
240861
  currentEntangledActionThenable = {
239513
240862
  status: "pending",
239514
240863
  value: void 0,
239515
- then: function(resolve4) {
239516
- entangledListeners.push(resolve4);
240864
+ then: function(resolve5) {
240865
+ entangledListeners.push(resolve5);
239517
240866
  }
239518
240867
  };
239519
240868
  }
@@ -239536,8 +240885,8 @@ var require_react_reconciler_development = __commonJS({
239536
240885
  status: "pending",
239537
240886
  value: null,
239538
240887
  reason: null,
239539
- then: function(resolve4) {
239540
- listeners.push(resolve4);
240888
+ then: function(resolve5) {
240889
+ listeners.push(resolve5);
239541
240890
  }
239542
240891
  };
239543
240892
  thenable.then(
@@ -254995,7 +256344,7 @@ var require_websocket = __commonJS({
254995
256344
  var http2 = __require("http");
254996
256345
  var net = __require("net");
254997
256346
  var tls = __require("tls");
254998
- var { randomBytes, createHash: createHash2 } = __require("crypto");
256347
+ var { randomBytes, createHash: createHash3 } = __require("crypto");
254999
256348
  var { Duplex, Readable } = __require("stream");
255000
256349
  var { URL: URL2 } = __require("url");
255001
256350
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -255663,7 +257012,7 @@ var require_websocket = __commonJS({
255663
257012
  abortHandshake(websocket, socket, "Invalid Upgrade header");
255664
257013
  return;
255665
257014
  }
255666
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
257015
+ const digest = createHash3("sha1").update(key + GUID).digest("base64");
255667
257016
  if (res.headers["sec-websocket-accept"] !== digest) {
255668
257017
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
255669
257018
  return;
@@ -256032,7 +257381,7 @@ var require_websocket_server = __commonJS({
256032
257381
  var EventEmitter3 = __require("events");
256033
257382
  var http2 = __require("http");
256034
257383
  var { Duplex } = __require("stream");
256035
- var { createHash: createHash2 } = __require("crypto");
257384
+ var { createHash: createHash3 } = __require("crypto");
256036
257385
  var extension2 = require_extension();
256037
257386
  var PerMessageDeflate2 = require_permessage_deflate();
256038
257387
  var subprotocol2 = require_subprotocol();
@@ -256339,7 +257688,7 @@ var require_websocket_server = __commonJS({
256339
257688
  );
256340
257689
  }
256341
257690
  if (this._state > RUNNING) return abortHandshake(socket, 503);
256342
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
257691
+ const digest = createHash3("sha1").update(key + GUID).digest("base64");
256343
257692
  const headers = [
256344
257693
  "HTTP/1.1 101 Switching Protocols",
256345
257694
  "Upgrade: websocket",
@@ -277257,8 +278606,8 @@ var init_ink = __esm({
277257
278606
  }
277258
278607
  }
277259
278608
  async waitUntilExit() {
277260
- this.exitPromise ||= new Promise((resolve4, reject2) => {
277261
- this.resolveExitPromise = resolve4;
278609
+ this.exitPromise ||= new Promise((resolve5, reject2) => {
278610
+ this.resolveExitPromise = resolve5;
277262
278611
  this.rejectExitPromise = reject2;
277263
278612
  });
277264
278613
  if (!this.beforeExitHandler) {
@@ -279641,7 +280990,7 @@ async function runAuthCommand(parsed, writer) {
279641
280990
  `);
279642
280991
  return 0;
279643
280992
  }
279644
- await new Promise((resolve4) => setTimeout(resolve4, 1500));
280993
+ await new Promise((resolve5) => setTimeout(resolve5, 1500));
279645
280994
  }
279646
280995
  writer.stderr("No CLI session arrived. Try `perch login` again after confirming the browser sign-in completed.\n");
279647
280996
  return 2;
@@ -279774,14 +281123,14 @@ function writeAPScenarioResult(result2, json, writer) {
279774
281123
  }
279775
281124
  function resolveFolderPath(input) {
279776
281125
  const resolved = resolvePath(input);
279777
- const stat = fs13.existsSync(resolved) ? fs13.statSync(resolved) : null;
279778
- if (!stat?.isDirectory()) throw new Error(`Folder does not exist: ${resolved}`);
281126
+ const stat2 = fs13.existsSync(resolved) ? fs13.statSync(resolved) : null;
281127
+ if (!stat2?.isDirectory()) throw new Error(`Folder does not exist: ${resolved}`);
279779
281128
  return resolved;
279780
281129
  }
279781
281130
  function resolveExistingDirectory(input) {
279782
281131
  const resolved = resolvePath(input);
279783
- const stat = fs13.existsSync(resolved) ? fs13.statSync(resolved) : null;
279784
- if (!stat?.isDirectory()) throw new Error(`Directory does not exist: ${resolved}`);
281132
+ const stat2 = fs13.existsSync(resolved) ? fs13.statSync(resolved) : null;
281133
+ if (!stat2?.isDirectory()) throw new Error(`Directory does not exist: ${resolved}`);
279785
281134
  return resolved;
279786
281135
  }
279787
281136
  function resolvePath(input) {