@quantiya/codevibe-antigravity-plugin 2.0.29 → 2.0.31

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/server.js +358 -38
  2. package/package.json +2 -2
package/dist/server.js CHANGED
@@ -1120,23 +1120,28 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
1120
1120
  async probeApprovalUIActive() {
1121
1121
  try {
1122
1122
  const snapshot = await this.captureSnapshot();
1123
- const active = looksLikePromptSnapshot(snapshot);
1123
+ const hasPrompt = looksLikePromptSnapshot(snapshot);
1124
1124
  let headerText = null;
1125
- if (active) {
1126
- const kind = detectActivePromptKind(snapshot);
1125
+ let kind = null;
1126
+ if (hasPrompt) {
1127
+ kind = detectActivePromptKind(snapshot);
1127
1128
  if (kind === "question") {
1128
1129
  const q = extractQuestionHeader(snapshot);
1129
1130
  headerText = q ? q.body : null;
1130
1131
  } else if (kind === "file_access") {
1131
1132
  const fileAccess = extractFileAccessPrompt(snapshot);
1132
1133
  headerText = fileAccess ? fileAccess.identity : null;
1134
+ } else if (kind === "system_error") {
1135
+ const err = extractSystemErrorPrompt(snapshot);
1136
+ headerText = err ? err.message : null;
1133
1137
  } else if (kind === "approval") {
1134
1138
  headerText = extractHeader(snapshot);
1135
1139
  }
1136
1140
  }
1137
- return { active, headerText, probeSucceeded: true };
1141
+ const isChooser = hasPrompt && kind !== "system_error";
1142
+ return { active: isChooser, headerText, probeSucceeded: true, kind };
1138
1143
  } catch {
1139
- return { active: false, headerText: null, probeSucceeded: false };
1144
+ return { active: false, headerText: null, probeSucceeded: false, kind: null };
1140
1145
  }
1141
1146
  }
1142
1147
  // ─── tmux pipe-pane plumbing ─────────────────────────────────────────────
@@ -1397,25 +1402,119 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
1397
1402
  function looksLikePromptDelta(chunk) {
1398
1403
  return (
1399
1404
  // Approval UI signatures
1400
- /Requesting permission for:/i.test(chunk) || /Do you want to proceed\?/i.test(chunk) || /\besc to cancel\b/i.test(chunk) || /^\s*[1-9]\. (?:Yes|No)\b/im.test(chunk) || /\[(?:y\/n|Y\/n|y\/N)\]/.test(chunk) || /Question \d+\/\d+:/i.test(chunk) || /\benter Select\b/i.test(chunk) || /^File access\s*$/im.test(chunk) || /Allow access to this file\?/i.test(chunk) || /No, deny access/i.test(chunk)
1405
+ /Requesting permission for:/i.test(chunk) || /Do you want to proceed\?/i.test(chunk) || /\besc to cancel\b/i.test(chunk) || /^\s*[1-9]\. (?:Yes|No)\b/im.test(chunk) || /\[(?:y\/n|Y\/n|y\/N)\]/.test(chunk) || /Question \d+\/\d+:/i.test(chunk) || /\benter Select\b/i.test(chunk) || /^File access\s*$/im.test(chunk) || /Allow access to this file\?/i.test(chunk) || /No, deny access/i.test(chunk) || /Our servers are experiencing/i.test(chunk) || /Error ID:\s*[0-9a-fA-F-]+/i.test(chunk) || /(?:⚠️|⚠\uFE0F?)\s+.*(?:high traffic|try again in a minute|try again later|stream was interrupted|stream interrupted|overloaded|unexpected error)/i.test(chunk)
1401
1406
  );
1402
1407
  }
1408
+ function isSystemErrorBannerLine(line) {
1409
+ const trimmed = line.trim();
1410
+ if (!trimmed) return false;
1411
+ if (!/^(?:⚠️|⚠\uFE0F?)\s+/.test(trimmed)) return false;
1412
+ if (/Our servers are experiencing/i.test(trimmed)) return true;
1413
+ if (/The stream was interrupted/i.test(trimmed)) return true;
1414
+ if (/The model is overloaded/i.test(trimmed)) return true;
1415
+ if (/(?:high traffic|try again in a minute|try again later|stream was interrupted|stream interrupted|overloaded|unexpected error|error id:)/i.test(trimmed)) return true;
1416
+ return false;
1417
+ }
1418
+ function isSystemErrorActiveFromIndex(lines, errorIdx) {
1419
+ let inHeaderBlock = true;
1420
+ let sawErrorIdLine = false;
1421
+ for (let j = errorIdx + 1; j < lines.length; j++) {
1422
+ const trimmed = lines[j].trim();
1423
+ if (!trimmed) {
1424
+ sawErrorIdLine = false;
1425
+ continue;
1426
+ }
1427
+ if (/^[│\s]*[❯>]\s+\S/.test(trimmed)) return false;
1428
+ if (/^[▶▸\s]*Thought for\s+\d+/i.test(trimmed)) return false;
1429
+ if (/^Thinking\b/i.test(trimmed)) return false;
1430
+ if (/^[●\s]*(?:[A-Za-z_-]+)\(/.test(trimmed)) return false;
1431
+ if (/Requesting permission for:/i.test(trimmed)) return false;
1432
+ if (/Question \d+\/\d+:/i.test(trimmed)) return false;
1433
+ if (/^\s*File access\s*$/i.test(trimmed)) return false;
1434
+ if (/^[>\s]*[1-9]\. \S/.test(trimmed)) return false;
1435
+ if (/Allow access to this file\?/i.test(trimmed)) return false;
1436
+ if (/Do you want to proceed\?/i.test(trimmed)) return false;
1437
+ if (/\benter Select\b/i.test(trimmed)) return false;
1438
+ if (/^Error ID:\s*[0-9a-fA-F-]*/i.test(trimmed)) {
1439
+ inHeaderBlock = false;
1440
+ sawErrorIdLine = true;
1441
+ continue;
1442
+ }
1443
+ if (sawErrorIdLine && /^[0-9a-fA-F-]+$/.test(trimmed) && /[0-9a-fA-F]/.test(trimmed)) {
1444
+ continue;
1445
+ }
1446
+ sawErrorIdLine = false;
1447
+ if (/^[─━│╭╰╮╯\s_-]*$/.test(trimmed)) {
1448
+ inHeaderBlock = false;
1449
+ continue;
1450
+ }
1451
+ if (/^[│\s]*[❯>]\s*$/.test(trimmed)) {
1452
+ inHeaderBlock = false;
1453
+ continue;
1454
+ }
1455
+ if (/^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(trimmed)) {
1456
+ inHeaderBlock = false;
1457
+ continue;
1458
+ }
1459
+ if (/^\s*(?:accept-edits\s+·\s+)?Gemini\s+[\d.]+\s+(?:Flash|Pro)\b/i.test(trimmed)) {
1460
+ inHeaderBlock = false;
1461
+ continue;
1462
+ }
1463
+ if (inHeaderBlock && j - errorIdx <= 4) {
1464
+ continue;
1465
+ }
1466
+ return false;
1467
+ }
1468
+ return true;
1469
+ }
1470
+ function isLegacyYNActiveInLines(lines) {
1471
+ let ynIdx = -1;
1472
+ for (let i = lines.length - 1; i >= 0; i--) {
1473
+ if (/\[(?:y\/n|Y\/n|y\/N)\]/.test(lines[i]) && /\b(?:apply|approve|allow|continue|proceed|run|execute|confirm)\b/i.test(lines[i])) {
1474
+ ynIdx = i;
1475
+ break;
1476
+ }
1477
+ }
1478
+ if (ynIdx < 0) return { active: false, lineIdx: -1 };
1479
+ for (let j = ynIdx + 1; j < lines.length; j++) {
1480
+ const trimmed = lines[j].trim();
1481
+ if (!trimmed) continue;
1482
+ if (/^[─━│╭╰╮╯\s_-]*$/.test(trimmed)) continue;
1483
+ if (/^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(trimmed)) continue;
1484
+ return { active: false, lineIdx: -1 };
1485
+ }
1486
+ return { active: true, lineIdx: ynIdx };
1487
+ }
1403
1488
  function looksLikePromptSnapshot(snapshot) {
1404
1489
  const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
1405
- const recent = stripped.split("\n").slice(-30).join("\n");
1406
- const hasApprovalHeader = /Requesting permission for:/i.test(recent) || /Do you want to proceed\?/i.test(recent);
1407
- const hasApprovalOptions = /^\s*[1-9]\. (?:Yes|No)\b/im.test(recent);
1408
- const hasApprovalFooter = /\besc to cancel\b/i.test(recent);
1409
- const isApproval = hasApprovalHeader && hasApprovalOptions && hasApprovalFooter;
1410
- const hasQuestionHeader = /Question \d+\/\d+:/i.test(recent);
1411
- const hasQuestionOptions = /^[>\s]+[1-9]\. \S/im.test(recent);
1412
- const hasQuestionFooter = /\benter Select\b/i.test(recent);
1413
- const isQuestion = hasQuestionHeader && hasQuestionOptions && hasQuestionFooter;
1414
- const hasFileAccessHeader = /^File access\s*$/im.test(recent);
1415
- const hasFileAccessQuestion = /Allow access to this file\?/i.test(recent);
1416
- const isFileAccess = hasFileAccessHeader && hasFileAccessQuestion && hasApprovalOptions && hasApprovalFooter;
1417
- const legacyYN = /\[(?:y\/n|Y\/n|y\/N)\]/.test(recent) && /\b(?:apply|approve|allow|continue|proceed|run|execute|confirm)\b/i.test(recent);
1418
- return legacyYN || isApproval || isQuestion || isFileAccess;
1490
+ const activeKind = detectActivePromptKind(snapshot);
1491
+ if (activeKind !== null) {
1492
+ if (activeKind === "system_error") return true;
1493
+ const recent = stripped.trimEnd().split("\n").slice(-30).join("\n");
1494
+ if (activeKind === "approval") {
1495
+ const hasApprovalHeader = /Requesting permission for:/i.test(recent) || /Do you want to proceed\?/i.test(recent);
1496
+ const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(recent);
1497
+ const hasApprovalFooter = /\besc to cancel\b/i.test(recent);
1498
+ return hasApprovalHeader && hasApprovalOptions && hasApprovalFooter;
1499
+ }
1500
+ if (activeKind === "question") {
1501
+ const hasQuestionHeader = /Question \d+\/\d+:/i.test(recent);
1502
+ const hasQuestionOptions = /^[>\s]*[1-9]\. \S/im.test(recent);
1503
+ const hasQuestionFooter = /\benter Select\b/i.test(recent);
1504
+ return hasQuestionHeader && hasQuestionOptions && hasQuestionFooter;
1505
+ }
1506
+ if (activeKind === "file_access") {
1507
+ const hasFileAccessHeader = /^File access\s*$/im.test(recent);
1508
+ const hasFileAccessQuestion = /Allow access to this file\?/i.test(recent);
1509
+ const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(recent);
1510
+ const hasApprovalFooter = /\besc to cancel\b/i.test(recent);
1511
+ return hasFileAccessHeader && hasFileAccessQuestion && hasApprovalOptions && hasApprovalFooter;
1512
+ }
1513
+ return true;
1514
+ }
1515
+ const lines = stripped.split("\n");
1516
+ const legacyYN = isLegacyYNActiveInLines(lines);
1517
+ return legacyYN.active;
1419
1518
  }
1420
1519
  function extractFileAccessPrompt(snapshot) {
1421
1520
  const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
@@ -1451,31 +1550,221 @@ function extractFullHeaderLine(snapshot) {
1451
1550
  }
1452
1551
  function detectActivePromptKind(snapshot) {
1453
1552
  const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
1454
- const lines = stripped.split("\n");
1553
+ const lines = stripped.trimEnd().split("\n");
1554
+ const windowStart = Math.max(0, lines.length - 30);
1455
1555
  const approvalRe = /Requesting permission for:/i;
1456
1556
  const questionRe = /Question \d+\/\d+:/i;
1457
1557
  const fileAccessRe = /^\s*File access\s*$/i;
1458
1558
  let lastApprovalLineIdx = -1;
1459
1559
  let lastQuestionLineIdx = -1;
1460
1560
  let lastFileAccessLineIdx = -1;
1561
+ let lastSystemErrorLineIdx = -1;
1461
1562
  for (let i = lines.length - 1; i >= 0; i--) {
1462
- if (lastApprovalLineIdx < 0 && approvalRe.test(lines[i])) {
1463
- lastApprovalLineIdx = i;
1563
+ const line = lines[i];
1564
+ if (i >= windowStart) {
1565
+ if (lastApprovalLineIdx < 0 && approvalRe.test(line)) {
1566
+ lastApprovalLineIdx = i;
1567
+ }
1568
+ if (lastQuestionLineIdx < 0 && questionRe.test(line)) {
1569
+ lastQuestionLineIdx = i;
1570
+ }
1571
+ if (lastFileAccessLineIdx < 0 && fileAccessRe.test(line)) {
1572
+ lastFileAccessLineIdx = i;
1573
+ }
1574
+ }
1575
+ if (lastSystemErrorLineIdx < 0 && isSystemErrorBannerLine(line)) {
1576
+ lastSystemErrorLineIdx = i;
1464
1577
  }
1465
- if (lastQuestionLineIdx < 0 && questionRe.test(lines[i])) {
1466
- lastQuestionLineIdx = i;
1578
+ if (lastApprovalLineIdx >= 0 && lastQuestionLineIdx >= 0 && lastFileAccessLineIdx >= 0 && lastSystemErrorLineIdx >= 0) break;
1579
+ }
1580
+ if (lastApprovalLineIdx < 0 && lastQuestionLineIdx < 0) {
1581
+ for (let i = lines.length - 1; i >= windowStart; i--) {
1582
+ if (/Do you want to proceed\?/i.test(lines[i])) {
1583
+ lastApprovalLineIdx = i;
1584
+ break;
1585
+ }
1586
+ }
1587
+ }
1588
+ const latestChooserIdx = Math.max(
1589
+ lastApprovalLineIdx,
1590
+ lastQuestionLineIdx,
1591
+ lastFileAccessLineIdx
1592
+ );
1593
+ if (lastSystemErrorLineIdx >= 0 && lastSystemErrorLineIdx > latestChooserIdx) {
1594
+ if (latestChooserIdx >= 0) {
1595
+ const remainingLines = lines.slice(lastSystemErrorLineIdx + 1).join("\n");
1596
+ if (latestChooserIdx === lastQuestionLineIdx) {
1597
+ const hasQuestionOptions = /^[>\s]*[1-9]\. \S/im.test(remainingLines);
1598
+ const hasQuestionFooter = /\benter Select\b/i.test(remainingLines);
1599
+ if (hasQuestionOptions && hasQuestionFooter) {
1600
+ return "question";
1601
+ }
1602
+ } else if (latestChooserIdx === lastFileAccessLineIdx) {
1603
+ const hasFileAccessQuestion = /Allow access to this file\?/i.test(remainingLines);
1604
+ const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(remainingLines);
1605
+ const hasApprovalFooter = /\besc to cancel\b/i.test(remainingLines);
1606
+ if (hasFileAccessQuestion && hasApprovalOptions && hasApprovalFooter) {
1607
+ return "file_access";
1608
+ }
1609
+ } else if (latestChooserIdx === lastApprovalLineIdx) {
1610
+ const hasApprovalOptions = /^[>\s]*[1-9]\. (?:Yes|No)\b/im.test(remainingLines);
1611
+ const hasApprovalFooter = /\besc to cancel\b/i.test(remainingLines);
1612
+ if (hasApprovalOptions && hasApprovalFooter) {
1613
+ return "approval";
1614
+ }
1615
+ }
1467
1616
  }
1468
- if (lastFileAccessLineIdx < 0 && fileAccessRe.test(lines[i])) {
1469
- lastFileAccessLineIdx = i;
1617
+ if (isSystemErrorActiveFromIndex(lines, lastSystemErrorLineIdx)) {
1618
+ return "system_error";
1470
1619
  }
1471
- if (lastApprovalLineIdx >= 0 && lastQuestionLineIdx >= 0 && lastFileAccessLineIdx >= 0) break;
1620
+ return null;
1621
+ }
1622
+ if (latestChooserIdx < 0) return null;
1623
+ if (latestChooserIdx === lastFileAccessLineIdx) {
1624
+ return "file_access";
1625
+ }
1626
+ if (latestChooserIdx === lastQuestionLineIdx) {
1627
+ return "question";
1472
1628
  }
1473
- const latest = Math.max(lastApprovalLineIdx, lastQuestionLineIdx, lastFileAccessLineIdx);
1474
- if (latest < 0) return null;
1475
- if (latest === lastFileAccessLineIdx) return "file_access";
1476
- if (latest === lastQuestionLineIdx) return "question";
1477
1629
  return "approval";
1478
1630
  }
1631
+ var SYSTEM_ERROR_COMPLETE_WORDS = /* @__PURE__ */ new Set([
1632
+ "our",
1633
+ "servers",
1634
+ "server",
1635
+ "are",
1636
+ "experiencing",
1637
+ "experience",
1638
+ "high",
1639
+ "traffic",
1640
+ "right",
1641
+ "now",
1642
+ "please",
1643
+ "try",
1644
+ "again",
1645
+ "in",
1646
+ "a",
1647
+ "minute",
1648
+ "minutes",
1649
+ "an",
1650
+ "unexpected",
1651
+ "error",
1652
+ "occurred",
1653
+ "the",
1654
+ "model",
1655
+ "is",
1656
+ "overloaded",
1657
+ "later",
1658
+ "stream",
1659
+ "was",
1660
+ "interrupted",
1661
+ "continue",
1662
+ "task",
1663
+ "you",
1664
+ "were",
1665
+ "working",
1666
+ "on",
1667
+ "while",
1668
+ "processing",
1669
+ "your",
1670
+ "connection",
1671
+ "timeout",
1672
+ "temporary",
1673
+ "unavailable",
1674
+ "service",
1675
+ "gateway",
1676
+ "capacity",
1677
+ "exceeded",
1678
+ "resource",
1679
+ "exhausted",
1680
+ "internal",
1681
+ "failure",
1682
+ "request",
1683
+ "response",
1684
+ "refused",
1685
+ "reset",
1686
+ "network",
1687
+ "operation",
1688
+ "aborted",
1689
+ "cancelled",
1690
+ "rate",
1691
+ "limit",
1692
+ "limited",
1693
+ "throttle",
1694
+ "throttled"
1695
+ ]);
1696
+ function extractSystemErrorPrompt(snapshot) {
1697
+ const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
1698
+ const lines = stripped.split("\n");
1699
+ let bannerIdx = -1;
1700
+ let rawMessage = "";
1701
+ for (let i = lines.length - 1; i >= 0; i--) {
1702
+ if (isSystemErrorBannerLine(lines[i])) {
1703
+ bannerIdx = i;
1704
+ rawMessage = lines[i].trim();
1705
+ break;
1706
+ }
1707
+ }
1708
+ if (bannerIdx < 0) return null;
1709
+ let message = rawMessage.replace(/^(?:⚠️|⚠\uFE0F?)\s*/, "").trim();
1710
+ let wrapWidth = Array.from(lines[bannerIdx].trimEnd()).length;
1711
+ for (let j = bannerIdx + 1; j < Math.min(lines.length, bannerIdx + 6); j++) {
1712
+ const trimmed = lines[j].trim();
1713
+ if (!trimmed || /^Error ID:/i.test(trimmed) || /^[─━│╭╰╮╯\s_-]*$/.test(trimmed) || /^[│\s]*[❯>]/.test(trimmed)) break;
1714
+ const len = Array.from(lines[j].trimEnd()).length;
1715
+ if (len > wrapWidth) wrapWidth = len;
1716
+ }
1717
+ let errorId;
1718
+ for (let j = bannerIdx + 1; j < Math.min(lines.length, bannerIdx + 6); j++) {
1719
+ const trimmed = lines[j].trim();
1720
+ if (!trimmed) continue;
1721
+ if (/^[─━│╭╰╮╯\s_-]*$/.test(trimmed)) break;
1722
+ if (/^[│\s]*[❯>]/.test(trimmed)) break;
1723
+ const idMatch = trimmed.match(/^Error ID:\s*([0-9a-fA-F-]*)/i);
1724
+ if (idMatch) {
1725
+ if (idMatch[1]) {
1726
+ errorId = idMatch[1].trim();
1727
+ }
1728
+ let k = j + 1;
1729
+ while (k < lines.length) {
1730
+ const nextRaw = lines[k];
1731
+ if (!nextRaw.trim()) break;
1732
+ const nextPiece = nextRaw.trim().match(/^([0-9a-fA-F-]+)$/);
1733
+ if (nextPiece && /[0-9a-fA-F]/.test(nextPiece[1]) && !/^[─━│╭╰╮╯\s_-]*$/.test(nextPiece[1])) {
1734
+ errorId = `${errorId || ""}${nextPiece[1].trim()}`;
1735
+ k++;
1736
+ } else {
1737
+ break;
1738
+ }
1739
+ }
1740
+ break;
1741
+ }
1742
+ const prevRaw = lines[j - 1];
1743
+ if (message.endsWith("-") || trimmed.startsWith("-")) {
1744
+ message = `${message}${trimmed}`;
1745
+ } else if (/^[.,!?:;]/.test(trimmed)) {
1746
+ message = `${message}${trimmed}`;
1747
+ } else {
1748
+ const prevEndedAlnum = /[a-zA-Z0-9]$/.test(message);
1749
+ const nextStartsAlnum = /^[a-zA-Z0-9]/.test(trimmed);
1750
+ const prevHadSpace = /\s$/.test(prevRaw);
1751
+ const nextHadSpace = /^\s/.test(lines[j]);
1752
+ const prevLen = Array.from(prevRaw.trimEnd()).length;
1753
+ const prevWord = ((message.match(/([a-zA-Z0-9]+)$/) || [])[1] || "").toLowerCase();
1754
+ const nextWord = ((trimmed.match(/^([a-zA-Z0-9]+)/) || [])[1] || "").toLowerCase();
1755
+ const isTwoCompleteWords = SYSTEM_ERROR_COMPLETE_WORDS.has(prevWord) && SYSTEM_ERROR_COMPLETE_WORDS.has(nextWord);
1756
+ const reachedMargin = prevLen >= wrapWidth - 1;
1757
+ const isPhysicalWrap = !prevHadSpace && !nextHadSpace && prevEndedAlnum && nextStartsAlnum && reachedMargin && !isTwoCompleteWords;
1758
+ if (isPhysicalWrap) {
1759
+ message = `${message}${trimmed}`;
1760
+ } else {
1761
+ message = `${message} ${trimmed}`.trim();
1762
+ }
1763
+ }
1764
+ }
1765
+ const fullLine = errorId ? `\u26A0\uFE0F Antigravity error: ${message} (Error ID: ${errorId})` : `\u26A0\uFE0F Antigravity error: ${message}`;
1766
+ return { message, errorId, fullLine };
1767
+ }
1479
1768
  function extractQuestionHeader(snapshot) {
1480
1769
  const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
1481
1770
  const matches = Array.from(stripped.matchAll(/Question \d+\/\d+:\s*([^\n]+)/gi));
@@ -1524,6 +1813,12 @@ function parseApprovalSnapshot(snapshot) {
1524
1813
  return parseQuestionSnapshot(stripped, snapshot, questionHeader);
1525
1814
  }
1526
1815
  }
1816
+ if (kind === "system_error") {
1817
+ const systemError = extractSystemErrorPrompt(stripped);
1818
+ if (systemError) {
1819
+ return parseSystemErrorSnapshot(stripped, snapshot, systemError);
1820
+ }
1821
+ }
1527
1822
  return parseApprovalUISnapshot(stripped, snapshot);
1528
1823
  }
1529
1824
  function buildApprovalSemanticKey(candidate) {
@@ -1569,6 +1864,19 @@ function parseFileAccessSnapshot(stripped, originalSnapshot, prompt) {
1569
1864
  paneHash: hashPromptSnapshot(originalSnapshot)
1570
1865
  };
1571
1866
  }
1867
+ function parseSystemErrorSnapshot(stripped, originalSnapshot, prompt) {
1868
+ return {
1869
+ kind: "system_error",
1870
+ headerText: prompt.message,
1871
+ fullHeaderLine: prompt.fullLine,
1872
+ command: void 0,
1873
+ filePath: void 0,
1874
+ options: [],
1875
+ submitMap: {},
1876
+ body: prompt.errorId ? `Error ID: ${prompt.errorId}` : "",
1877
+ paneHash: hashPromptSnapshot(originalSnapshot)
1878
+ };
1879
+ }
1572
1880
  function parseApprovalUISnapshot(stripped, originalSnapshot) {
1573
1881
  const headerText = extractHeader(stripped);
1574
1882
  if (!headerText) return null;
@@ -4290,14 +4598,16 @@ var McpServer = class _McpServer {
4290
4598
  * Content is E2E-encrypted; the raise identity rides plaintext top-level.
4291
4599
  * Fail-closed on a missing session key — never cleartext.
4292
4600
  */
4293
- async emitSuppressedPromptCarrier(session, record) {
4294
- const bannerText = "\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.";
4601
+ async emitSuppressedPromptCarrier(session, record, customBannerText) {
4602
+ const bannerText = customBannerText || "\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.";
4295
4603
  const input = {
4296
4604
  sessionId: session.sessionId,
4297
4605
  type: import_codevibe_core5.EventType.NOTIFICATION,
4298
4606
  source: import_codevibe_core5.EventSource.DESKTOP,
4299
4607
  content: bannerText,
4300
- metadata: { e1SuppressedPrompt: true },
4608
+ metadata: {
4609
+ e1SuppressedPrompt: true
4610
+ },
4301
4611
  ...(0, import_codevibe_core5.raiseFieldsFromRecord)(record),
4302
4612
  notificationText: bannerText,
4303
4613
  timestamp: (0, import_codevibe_core5.prepareEventTimestamp)({ orderingKey: session.sessionId })
@@ -4318,7 +4628,7 @@ var McpServer = class _McpServer {
4318
4628
  * NOTIFICATION carrier. A null content-key always mints (can't prove a
4319
4629
  * re-observation) — never under-nag.
4320
4630
  */
4321
- raiseSuppressedBadge(session, contentKey) {
4631
+ raiseSuppressedBadge(session, contentKey, customBannerText) {
4322
4632
  if (contentKey) {
4323
4633
  const idxKey = `${session.sessionId}::${contentKey}`;
4324
4634
  const existingId = this.e1ContentKeyIndex.get(idxKey);
@@ -4327,7 +4637,13 @@ var McpServer = class _McpServer {
4327
4637
  }
4328
4638
  const record = this.newE1Raise(session.sessionId, false);
4329
4639
  if (contentKey) this.e1ContentKeyIndex.set(`${session.sessionId}::${contentKey}`, record.promptId);
4330
- this.emitSuppressedPromptCarrier(session, record).catch((e) => {
4640
+ if (customBannerText) {
4641
+ const entry = this.e1PromptRaises.get(record.promptId);
4642
+ if (entry) {
4643
+ entry.contentPlain = customBannerText;
4644
+ }
4645
+ }
4646
+ this.emitSuppressedPromptCarrier(session, record, customBannerText).catch((e) => {
4331
4647
  logger.error("E1: failed to emit suppressed-prompt badge carrier", {
4332
4648
  sessionId: session.sessionId,
4333
4649
  promptId: record.promptId,
@@ -4495,7 +4811,7 @@ var McpServer = class _McpServer {
4495
4811
  if (!await this.createEncryptedEvent(session, input)) return false;
4496
4812
  } else {
4497
4813
  rec.mobileActionable = false;
4498
- if (!await this.emitSuppressedPromptCarrier(session, rec)) return false;
4814
+ if (!await this.emitSuppressedPromptCarrier(session, rec, entry.contentPlain)) return false;
4499
4815
  }
4500
4816
  this.e1NeedsReRaise.delete(rec.promptId);
4501
4817
  logger.info("E1: re-raised prompt onto replacement session", {
@@ -4708,6 +5024,10 @@ var McpServer = class _McpServer {
4708
5024
  this.raiseSuppressedBadge(session, cand.snapshotHash);
4709
5025
  return;
4710
5026
  }
5027
+ if (parsed.kind === "system_error") {
5028
+ this.raiseSuppressedBadge(session, cand.snapshotHash, parsed.fullHeaderLine);
5029
+ return;
5030
+ }
4711
5031
  const activeConvId = this.pickActiveConversationForPrompt();
4712
5032
  if (!activeConvId) {
4713
5033
  logger.warn("Prompt candidate fired but no active conversation known", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-antigravity-plugin",
3
- "version": "2.0.29",
3
+ "version": "2.0.31",
4
4
  "description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
5
5
  "main": "dist/server.js",
6
6
  "codevibe": {
@@ -46,7 +46,7 @@
46
46
  "node": ">=22.0.0"
47
47
  },
48
48
  "dependencies": {
49
- "@quantiya/codevibe-core": "^2.0.26",
49
+ "@quantiya/codevibe-core": "^2.0.27",
50
50
  "chokidar": "^5.0.0",
51
51
  "dotenv": "^16.6.1",
52
52
  "express": "^5.1.0",