@swmansion/argent 0.14.1-next.7 → 0.14.1-next.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-cmds.mjs CHANGED
@@ -7289,11 +7289,15 @@ function isScalarType(type) {
7289
7289
  }
7290
7290
  function coerceScalar(raw, type, field) {
7291
7291
  if (type === "number") {
7292
+ if (raw.trim() === "")
7293
+ throw new FlagParseException(`--${field} expected a number, got "${raw}"`);
7292
7294
  const n2 = Number(raw);
7293
7295
  if (Number.isNaN(n2)) throw new FlagParseException(`--${field} expected a number, got "${raw}"`);
7294
7296
  return n2;
7295
7297
  }
7296
7298
  if (type === "integer") {
7299
+ if (raw.trim() === "")
7300
+ throw new FlagParseException(`--${field} expected an integer, got "${raw}"`);
7297
7301
  const n2 = Number(raw);
7298
7302
  if (!Number.isInteger(n2))
7299
7303
  throw new FlagParseException(`--${field} expected an integer, got "${raw}"`);
@@ -7322,6 +7326,7 @@ function parseFlags(argv, schema) {
7322
7326
  let helpRequested = false;
7323
7327
  let rawArgs = null;
7324
7328
  const seenArrayFields = /* @__PURE__ */ new Set();
7329
+ const jsonFields = /* @__PURE__ */ new Set();
7325
7330
  function takeNext(i2, flag) {
7326
7331
  if (i2 + 1 >= argv.length) {
7327
7332
  throw new FlagParseException(`--${flag} requires a value`);
@@ -7360,7 +7365,13 @@ function parseFlags(argv, schema) {
7360
7365
  if (flag.endsWith("-json")) {
7361
7366
  const fieldName = flag.slice(0, -"-json".length);
7362
7367
  const { value: value2, nextIndex: nextIndex2 } = inlineValue !== void 0 ? { value: inlineValue, nextIndex: i2 } : takeNext(i2, flag);
7368
+ if (seenArrayFields.has(fieldName)) {
7369
+ throw new FlagParseException(
7370
+ `--${fieldName} and --${flag} cannot be mixed for the same field; pass it entirely as --${flag} '<json>' or --args '<json>'`
7371
+ );
7372
+ }
7363
7373
  args[fieldName] = parseJsonOrThrow(value2, `--${flag}`);
7374
+ jsonFields.add(fieldName);
7364
7375
  i2 = nextIndex2;
7365
7376
  continue;
7366
7377
  }
@@ -7392,6 +7403,11 @@ function parseFlags(argv, schema) {
7392
7403
  );
7393
7404
  }
7394
7405
  const { value: value2, nextIndex: nextIndex2 } = inlineValue !== void 0 ? { value: inlineValue, nextIndex: i2 } : takeNext(i2, flag);
7406
+ if (jsonFields.has(flag)) {
7407
+ throw new FlagParseException(
7408
+ `--${flag} and --${flag}-json cannot be mixed for the same field; pass it entirely as --${flag}-json '<json>' or --args '<json>'`
7409
+ );
7410
+ }
7395
7411
  const coerced = coerceScalar(value2, itemType, flag);
7396
7412
  if (!seenArrayFields.has(flag)) {
7397
7413
  args[flag] = [coerced];
@@ -111642,6 +111642,9 @@ Booted/ready devices are listed first. Platforms whose CLI is unavailable are si
111642
111642
  // ../tool-server/src/utils/variant-proposals.ts
111643
111643
  init_src();
111644
111644
  var MAX_COMMENT_LENGTH = 2e3;
111645
+ var MAX_PENDING_OUTCOMES = 32;
111646
+ var MAX_MATCH_VALUE_LENGTH = 200;
111647
+ var MAX_ANNOTATIONS = 200;
111645
111648
  function slug(s) {
111646
111649
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
111647
111650
  }
@@ -111695,6 +111698,18 @@ var VariantProposalStore = class {
111695
111698
  waitersList = [];
111696
111699
  /** Frozen result of the current round once the user submits. */
111697
111700
  lastOutcome = null;
111701
+ /**
111702
+ * Completed outcomes that finished with no `await_user_selection` parked to
111703
+ * receive them directly (submitSelection's "no waiter" branch), queued in
111704
+ * completion order. `autoRollIfCompleted` rolls into a fresh round on the
111705
+ * very next `propose_variant` regardless of whether the outcome was ever
111706
+ * retrieved — so this queue, unlike `lastOutcome`/`completed`/`consumed`
111707
+ * (which describe only the CURRENT round), survives `reset()` and is
111708
+ * drained first by `awaitSelection`, oldest first. Without it, a human's
111709
+ * already-submitted selection is destroyed the moment the next
111710
+ * `propose_variant` call rolls the round out from under it.
111711
+ */
111712
+ pendingOutcomes = [];
111698
111713
  /** Begin a fresh round, discarding the previous one's proposals/selections. */
111699
111714
  reset() {
111700
111715
  const superseded = this.waitersList.filter((w) => !w.settled);
@@ -111750,11 +111765,11 @@ var VariantProposalStore = class {
111750
111765
  });
111751
111766
  }
111752
111767
  }
111753
- autoRollIfConsumed() {
111754
- if (this.completed && this.consumed) this.reset();
111768
+ autoRollIfCompleted() {
111769
+ if (this.completed) this.reset();
111755
111770
  }
111756
111771
  proposeVariant(input) {
111757
- this.autoRollIfConsumed();
111772
+ this.autoRollIfCompleted();
111758
111773
  if (input.udid && input.udid.trim()) this.device = input.udid.trim();
111759
111774
  const match = input.match ?? { by: "text", value: input.element };
111760
111775
  const key = `${match.by}:${match.value.trim().toLowerCase()}`;
@@ -111817,7 +111832,10 @@ var VariantProposalStore = class {
111817
111832
  */
111818
111833
  setCliSession(active, agents = []) {
111819
111834
  const transitioned = this.cliSession !== active;
111820
- if (transitioned && active && (this.completed || this.proposals.length > 0)) this.reset();
111835
+ if (transitioned) {
111836
+ if (this.completed || this.proposals.length > 0) this.reset();
111837
+ this.pendingOutcomes = [];
111838
+ }
111821
111839
  this.cliSession = active;
111822
111840
  this.lensAgents = active ? agents.map((a) => ({ ...a })) : [];
111823
111841
  this.lensAgentChoice = null;
@@ -111882,30 +111900,51 @@ var VariantProposalStore = class {
111882
111900
  }
111883
111901
  /** Called by the preview UI when the human presses "Complete selection". */
111884
111902
  submitSelection(input) {
111885
- const cleanAnnotations = (input.annotations ?? []).filter((a) => a && typeof a.comment === "string" && a.comment.trim()).map((a) => ({
111903
+ const cleanAnnotations = (input.annotations ?? []).filter((a) => a && typeof a.comment === "string" && a.comment.trim()).slice(0, MAX_ANNOTATIONS).map((a) => ({
111886
111904
  target: String(a.target ?? "").slice(0, 200) || "(element)",
111887
- match: a.match,
111905
+ // Cap the matcher value too — it is caller-supplied and, unlike the
111906
+ // comment, was previously ingested uncapped.
111907
+ match: {
111908
+ by: a.match?.by ?? "text",
111909
+ value: String(a.match?.value ?? "").slice(0, MAX_MATCH_VALUE_LENGTH)
111910
+ },
111888
111911
  comment: a.comment.trim().slice(0, MAX_COMMENT_LENGTH)
111889
111912
  }));
111890
111913
  if (this.proposals.length === 0 && cleanAnnotations.length === 0) {
111891
111914
  throw new Error("Nothing to submit \u2014 no proposals and no comments.");
111892
111915
  }
111893
- this.submitted = input.selections.filter((s) => this.proposals.some((p) => p.id === s.elementId)).map(
111894
- (s) => s.comment === void 0 ? s : { ...s, comment: s.comment.slice(0, MAX_COMMENT_LENGTH) }
111895
- );
111916
+ const seenElementIds = /* @__PURE__ */ new Set();
111917
+ this.submitted = input.selections.filter((s) => this.proposals.some((p) => p.id === s.elementId)).filter((s) => {
111918
+ if (seenElementIds.has(s.elementId)) return false;
111919
+ seenElementIds.add(s.elementId);
111920
+ return true;
111921
+ }).map((s) => {
111922
+ const capped = s.comment === void 0 ? s : { ...s, comment: s.comment.slice(0, MAX_COMMENT_LENGTH) };
111923
+ return capped.variantId == null ? capped : { ...capped, variantId: capped.variantId.slice(0, MAX_MATCH_VALUE_LENGTH) };
111924
+ });
111896
111925
  this.submittedAnnotations = cleanAnnotations;
111897
111926
  this.globalComment = (input.globalComment ?? "").trim().slice(0, MAX_COMMENT_LENGTH);
111927
+ const wasResubmit = this.completed;
111898
111928
  this.completed = true;
111899
- this.consumed = false;
111929
+ if (!wasResubmit) this.consumed = false;
111900
111930
  this.lastOutcome = this.buildOutcome();
111901
111931
  const round2 = this.round;
111902
111932
  const toSettle = this.waitersList.filter((w) => !w.settled && w.round === round2);
111903
111933
  this.waitersList = this.waitersList.filter((w) => w.round !== round2 || w.settled);
111904
- if (toSettle.length > 0) this.consumed = true;
111905
- if (this.cliSession) this.consumed = true;
111906
- for (const w of toSettle) {
111907
- w.settled = true;
111908
- w.settle(this.lastOutcome);
111934
+ if (toSettle.length > 0) {
111935
+ this.consumed = true;
111936
+ for (const w of toSettle) {
111937
+ w.settled = true;
111938
+ w.settle(this.lastOutcome);
111939
+ }
111940
+ } else if (this.cliSession) {
111941
+ this.consumed = true;
111942
+ } else if (!this.consumed) {
111943
+ this.pendingOutcomes = this.pendingOutcomes.filter((o) => o.round !== round2);
111944
+ this.pendingOutcomes.push(this.lastOutcome);
111945
+ if (this.pendingOutcomes.length > MAX_PENDING_OUTCOMES) {
111946
+ this.pendingOutcomes.shift();
111947
+ }
111909
111948
  }
111910
111949
  this.events.emit("changed");
111911
111950
  this.events.emit("selectionSubmitted");
@@ -111969,16 +112008,23 @@ var VariantProposalStore = class {
111969
112008
  * round is superseded), so concurrent / re-entrant awaits never strand.
111970
112009
  */
111971
112010
  awaitSelection(opts) {
112011
+ if (opts.signal?.aborted) {
112012
+ const err = new Error("await_user_selection aborted (client disconnected)");
112013
+ err.name = "AbortError";
112014
+ return Promise.reject(err);
112015
+ }
112016
+ if (this.pendingOutcomes.length > 0) {
112017
+ const outcome = this.pendingOutcomes.shift();
112018
+ if (outcome.round === this.round) this.consumed = true;
112019
+ const morePending = this.pendingOutcomes.length > 0 || outcome.round !== this.round && this.proposals.length > 0;
112020
+ return Promise.resolve(morePending ? { ...outcome, morePending: true } : outcome);
112021
+ }
111972
112022
  if (this.completed && this.consumed) {
111973
112023
  return Promise.resolve({
111974
112024
  status: "no_proposals",
111975
112025
  message: "The previous selection round was already returned. Call propose_variant to stage new variants before awaiting again."
111976
112026
  });
111977
112027
  }
111978
- if (this.completed && !this.consumed) {
111979
- this.consumed = true;
111980
- return Promise.resolve(this.lastOutcome ?? this.buildOutcome());
111981
- }
111982
112028
  if (this.proposals.length === 0) {
111983
112029
  return Promise.resolve({
111984
112030
  status: "no_proposals",
@@ -112027,10 +112073,7 @@ var VariantProposalStore = class {
112027
112073
  this.waitersList.push(waiter);
112028
112074
  this.events.emit("changed");
112029
112075
  this.events.emit("awaitParked");
112030
- if (opts.signal) {
112031
- if (opts.signal.aborted) return onAbort();
112032
- opts.signal.addEventListener("abort", onAbort, { once: true });
112033
- }
112076
+ if (opts.signal) opts.signal.addEventListener("abort", onAbort, { once: true });
112034
112077
  });
112035
112078
  }
112036
112079
  };
@@ -112910,6 +112953,13 @@ function descendantText(parsed, maxChars = 120) {
112910
112953
  const stack = [parsed];
112911
112954
  while (stack.length > 0) {
112912
112955
  const x = stack.pop();
112956
+ if (attrIsTrue(x.attrs, "password")) {
112957
+ if (!seen.has("[password]")) {
112958
+ seen.add("[password]");
112959
+ parts.push("[password]");
112960
+ }
112961
+ continue;
112962
+ }
112913
112963
  for (const k of ["text", "content-desc"]) {
112914
112964
  const v = (x.attrs[k] ?? "").trim();
112915
112965
  if (v && !seen.has(v)) {
@@ -113001,6 +113051,9 @@ function computeNodeOutput(parsed, scrollClip, outputs, opts) {
113001
113051
  }
113002
113052
  const interactive = isInteractive(attrs);
113003
113053
  let label = labelOf(attrs);
113054
+ if (attrIsTrue(attrs, "password")) {
113055
+ label = "[password]";
113056
+ }
113004
113057
  if (cls.endsWith(".ImageView") && !interactive && !label) {
113005
113058
  return keptChildren;
113006
113059
  }
@@ -113015,6 +113068,9 @@ function computeNodeOutput(parsed, scrollClip, outputs, opts) {
113015
113068
  if (interactive && bounds && keptChildren.length === 1) {
113016
113069
  const c = keptChildren[0];
113017
113070
  if (c.clickable && c.pixelBounds && rectsEqual(c.pixelBounds, bounds)) {
113071
+ if (!c.label && label) c.label = label;
113072
+ const rid = attrs["resource-id"];
113073
+ if (!c.identifier && rid) c.identifier = rid;
113018
113074
  return [c];
113019
113075
  }
113020
113076
  }
@@ -113024,9 +113080,6 @@ function computeNodeOutput(parsed, scrollClip, outputs, opts) {
113024
113080
  (c) => !(c.role === "StaticText" && c.label && lower.includes(c.label.toLowerCase()) && !c.clickable)
113025
113081
  );
113026
113082
  }
113027
- if (attrIsTrue(attrs, "password")) {
113028
- label = "[password]";
113029
- }
113030
113083
  const node = makeUiNode(attrs, deriveUiAutomatorRole(cls), bounds, label, keptChildren);
113031
113084
  if (hiddenInScroll > 0) node.scrollHidden = hiddenInScroll;
113032
113085
  return [node];
@@ -113065,7 +113118,7 @@ function finalizeUiNode(n, children, sw, sh) {
113065
113118
  };
113066
113119
  } else {
113067
113120
  if (children.length === 0) return null;
113068
- if (children.length === 1) return children[0];
113121
+ if (children.length === 1 && !n.label && !n.identifier) return children[0];
113069
113122
  const x1 = Math.min(...children.map((c) => c.frame.x));
113070
113123
  const y1 = Math.min(...children.map((c) => c.frame.y));
113071
113124
  const x2 = Math.max(...children.map((c) => c.frame.x + c.frame.width));
@@ -115430,7 +115483,7 @@ var LogFileWriter = class {
115430
115483
  const sourceFile = sourceUrl ? cleanSourceUrl(sourceUrl) ?? void 0 : void 0;
115431
115484
  const source = sourceFile !== void 0 && sourceLine !== void 0 ? `${sourceFile}:${sourceLine}` : "-";
115432
115485
  const flatMessage = entry.message.replace(/\n/g, " ");
115433
- const levelDisplay = LEVEL_DISPLAY[entry.level] ?? entry.level.toUpperCase().padEnd(5).slice(0, 5);
115486
+ const levelDisplay = LEVEL_DISPLAY[entry.level] ?? entry.level.toUpperCase().padEnd(5);
115434
115487
  const line = `[L:${entry.id}] ${entry.timestamp} ${levelDisplay} ${source} | ${flatMessage}
115435
115488
  `;
115436
115489
  if (this.ready && this.fd !== null) {
@@ -115543,6 +115596,13 @@ function parseFlatLine(line) {
115543
115596
  };
115544
115597
  }
115545
115598
 
115599
+ // ../tool-server/src/utils/debugger/console-timestamp.ts
115600
+ var MAX_TIMESTAMP_MS = 864e13;
115601
+ function consoleTimestampToIso(rawTimestampMs) {
115602
+ const usable = Number.isFinite(rawTimestampMs) && Math.abs(rawTimestampMs) <= MAX_TIMESTAMP_MS;
115603
+ return new Date(usable ? rawTimestampMs : Date.now()).toISOString();
115604
+ }
115605
+
115546
115606
  // ../tool-server/src/blueprints/chromium-js-runtime-debugger.ts
115547
115607
  var CHROMIUM_JS_RUNTIME_DEBUGGER_NAMESPACE = "ChromiumJsRuntimeDebugger";
115548
115608
  function chromiumJsRuntimeDebuggerRef(device) {
@@ -115669,7 +115729,7 @@ var chromiumJsRuntimeDebuggerBlueprint = {
115669
115729
  };
115670
115730
  logWriter.write({
115671
115731
  id: entry.id,
115672
- timestamp: new Date(ts).toISOString(),
115732
+ timestamp: consoleTimestampToIso(ts),
115673
115733
  level: entry.level,
115674
115734
  message: entry.message,
115675
115735
  stackTrace: entry.stackTrace
@@ -117132,7 +117192,7 @@ var jsRuntimeDebuggerBlueprint = {
117132
117192
  };
117133
117193
  logWriter.write({
117134
117194
  id: entry.id,
117135
- timestamp: new Date(entry.timestamp * 1e3).toISOString(),
117195
+ timestamp: consoleTimestampToIso(entry.timestamp),
117136
117196
  level: entry.level,
117137
117197
  message: entry.message,
117138
117198
  stackTrace: entry.stackTrace
@@ -122513,8 +122573,21 @@ function makeComponentTreeScript(opts) {
122513
122573
  return `(async function() {
122514
122574
  var REQUEST_ID = ${requestId};
122515
122575
  var TRACK_SKIPPED = ${trackSkipped};
122576
+ // Deliver errors through the SAME binding channel as the success path. The
122577
+ // consumer (evaluateWithBinding) runs Runtime.evaluate WITHOUT returnByValue/
122578
+ // awaitPromise, so the IIFE's return value is discarded \u2014 a 'return' here never
122579
+ // settles the promise and the tool hangs until the 15s binding timeout. Route
122580
+ // every error through __argent_callback with result = JSON.stringify({ error }),
122581
+ // matching the shape the tool parses (response.result \u2192 JSON.parse \u2192 .error).
122582
+ function __argent_fail(msg) {
122583
+ __argent_callback(JSON.stringify({ requestId: REQUEST_ID, result: JSON.stringify({ error: msg }) }));
122584
+ }
122585
+ // Route any UNEXPECTED throw (outside the named guards below) through the same
122586
+ // binding, so a crash still settles immediately instead of hanging to the 15s
122587
+ // binding timeout. Mirrors inspect-at-point.ts's top-level try/catch.
122588
+ try {
122516
122589
  var hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
122517
- if (!hook) return JSON.stringify({ error: 'No DevTools hook' });
122590
+ if (!hook) { __argent_fail('No DevTools hook'); return; }
122518
122591
  // Collect fiber roots across ALL renderers. A single React renderer id is not
122519
122592
  // safe to assume: secondary reconcilers (e.g. react-native-skia) frequently
122520
122593
  // register first and own renderer id 1, whose roots contain only that library's
@@ -122532,7 +122605,7 @@ function makeComponentTreeScript(opts) {
122532
122605
  var _legacy = hook.getFiberRoots(1);
122533
122606
  if (_legacy) _legacy.forEach(function(_rt) { _allRoots.push(_rt); });
122534
122607
  }
122535
- if (_allRoots.length === 0) return JSON.stringify({ error: 'No fiber roots' });
122608
+ if (_allRoots.length === 0) { __argent_fail('No fiber roots'); return; }
122536
122609
  // Pick the mounted root with the largest fiber subtree \u2014 i.e. the real app UI.
122537
122610
  var root = _allRoots[0];
122538
122611
  var _bestSize = -1;
@@ -122562,7 +122635,7 @@ function makeComponentTreeScript(opts) {
122562
122635
  try { var m = __r(i); if (m && m.UIManager) { UIManagerMod = m.UIManager; break; } } catch(e) {}
122563
122636
  }
122564
122637
  }
122565
- if (!UIManagerMod) return JSON.stringify({ error: 'Could not find UIManager' });
122638
+ if (!UIManagerMod) { __argent_fail('Could not find UIManager'); return; }
122566
122639
  }
122567
122640
 
122568
122641
  var SKIP = new Set([
@@ -122910,6 +122983,9 @@ function makeComponentTreeScript(opts) {
122910
122983
  result.skippedCounts = skippedCounts;
122911
122984
  }
122912
122985
  __argent_callback(JSON.stringify({ requestId: REQUEST_ID, result: JSON.stringify(result) }));
122986
+ } catch (e) {
122987
+ __argent_fail('Component-tree script crashed: ' + (e && e.message ? e.message : String(e)));
122988
+ }
122913
122989
  })()`;
122914
122990
  }
122915
122991
 
@@ -131110,7 +131186,7 @@ function enrich(input) {
131110
131186
  }
131111
131187
 
131112
131188
  // ../tool-server/src/utils/react-profiler/pipeline/03-tag.ts
131113
- var ANIMATED_PATTERN = /(Animated|Animation|Transition|Motion)/i;
131189
+ var ANIMATED_PATTERN = /(?<![A-Z0-9])(Animated|Animation|Transition|Motion)(?=[A-Z0-9_(.]|$)/;
131114
131190
  var RECYCLER_CHILD_PATTERN = /(ListItem|CellItem|Cell|Row|Item)$/i;
131115
131191
  var RECYCLER_PARENT_PATTERN = /^(FlatList|SectionList|VirtualizedList|FlashList|RecyclerListView)/i;
131116
131192
  function tag(input) {
@@ -135202,8 +135278,9 @@ async function renderHangStacksAndroid(opts, target) {
135202
135278
  return `_Invalid hang_index ${opts.hangIndex}. There are ${hangRows.length} hangs (0-indexed)._`;
135203
135279
  }
135204
135280
  const hang = hangRows[opts.hangIndex];
135205
- const startNs = hang.ts_ns;
135206
- const endNs = hang.ts_ns + hang.dur_ns;
135281
+ const startNs = Number(hang.ts_ns);
135282
+ const durNs = Number(hang.dur_ns);
135283
+ const endNs = startNs + durNs;
135207
135284
  const [stateRows, sampleRows] = await Promise.all([
135208
135285
  runTpQuery({
135209
135286
  tracePath: opts.tracePath,
@@ -135224,18 +135301,22 @@ async function renderHangStacksAndroid(opts, target) {
135224
135301
  }
135225
135302
  }).catch(() => [])
135226
135303
  ]);
135227
- const durationMs = Math.round(hang.dur_ns / 1e6);
135304
+ const durationMs = Math.round(durNs / 1e6);
135305
+ const stateBreakdown = stateRows.map((r) => ({
135306
+ state: r.state,
135307
+ blockedFunction: r.blocked_function,
135308
+ durationMs: Math.round(Number(r.total_dur_ns) / 1e6)
135309
+ }));
135228
135310
  const lines = [
135229
135311
  `## Hang #${opts.hangIndex} \u2014 ${hang.kind} (${durationMs}ms)` + (hang.reason ? ` \u2014 reason: \`${hang.reason}\`` : ""),
135230
135312
  ""
135231
135313
  ];
135232
- if (stateRows.length > 0) {
135314
+ if (stateBreakdown.length > 0) {
135233
135315
  lines.push("### Main-thread State Breakdown", "");
135234
135316
  lines.push("| State | Blocked on | Duration |", "|---|---|---|");
135235
- for (const row of stateRows) {
135236
- const ms = Math.round(row.total_dur_ns / 1e6);
135317
+ for (const entry of stateBreakdown) {
135237
135318
  lines.push(
135238
- `| ${row.state} | ${row.blocked_function ? `\`${row.blocked_function}\`` : "\u2014"} | ${ms}ms |`
135319
+ `| ${entry.state} | ${entry.blockedFunction ? `\`${entry.blockedFunction}\`` : "\u2014"} | ${entry.durationMs}ms |`
135239
135320
  );
135240
135321
  }
135241
135322
  lines.push("");
@@ -135258,13 +135339,7 @@ async function renderHangStacksAndroid(opts, target) {
135258
135339
  lines.push("```");
135259
135340
  }
135260
135341
  } else {
135261
- const blocking = summarizeHangBlocking(
135262
- stateRows.map((r) => ({
135263
- state: r.state,
135264
- blockedFunction: r.blocked_function,
135265
- durationMs: Math.round(r.total_dur_ns / 1e6)
135266
- }))
135267
- );
135342
+ const blocking = summarizeHangBlocking(stateBreakdown);
135268
135343
  lines.push("### Main-thread Samples During Hang", "");
135269
135344
  if (blocking && blocking.kind === "blocked") {
135270
135345
  lines.push(
@@ -135274,7 +135349,7 @@ async function renderHangStacksAndroid(opts, target) {
135274
135349
  lines.push(
135275
135350
  `_No usable on-CPU stack samples were captured during this hang, even though the main thread was on-CPU (state \`${blocking.dominantState}\`, executing) for most of the window \u2014 the sampler could not unwind a call stack (commonly stripped or missing frame symbols). This is genuine main-thread CPU work, not a wait; see the state breakdown above._`
135276
135351
  );
135277
- } else if (stateRows.length > 0) {
135352
+ } else if (stateBreakdown.length > 0) {
135278
135353
  lines.push(
135279
135354
  `_No on-CPU stack samples were captured during this hang. The main thread spent the window off-CPU or runnable-but-not-scheduled, so there is no CPU call stack to show; see the state breakdown above._`
135280
135355
  );
@@ -135405,8 +135480,12 @@ function cpuRowsToAggregatorRows(rows, traceStartNs) {
135405
135480
  timestampsNs: [],
135406
135481
  callChains: [{ chain: [dominant], count: row.sample_count }],
135407
135482
  precomputedBursts: parseBurstWindows(row.burst_windows, traceStartMs),
135408
- firstMs: Math.round((row.first_ts_ns - traceStartNs) / 1e6),
135409
- lastMs: Math.round((row.last_ts_ns - traceStartNs) / 1e6),
135483
+ // first/last_ts_ns are absolute CLOCK_MONOTONIC ns: they exceed 2^53 after
135484
+ // ~104 days of device uptime, so the WASM decoder hands them back as bigint
135485
+ // (see readCell). traceStartNs is already a plain Number, so coerce here to
135486
+ // avoid "Cannot mix BigInt and other types" — values stay integral.
135487
+ firstMs: Math.round((Number(row.first_ts_ns) - traceStartNs) / 1e6),
135488
+ lastMs: Math.round((Number(row.last_ts_ns) - traceStartNs) / 1e6),
135410
135489
  sampleCount: row.sample_count
135411
135490
  });
135412
135491
  }
@@ -135414,16 +135493,16 @@ function cpuRowsToAggregatorRows(rows, traceStartNs) {
135414
135493
  }
135415
135494
  function hangRowsToBottlenecks(rows, traceStartNs) {
135416
135495
  return rows.map((row) => {
135417
- const durationMs = Math.round(row.dur_ns / 1e6);
135418
- const startNs = row.ts_ns - traceStartNs;
135496
+ const durNs = Number(row.dur_ns);
135497
+ const startNs = Number(row.ts_ns) - traceStartNs;
135419
135498
  return {
135420
135499
  type: "ui_hang",
135421
135500
  platform: "android",
135422
135501
  hangType: row.kind,
135423
- durationMs,
135502
+ durationMs: Math.round(durNs / 1e6),
135424
135503
  startTimeFormatted: formatTraceTime(startNs),
135425
135504
  startNs,
135426
- endNs: startNs + row.dur_ns,
135505
+ endNs: startNs + durNs,
135427
135506
  suspectedFunctions: [],
135428
135507
  appCallChains: [],
135429
135508
  severity: classifyAndroidHangSeverity(row),
@@ -135473,7 +135552,7 @@ function formatTraceTime(ns) {
135473
135552
  function classifyAndroidHangSeverity(row) {
135474
135553
  if (row.kind === "anr") return "RED";
135475
135554
  if (row.reason === "App Deadline Missed") return "RED";
135476
- const durationMs = row.dur_ns / 1e6;
135555
+ const durationMs = Number(row.dur_ns) / 1e6;
135477
135556
  if (durationMs > 500) return "RED";
135478
135557
  return "YELLOW";
135479
135558
  }
@@ -140825,11 +140904,14 @@ It parks until the user presses "Complete selection" in the Argent Lens window (
140825
140904
  that opens automatically on the user's screen), then returns their choices and any comments.
140826
140905
 
140827
140906
  Returns one of:
140828
- \u2022 { status: "completed", selections: [{ element, chosenVariant, comment? }], unselected,
140829
- annotations: [{ target, match, comment }], globalComment }
140830
- \u2014 the user is done; apply chosenVariant for each element (skip ones in \`unselected\`).
140831
- \`annotations\` are free-form comments the user pinned to specific on-screen elements via
140832
- the inspector \u2014 treat each as a change request for that element.
140907
+ \u2022 { status: "completed", round, selections: [{ element, chosenVariant, comment? }], unselected,
140908
+ annotations: [{ target, match, comment }], globalComment, morePending? }
140909
+ \u2014 the user finished this round; apply chosenVariant for each element (skip ones in
140910
+ \`unselected\`). \`annotations\` are free-form comments the user pinned to specific on-screen
140911
+ elements via the inspector \u2014 treat each as a change request for that element.
140912
+ If \`morePending\` is true, another already-decided round or a freshly-staged round is
140913
+ still waiting \u2014 call await_user_selection AGAIN (repeat until you get a completed result
140914
+ without \`morePending\`, a \`pending\`, or \`no_proposals\`); do not stop on this result alone.
140833
140915
  \u2022 { status: "pending", message, proposedElements } \u2014 timeoutSeconds elapsed with no submission.
140834
140916
  Expected, not an error: the proposals are still live; call await_user_selection AGAIN.
140835
140917
  \u2022 { status: "no_proposals" } \u2014 you called this before propose_variant.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.14.1-next.7",
3
+ "version": "0.14.1-next.8",
4
4
  "description": "MCP server for iOS Simulator and Android Emulator control",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {