@quantiya/codevibe-antigravity-plugin 2.0.72 → 2.0.74

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 +395 -23
  2. package/package.json +2 -2
package/dist/server.js CHANGED
@@ -38,10 +38,18 @@ __export(server_exports, {
38
38
  classifyEntrypoint: () => classifyEntrypoint,
39
39
  classifyTmuxHasSessionError: () => classifyTmuxHasSessionError,
40
40
  commandDigest: () => commandDigest,
41
+ detectActivePromptKind: () => detectActivePromptKind,
42
+ extractSystemErrorPrompt: () => extractSystemErrorPrompt,
41
43
  generateLaunchSessionId: () => generateLaunchSessionId,
42
44
  getActiveConversationFromCliLog: () => getActiveConversationFromCliLog,
45
+ isAgyStatusFooterLine: () => isAgyStatusFooterLine,
46
+ isComposedStatusFooter: () => isComposedStatusFooter,
47
+ isSystemErrorActiveFromIndex: () => isSystemErrorActiveFromIndex,
48
+ looksLikePromptSnapshot: () => looksLikePromptSnapshot,
49
+ parseApprovalSnapshot: () => parseApprovalSnapshot,
43
50
  parseMaybeJson: () => parseMaybeJson,
44
51
  readTranscriptFirstType: () => readTranscriptFirstType,
52
+ reconstructFooter: () => reconstructFooter,
45
53
  runForeignSpawnIdle: () => runForeignSpawnIdle
46
54
  });
47
55
  module.exports = __toCommonJS(server_exports);
@@ -2497,7 +2505,7 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
2497
2505
  }
2498
2506
  const safeLines = Math.max(1, Math.floor(lines));
2499
2507
  const escapedSession = escapeShellArg(this.sessionName);
2500
- const cmd = `tmux capture-pane -p -e -S -${safeLines} -t '${escapedSession}'`;
2508
+ const cmd = `tmux capture-pane -p -e -J -S -${safeLines} -t '${escapedSession}'`;
2501
2509
  try {
2502
2510
  const { stdout } = await execAsync(cmd);
2503
2511
  return stdout;
@@ -2809,19 +2817,134 @@ var TmuxPaneObserver = class _TmuxPaneObserver extends import_events2.EventEmitt
2809
2817
  function looksLikePromptDelta(chunk) {
2810
2818
  return (
2811
2819
  // Approval UI signatures
2812
- /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)
2820
+ /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|quota\b|upgrade your subscription|increase your limits|resets in\s+\d|credits balance is too low|out of credits|rate[- ]limit|capacity exhausted|exhausted your quota|internal error|currently unreachable|network issue connecting|authentication expired|authentication issue|terminated due to error|invalid output|invalid tool call)/i.test(chunk)
2813
2821
  );
2814
2822
  }
2823
+ function isNonSystemErrorWarningLine(trimmed) {
2824
+ return /^(?:⚠️|⚠\uFE0F?)\s*(?:Approval Required|Overwrite settings\.json|Settings Error|Statusline error|alt screen unavailable|warning:|back\b|Hooks run shell commands|Confirm the command is safe)/i.test(trimmed);
2825
+ }
2815
2826
  function isSystemErrorBannerLine(line) {
2827
+ if (!line) return false;
2828
+ if (!/^(?:⚠️|⚠\uFE0F?)\s+\S/.test(line)) return false;
2816
2829
  const trimmed = line.trim();
2817
- if (!trimmed) return false;
2818
- if (!/^(?:⚠️|⚠\uFE0F?)\s+/.test(trimmed)) return false;
2830
+ if (isNonSystemErrorWarningLine(trimmed)) return false;
2819
2831
  if (/Our servers are experiencing/i.test(trimmed)) return true;
2820
2832
  if (/The stream was interrupted/i.test(trimmed)) return true;
2821
- if (/The model is overloaded/i.test(trimmed)) return true;
2822
- 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;
2833
+ if (/The model (?:is|API is currently) overloaded/i.test(trimmed)) return true;
2834
+ if (/The model (?:encountered an internal error|is currently unreachable|produced an invalid)/i.test(trimmed)) return true;
2835
+ if (/Model produced invalid output/i.test(trimmed)) return true;
2836
+ if (/There was a network issue connecting to the server/i.test(trimmed)) return true;
2837
+ if (/Authentication (?:expired|issue:)/i.test(trimmed)) return true;
2838
+ if (/Agent execution terminated due to error/i.test(trimmed)) return true;
2839
+ if (/(?:high[- ]traffic|try again in a minute|try again later|stream was interrupted|stream interrupted|overloaded|unexpected error|error id:|quota\b|upgrade your subscription|increase your limits|resets in\s+\d|credits balance is too low|out of credits|rate[- ]limit|capacity exhausted|exhausted your quota)/i.test(trimmed)) return true;
2823
2840
  return false;
2824
2841
  }
2842
+ function isSystemErrorBannerLineInContext(lines, idx) {
2843
+ const raw = lines[idx];
2844
+ if (raw === void 0) return false;
2845
+ if (isSystemErrorBannerLine(raw)) return true;
2846
+ if (!/^(?:⚠️|⚠\uFE0F?)\s+\S/.test(raw)) return false;
2847
+ const trimmed = raw.trim();
2848
+ if (isNonSystemErrorWarningLine(trimmed)) return false;
2849
+ for (let j = idx + 1; j < Math.min(lines.length, idx + 7); j++) {
2850
+ const nextRaw = lines[j];
2851
+ const nextTrimmed = (nextRaw || "").trim();
2852
+ if (!nextTrimmed) break;
2853
+ if (/^[─━│╭╰╮╯\s_-]*$/.test(nextTrimmed)) break;
2854
+ if (/^[│\s]*[❯>]/.test(nextTrimmed)) break;
2855
+ if (/^(?:⚠️|⚠\uFE0F?)\s+/.test(nextTrimmed)) break;
2856
+ if (/Requesting permission for:|Question \d+\/\d+:|^\s*File access\s*$|^[>\s]*[1-9]\. \S/i.test(nextTrimmed)) break;
2857
+ const idMatch = nextTrimmed.match(/^Error ID:\s*([0-9a-fA-F-]*)$/i);
2858
+ if (idMatch) {
2859
+ if (/[0-9a-fA-F]/.test(idMatch[1])) return true;
2860
+ const following = (lines[j + 1] || "").trim();
2861
+ if (/^[0-9a-fA-F-]+$/.test(following) && /[0-9a-fA-F]/.test(following)) return true;
2862
+ break;
2863
+ }
2864
+ }
2865
+ return false;
2866
+ }
2867
+ function isComposedStatusFooter(trimmed) {
2868
+ if (!trimmed || !trimmed.includes("\xB7")) return false;
2869
+ const segments = trimmed.split(/\s*·\s*/).map((s) => s.trim()).filter(Boolean);
2870
+ if (segments.length < 1) return false;
2871
+ const isSegmentValid = (seg) => {
2872
+ if (/^(?:auto|accept-edits|plan|best-of-n)$/i.test(seg)) return true;
2873
+ if (/^(?:low|medium|high|xhigh|max|minimal)$/i.test(seg)) return true;
2874
+ if (/^(?:Gemini|Claude|GPT|Gemma)(?:[- ][A-Za-z0-9._()/-]+)*$/i.test(seg)) {
2875
+ const words = seg.split(/\s+/);
2876
+ if (words.length <= 6 && !/^(?:is|was|are|completed|returned|failed|error|running|waiting|great)\b/i.test(words[1] || "")) {
2877
+ return true;
2878
+ }
2879
+ }
2880
+ if (/^(?:\(initializing\.\.\.\)|\d+\s+task\(s\)|\/tasks|\d+\s+subagent\s*\(s\)|AI:\s+(?:\d+\s+\(\/credits to add more\)|Out of credits)|AI Credits:\s+\d+|Quota:\s*\d+(?:\.\d+)?%)$/i.test(seg)) {
2881
+ return true;
2882
+ }
2883
+ return false;
2884
+ };
2885
+ return segments.every(isSegmentValid);
2886
+ }
2887
+ function isAgyStatusFooterLine(trimmed) {
2888
+ if (!trimmed) return false;
2889
+ if (/^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(trimmed)) {
2890
+ return true;
2891
+ }
2892
+ if (/^(?:\(initializing\.\.\.\)|\d+\s+task\(s\)\s+·\s+\/tasks|\d+\s+subagent\s*\(s\)|AI:\s+(?:\d+\s+\(\/credits to add more\)|Out of credits)|AI Credits:\s+\d+|Quota:\s*\d+(?:\.\d+)?%)$/i.test(trimmed)) {
2893
+ return true;
2894
+ }
2895
+ if (isComposedStatusFooter(trimmed)) {
2896
+ return true;
2897
+ }
2898
+ return false;
2899
+ }
2900
+ function reconstructFooter(rawLines) {
2901
+ const cleaned = rawLines.map((l) => l.trim()).filter(Boolean);
2902
+ if (cleaned.length === 0) return "";
2903
+ if (cleaned.length === 1) return cleaned[0].replace(/\s*·\s*/g, " \xB7 ").trim();
2904
+ if (cleaned.length > 4) {
2905
+ return cleaned.join(" ").replace(/\s*·\s*/g, " \xB7 ").trim();
2906
+ }
2907
+ const hasMiddleDot = cleaned.some((l) => l.includes("\xB7"));
2908
+ const hasFooterKeyword = cleaned.some(
2909
+ (l) => /^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(l) || /\b(?:auto|accept-edits|plan|Gemini|Claude|GPT|Gemma|high|low|subagent|quota|credits)\b/i.test(l)
2910
+ );
2911
+ if (!hasMiddleDot && !hasFooterKeyword) {
2912
+ return cleaned.join(" ").replace(/\s*·\s*/g, " \xB7 ").trim();
2913
+ }
2914
+ const unpaddedIndices = [];
2915
+ for (let i = 1; i < rawLines.length; i++) {
2916
+ const prevEndsSpace = /\s$/.test(rawLines[i - 1]);
2917
+ const nextStartsSpace = /^\s/.test(rawLines[i]);
2918
+ if (!prevEndsSpace && !nextStartsSpace) {
2919
+ unpaddedIndices.push(i);
2920
+ }
2921
+ }
2922
+ const cappedIndices = unpaddedIndices.slice(0, 3);
2923
+ const numCombos = 1 << cappedIndices.length;
2924
+ let fallbackCandidate = "";
2925
+ for (let mask = 0; mask < numCombos; mask++) {
2926
+ let cur = rawLines[0].trim();
2927
+ for (let i = 1; i < rawLines.length; i++) {
2928
+ const unpaddedPos = cappedIndices.indexOf(i);
2929
+ let useSpace = true;
2930
+ if (unpaddedPos !== -1) {
2931
+ useSpace = Boolean(mask & 1 << unpaddedPos);
2932
+ } else if (unpaddedIndices.includes(i)) {
2933
+ useSpace = true;
2934
+ }
2935
+ const nextPart = rawLines[i].trim();
2936
+ cur = useSpace ? `${cur} ${nextPart}` : `${cur}${nextPart}`;
2937
+ }
2938
+ const normalized = cur.replace(/\s*·\s*/g, " \xB7 ").trim();
2939
+ if (isComposedStatusFooter(normalized)) {
2940
+ return normalized;
2941
+ }
2942
+ if (mask === numCombos - 1) {
2943
+ fallbackCandidate = normalized;
2944
+ }
2945
+ }
2946
+ return fallbackCandidate || cleaned.join(" ").replace(/\s*·\s*/g, " \xB7 ").trim();
2947
+ }
2825
2948
  function isSystemErrorActiveFromIndex(lines, errorIdx) {
2826
2949
  let inHeaderBlock = true;
2827
2950
  let sawErrorIdLine = false;
@@ -2834,7 +2957,7 @@ function isSystemErrorActiveFromIndex(lines, errorIdx) {
2834
2957
  if (/^[│\s]*[❯>]\s+\S/.test(trimmed)) return false;
2835
2958
  if (/^[▶▸\s]*Thought for\s+\d+/i.test(trimmed)) return false;
2836
2959
  if (/^Thinking\b/i.test(trimmed)) return false;
2837
- if (/^[●\s]*(?:[A-Za-z_-]+)\(/.test(trimmed)) return false;
2960
+ if (/^●\s*(?:[A-Za-z_-]+)\(/.test(trimmed)) return false;
2838
2961
  if (/Requesting permission for:/i.test(trimmed)) return false;
2839
2962
  if (/Question \d+\/\d+:/i.test(trimmed)) return false;
2840
2963
  if (/^\s*File access\s*$/i.test(trimmed)) return false;
@@ -2859,15 +2982,35 @@ function isSystemErrorActiveFromIndex(lines, errorIdx) {
2859
2982
  inHeaderBlock = false;
2860
2983
  continue;
2861
2984
  }
2862
- if (/^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(trimmed)) {
2863
- inHeaderBlock = false;
2864
- continue;
2985
+ if (!inHeaderBlock) {
2986
+ const remainingRaw = lines.slice(j).filter((l) => Boolean(l.trim()));
2987
+ if (remainingRaw.length > 0) {
2988
+ const hasTurnResumption = remainingRaw.some(
2989
+ (l) => /^[│\s]*[❯>]\s+\S/.test(l.trim()) || /^[▶▸\s]*Thought for\s+\d+/i.test(l.trim()) || /^Thinking\b/i.test(l.trim()) || /^●\s*(?:[A-Za-z_-]+)\(/.test(l.trim())
2990
+ );
2991
+ if (!hasTurnResumption) {
2992
+ if (remainingRaw.every((l) => isAgyStatusFooterLine(l.trim()))) {
2993
+ return true;
2994
+ }
2995
+ if (remainingRaw.length <= 4) {
2996
+ const hasFooterMarkers = remainingRaw.some(
2997
+ (l) => l.includes("\xB7") || /^\s*(?:\?\s+for shortcuts|esc to cancel)\b/i.test(l.trim()) || /\b(?:auto|accept-edits|plan|Gemini|Claude|GPT|Gemma|high|low|subagent|quota|credits)\b/i.test(l)
2998
+ );
2999
+ if (hasFooterMarkers) {
3000
+ const reconstructed = reconstructFooter(remainingRaw);
3001
+ if (isComposedStatusFooter(reconstructed)) {
3002
+ return true;
3003
+ }
3004
+ }
3005
+ }
3006
+ }
3007
+ }
2865
3008
  }
2866
- if (/^\s*(?:accept-edits\s+·\s+)?Gemini\s+[\d.]+\s+(?:Flash|Pro)\b/i.test(trimmed)) {
3009
+ if (isAgyStatusFooterLine(trimmed)) {
2867
3010
  inHeaderBlock = false;
2868
3011
  continue;
2869
3012
  }
2870
- if (inHeaderBlock && j - errorIdx <= 4) {
3013
+ if (inHeaderBlock && j - errorIdx <= 6) {
2871
3014
  continue;
2872
3015
  }
2873
3016
  return false;
@@ -2979,7 +3122,7 @@ function detectActivePromptKind(snapshot) {
2979
3122
  lastFileAccessLineIdx = i;
2980
3123
  }
2981
3124
  }
2982
- if (lastSystemErrorLineIdx < 0 && isSystemErrorBannerLine(line)) {
3125
+ if (lastSystemErrorLineIdx < 0 && isSystemErrorBannerLineInContext(lines, i)) {
2983
3126
  lastSystemErrorLineIdx = i;
2984
3127
  }
2985
3128
  if (lastApprovalLineIdx >= 0 && lastQuestionLineIdx >= 0 && lastFileAccessLineIdx >= 0 && lastSystemErrorLineIdx >= 0) break;
@@ -3035,7 +3178,7 @@ function detectActivePromptKind(snapshot) {
3035
3178
  }
3036
3179
  return "approval";
3037
3180
  }
3038
- var SYSTEM_ERROR_COMPLETE_WORDS = /* @__PURE__ */ new Set([
3181
+ var SYSTEM_ERROR_KNOWN_WORDS = /* @__PURE__ */ new Set([
3039
3182
  "our",
3040
3183
  "servers",
3041
3184
  "server",
@@ -3056,6 +3199,7 @@ var SYSTEM_ERROR_COMPLETE_WORDS = /* @__PURE__ */ new Set([
3056
3199
  "an",
3057
3200
  "unexpected",
3058
3201
  "error",
3202
+ "errors",
3059
3203
  "occurred",
3060
3204
  "the",
3061
3205
  "model",
@@ -3076,6 +3220,7 @@ var SYSTEM_ERROR_COMPLETE_WORDS = /* @__PURE__ */ new Set([
3076
3220
  "your",
3077
3221
  "connection",
3078
3222
  "timeout",
3223
+ "timeouts",
3079
3224
  "temporary",
3080
3225
  "unavailable",
3081
3226
  "service",
@@ -3087,26 +3232,222 @@ var SYSTEM_ERROR_COMPLETE_WORDS = /* @__PURE__ */ new Set([
3087
3232
  "internal",
3088
3233
  "failure",
3089
3234
  "request",
3235
+ "requests",
3090
3236
  "response",
3091
3237
  "refused",
3092
3238
  "reset",
3239
+ "resets",
3093
3240
  "network",
3094
3241
  "operation",
3095
3242
  "aborted",
3096
3243
  "cancelled",
3097
3244
  "rate",
3098
3245
  "limit",
3246
+ "limits",
3099
3247
  "limited",
3100
3248
  "throttle",
3101
- "throttled"
3249
+ "throttled",
3250
+ "individual",
3251
+ "quota",
3252
+ "reached",
3253
+ "upgrade",
3254
+ "subscription",
3255
+ "to",
3256
+ "increase",
3257
+ "have",
3258
+ "this",
3259
+ "image",
3260
+ "generation",
3261
+ "has",
3262
+ "been",
3263
+ "ai",
3264
+ "credits",
3265
+ "balance",
3266
+ "too",
3267
+ "low",
3268
+ "api",
3269
+ "currently",
3270
+ "and",
3271
+ "may",
3272
+ "intermittent",
3273
+ "encountered",
3274
+ "unreachable",
3275
+ "there",
3276
+ "issue",
3277
+ "connecting",
3278
+ "authentication",
3279
+ "expired",
3280
+ "log",
3281
+ "back",
3282
+ "check",
3283
+ "gcert",
3284
+ "reload",
3285
+ "window",
3286
+ "agent",
3287
+ "execution",
3288
+ "terminated",
3289
+ "due",
3290
+ "of",
3291
+ "out",
3292
+ "produced",
3293
+ "invalid",
3294
+ "output",
3295
+ "input",
3296
+ "tool",
3297
+ "call",
3298
+ "workspace",
3299
+ "policy",
3300
+ "prevented",
3301
+ "invocation",
3302
+ "for",
3303
+ "project",
3304
+ "blocked",
3305
+ "unauthorized",
3306
+ "administrator",
3307
+ "access",
3308
+ "denied",
3309
+ "because",
3310
+ "organizational",
3311
+ "restrictions",
3312
+ "prohibit",
3313
+ "requested",
3314
+ "reconnect",
3315
+ "upstream",
3316
+ "downstream",
3317
+ "during",
3318
+ "maintenance",
3319
+ "contact",
3320
+ "support",
3321
+ "bad",
3322
+ "from",
3323
+ "closed",
3324
+ "update",
3325
+ "updates",
3326
+ "within",
3327
+ "without",
3328
+ "cannot",
3329
+ "itself",
3330
+ "themselves",
3331
+ "fallback",
3332
+ "fallbacks",
3333
+ "subagent",
3334
+ "subagents",
3335
+ "billing",
3336
+ "configuration",
3337
+ "scheduled",
3338
+ "current",
3339
+ "user",
3340
+ "by",
3341
+ "could",
3342
+ "not",
3343
+ "connect",
3344
+ "rejected",
3345
+ "system",
3346
+ "it",
3347
+ "eu",
3348
+ "endpoint",
3349
+ "org",
3350
+ "organization",
3351
+ "set",
3352
+ "insufficient",
3353
+ "permissions",
3354
+ "permission",
3355
+ "valid",
3356
+ "retry",
3357
+ "projects",
3358
+ "states",
3359
+ "regions",
3360
+ "authorization",
3361
+ "authorized",
3362
+ "reauthorization",
3363
+ "reauthorized",
3364
+ "renew",
3365
+ "renewed"
3366
+ ]);
3367
+ var COMMON_COMPLETE_WORDS = /* @__PURE__ */ new Set([
3368
+ "in",
3369
+ "on",
3370
+ "at",
3371
+ "to",
3372
+ "by",
3373
+ "for",
3374
+ "of",
3375
+ "or",
3376
+ "and",
3377
+ "the",
3378
+ "a",
3379
+ "an",
3380
+ "is",
3381
+ "it",
3382
+ "be",
3383
+ "as",
3384
+ "if",
3385
+ "so",
3386
+ "valid",
3387
+ "set",
3388
+ "retry",
3389
+ "user",
3390
+ "projects",
3391
+ "policy",
3392
+ "endpoint",
3393
+ "org",
3394
+ "from",
3395
+ "with",
3396
+ "out",
3397
+ "up",
3398
+ "down",
3399
+ "you",
3400
+ "were",
3401
+ "are",
3402
+ "was",
3403
+ "your",
3404
+ "due",
3405
+ "right",
3406
+ "now",
3407
+ "again",
3408
+ "later",
3409
+ "this",
3410
+ "that",
3411
+ "model",
3412
+ "server",
3413
+ "error",
3414
+ "stream",
3415
+ "task",
3416
+ "quota",
3417
+ "limits",
3418
+ "credits",
3419
+ "billing",
3420
+ "configuration",
3421
+ "system",
3422
+ "access",
3423
+ "denied",
3424
+ "blocked",
3425
+ "rejected",
3426
+ "states",
3427
+ "regions",
3428
+ "renew"
3102
3429
  ]);
3430
+ var COMPOUND_WORDS = /* @__PURE__ */ new Set([
3431
+ "upstream",
3432
+ "downstream",
3433
+ "within",
3434
+ "without",
3435
+ "cannot",
3436
+ "itself",
3437
+ "themselves",
3438
+ "fallback",
3439
+ "fallbacks"
3440
+ ]);
3441
+ function isDurationToken(word) {
3442
+ return /^\d+[dhms](?:\d+[dhms])*$/i.test(word);
3443
+ }
3103
3444
  function extractSystemErrorPrompt(snapshot) {
3104
3445
  const stripped = snapshot.replace(ANSI_ESCAPE_REGEX, "");
3105
3446
  const lines = stripped.split("\n");
3106
3447
  let bannerIdx = -1;
3107
3448
  let rawMessage = "";
3108
3449
  for (let i = lines.length - 1; i >= 0; i--) {
3109
- if (isSystemErrorBannerLine(lines[i])) {
3450
+ if (isSystemErrorBannerLineInContext(lines, i)) {
3110
3451
  bannerIdx = i;
3111
3452
  rawMessage = lines[i].trim();
3112
3453
  break;
@@ -3115,14 +3456,15 @@ function extractSystemErrorPrompt(snapshot) {
3115
3456
  if (bannerIdx < 0) return null;
3116
3457
  let message = rawMessage.replace(/^(?:⚠️|⚠\uFE0F?)\s*/, "").trim();
3117
3458
  let wrapWidth = Array.from(lines[bannerIdx].trimEnd()).length;
3118
- for (let j = bannerIdx + 1; j < Math.min(lines.length, bannerIdx + 6); j++) {
3459
+ for (let j = bannerIdx + 1; j < Math.min(lines.length, bannerIdx + 8); j++) {
3119
3460
  const trimmed = lines[j].trim();
3120
3461
  if (!trimmed || /^Error ID:/i.test(trimmed) || /^[─━│╭╰╮╯\s_-]*$/.test(trimmed) || /^[│\s]*[❯>]/.test(trimmed)) break;
3121
3462
  const len = Array.from(lines[j].trimEnd()).length;
3122
3463
  if (len > wrapWidth) wrapWidth = len;
3123
3464
  }
3465
+ wrapWidth = Math.max(30, wrapWidth);
3124
3466
  let errorId;
3125
- for (let j = bannerIdx + 1; j < Math.min(lines.length, bannerIdx + 6); j++) {
3467
+ for (let j = bannerIdx + 1; j < Math.min(lines.length, bannerIdx + 8); j++) {
3126
3468
  const trimmed = lines[j].trim();
3127
3469
  if (!trimmed) continue;
3128
3470
  if (/^[─━│╭╰╮╯\s_-]*$/.test(trimmed)) break;
@@ -3147,10 +3489,17 @@ function extractSystemErrorPrompt(snapshot) {
3147
3489
  break;
3148
3490
  }
3149
3491
  const prevRaw = lines[j - 1];
3492
+ const prevDurationMatch = message.match(/([a-zA-Z0-9]+)$/);
3493
+ const nextDurationMatch = trimmed.match(/^([a-zA-Z0-9]+)/);
3494
+ const prevDurationWord = prevDurationMatch ? prevDurationMatch[1] : "";
3495
+ const nextDurationWord = nextDurationMatch ? nextDurationMatch[1] : "";
3496
+ const isDurationSplit = Boolean(prevDurationWord && nextDurationWord) && !/[.,!?:;)]$/.test(message) && !/^[.,!?:;(]/.test(trimmed) && isDurationToken((prevDurationWord + nextDurationWord).toLowerCase());
3150
3497
  if (message.endsWith("-") || trimmed.startsWith("-")) {
3151
3498
  message = `${message}${trimmed}`;
3152
3499
  } else if (/^[.,!?:;]/.test(trimmed)) {
3153
3500
  message = `${message}${trimmed}`;
3501
+ } else if (isDurationSplit) {
3502
+ message = `${message}${trimmed}`;
3154
3503
  } else {
3155
3504
  const prevEndedAlnum = /[a-zA-Z0-9]$/.test(message);
3156
3505
  const nextStartsAlnum = /^[a-zA-Z0-9]/.test(trimmed);
@@ -3159,9 +3508,12 @@ function extractSystemErrorPrompt(snapshot) {
3159
3508
  const prevLen = Array.from(prevRaw.trimEnd()).length;
3160
3509
  const prevWord = ((message.match(/([a-zA-Z0-9]+)$/) || [])[1] || "").toLowerCase();
3161
3510
  const nextWord = ((trimmed.match(/^([a-zA-Z0-9]+)/) || [])[1] || "").toLowerCase();
3162
- const isTwoCompleteWords = SYSTEM_ERROR_COMPLETE_WORDS.has(prevWord) && SYSTEM_ERROR_COMPLETE_WORDS.has(nextWord);
3511
+ const combinedWord = prevWord + nextWord;
3163
3512
  const reachedMargin = prevLen >= wrapWidth - 1;
3164
- const isPhysicalWrap = !prevHadSpace && !nextHadSpace && prevEndedAlnum && nextStartsAlnum && reachedMargin && !isTwoCompleteWords;
3513
+ const bothCompleteWords = COMMON_COMPLETE_WORDS.has(prevWord) && COMMON_COMPLETE_WORDS.has(nextWord);
3514
+ const isCompound = COMPOUND_WORDS.has(combinedWord);
3515
+ const formsKnownWord = SYSTEM_ERROR_KNOWN_WORDS.has(combinedWord);
3516
+ const isPhysicalWrap = !prevHadSpace && !nextHadSpace && prevEndedAlnum && nextStartsAlnum && reachedMargin && formsKnownWord && (!bothCompleteWords || isCompound);
3165
3517
  if (isPhysicalWrap) {
3166
3518
  message = `${message}${trimmed}`;
3167
3519
  } else {
@@ -3180,7 +3532,10 @@ function extractQuestionHeader(snapshot) {
3180
3532
  return { fullLine: last[0].trim(), body: last[1].trim() };
3181
3533
  }
3182
3534
  function hashPromptSnapshot(snapshot) {
3183
- const normalized = snapshot.replace(ANSI_ESCAPE_REGEX, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
3535
+ let normalized = snapshot.replace(ANSI_ESCAPE_REGEX, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
3536
+ if (detectActivePromptKind(normalized) === "system_error") {
3537
+ normalized = normalized.replace(/\bResets\s+in\s+\d+[dhms](?:\s*\d+[dhms])*\.?/gi, "Resets in <duration>");
3538
+ }
3184
3539
  return (0, import_crypto.createHash)("sha256").update(normalized).digest("hex");
3185
3540
  }
3186
3541
  var ANSI_ESCAPE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
@@ -6574,7 +6929,15 @@ var McpServer = class _McpServer {
6574
6929
  if (contentKey) {
6575
6930
  const idxKey = `${session.sessionId}::${contentKey}`;
6576
6931
  const existingId = this.e1ContentKeyIndex.get(idxKey);
6577
- if (existingId && this.e1PromptRaises.has(existingId)) return;
6932
+ if (existingId && this.e1PromptRaises.has(existingId)) {
6933
+ if (customBannerText) {
6934
+ const entry = this.e1PromptRaises.get(existingId);
6935
+ if (entry) {
6936
+ entry.contentPlain = customBannerText;
6937
+ }
6938
+ }
6939
+ return;
6940
+ }
6578
6941
  if (existingId) this.e1ContentKeyIndex.delete(idxKey);
6579
6942
  }
6580
6943
  const record = this.newE1Raise(session.sessionId, false);
@@ -6976,7 +7339,8 @@ var McpServer = class _McpServer {
6976
7339
  return;
6977
7340
  }
6978
7341
  if (parsed.kind === "system_error") {
6979
- this.raiseSuppressedBadge(session, cand.snapshotHash, parsed.fullHeaderLine);
7342
+ const errorContentKey = parsed.body.startsWith("Error ID: ") ? `system_error::${parsed.body}` : `system_error::${parsed.headerText.replace(/\bResets in \d+[dhms](?:\d+[dhms])*\.?/gi, "Resets in <duration>")}`;
7343
+ this.raiseSuppressedBadge(session, errorContentKey, parsed.fullHeaderLine);
6980
7344
  return;
6981
7345
  }
6982
7346
  const activeConvId = this.pickActiveConversationForPrompt();
@@ -7936,9 +8300,17 @@ var __testing = {
7936
8300
  classifyEntrypoint,
7937
8301
  classifyTmuxHasSessionError,
7938
8302
  commandDigest,
8303
+ detectActivePromptKind,
8304
+ extractSystemErrorPrompt,
7939
8305
  generateLaunchSessionId,
7940
8306
  getActiveConversationFromCliLog,
8307
+ isAgyStatusFooterLine,
8308
+ isComposedStatusFooter,
8309
+ isSystemErrorActiveFromIndex,
8310
+ looksLikePromptSnapshot,
8311
+ parseApprovalSnapshot,
7941
8312
  parseMaybeJson,
7942
8313
  readTranscriptFirstType,
8314
+ reconstructFooter,
7943
8315
  runForeignSpawnIdle
7944
8316
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-antigravity-plugin",
3
- "version": "2.0.72",
3
+ "version": "2.0.74",
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.67",
49
+ "@quantiya/codevibe-core": "^2.0.68",
50
50
  "chokidar": "^5.0.0",
51
51
  "dotenv": "^16.6.1",
52
52
  "express": "^5.1.0",