paseo-acp-agy 1.2.16 → 1.3.1

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/README.md CHANGED
@@ -92,24 +92,29 @@ To include `paseo-acp-agy` in Paseo's built-in provider store (`ACP_PROVIDER_CAT
92
92
 
93
93
  ## Plan Usage & Context Window Meter Setup
94
94
 
95
- To display the **Google Antigravity** quota breakdown in Paseo's `Settings -> Usage` (Plan usage) and activate the real-time context-window meter ("bolinha") in the chat composer:
95
+ ### Recommended (Paseo >= 0.11.0): Official Update-Proof Paseo Plugin
96
96
 
97
- ```bash
98
- npx -y paseo-acp-agy setup
99
- ```
97
+ Install the bundled **`antigravity-telemetry`** Paseo plugin once. It registers in `~/.paseo/config.json` and survives all future `Paseo.app`, Web, iPhone, and CLI updates without modifying signed `.app` bundles:
100
98
 
101
- Or if installed globally:
102
99
  ```bash
103
- paseo-acp-agy setup
100
+ paseo plugin install "$(paseo-acp-agy --plugin-path)"
104
101
  ```
105
102
 
106
- Then restart your Paseo app or daemon:
103
+ What this provides across Desktop, Web, and Mobile (iPhone):
104
+ - Native **Google Antigravity** quota gauges in Paseo's Sidebar and `Settings -> Usage` (`5h` window, weekly pool, credits)
105
+ - Live per-agent **Composer Pill** (`$cost · ctx X% · 5h Y%`) with interactive Popover / Mobile Bottom Sheet
106
+ - Dedicated **Antigravity Telemetry** workspace panel, Command Center action, and `/agy-telemetry` slash command
107
+ - Combined with native ACP `usage_update` notifications so Paseo's built-in `ContextWindowMeter` ("bolinha") stays deterministic on every session open, resume, model switch, and turn completion
108
+
109
+ ### Standalone Server Patch (Optional, Linux / Paseo < 0.11.0)
110
+
111
+ For standalone `@getpaseo/cli` servers where you prefer direct server-level patching:
112
+
107
113
  ```bash
114
+ paseo-acp-agy setup
108
115
  paseo daemon restart
109
116
  ```
110
117
 
111
- > **Note**: `paseo-acp-agy` also checks and auto-configures your local Paseo server automatically whenever it launches via `--acp`. Running `setup` manually ensures your existing daemon instance picks up the telemetry hooks immediately upon restart.
112
-
113
118
  ---
114
119
 
115
120
  ## Requirements
@@ -11,6 +11,7 @@ export declare class ACPServer extends EventEmitter {
11
11
  private rl;
12
12
  private isRunning;
13
13
  private pendingRequests;
14
+ private usagePublishTimers;
14
15
  private nextRequestId;
15
16
  constructor(options?: {
16
17
  input?: Readable;
@@ -27,6 +28,14 @@ export declare class ACPServer extends EventEmitter {
27
28
  sendRequest<T = any>(method: string, params: unknown): Promise<T>;
28
29
  private publishCommands;
29
30
  private publishUsageUpdate;
31
+ private clearUsagePublishTimers;
32
+ /**
33
+ * Schedules coalesced, unref'd usage_update notifications so ACP hosts (like Paseo)
34
+ * deterministically receive context window & session telemetry:
35
+ * - after awaiting newSession/loadSession + applyConfiguredOverrides() + session.subscribe()
36
+ * - after turn_completed overwrites or initializes currentTurnUsage in Paseo 0.10..0.13+
37
+ */
38
+ private scheduleUsagePublishBurst;
30
39
  private sessionState;
31
40
  private requireSession;
32
41
  private validateModel;
@@ -6,7 +6,7 @@ import { SessionManager } from "./session.js";
6
6
  import { executeSlashCommand, AVAILABLE_SLASH_COMMANDS } from "./slash-commands.js";
7
7
  import { getShortVersion } from "./version.js";
8
8
  import { resolveDefaultAgyBinary } from "./antigravity-process.js";
9
- import { loadConversationTranscript } from "./transcript.js";
9
+ import { loadConversationTranscript, estimateReplayContextTokens } from "./transcript.js";
10
10
  import { normalizeToolCall, normalizeToolResult, extractPlanEntries } from "./tool-formatter.js";
11
11
  import { McpBridge } from "./mcp-bridge.js";
12
12
  function splitModelAndEffort(modelInput) {
@@ -30,6 +30,7 @@ export class ACPServer extends EventEmitter {
30
30
  rl = null;
31
31
  isRunning = false;
32
32
  pendingRequests = new Map();
33
+ usagePublishTimers = new Map();
33
34
  nextRequestId = 1;
34
35
  constructor(options = {}) {
35
36
  super();
@@ -138,6 +139,53 @@ export class ACPServer extends EventEmitter {
138
139
  },
139
140
  });
140
141
  }
142
+ clearUsagePublishTimers(sessionId) {
143
+ if (sessionId !== undefined) {
144
+ const timers = this.usagePublishTimers.get(sessionId);
145
+ if (timers) {
146
+ for (const t of timers)
147
+ clearTimeout(t);
148
+ this.usagePublishTimers.delete(sessionId);
149
+ }
150
+ return;
151
+ }
152
+ for (const timers of this.usagePublishTimers.values()) {
153
+ for (const t of timers)
154
+ clearTimeout(t);
155
+ }
156
+ this.usagePublishTimers.clear();
157
+ }
158
+ /**
159
+ * Schedules coalesced, unref'd usage_update notifications so ACP hosts (like Paseo)
160
+ * deterministically receive context window & session telemetry:
161
+ * - after awaiting newSession/loadSession + applyConfiguredOverrides() + session.subscribe()
162
+ * - after turn_completed overwrites or initializes currentTurnUsage in Paseo 0.10..0.13+
163
+ */
164
+ scheduleUsagePublishBurst(sessionId, delaysMs) {
165
+ this.clearUsagePublishTimers(sessionId);
166
+ if (!this.isRunning || delaysMs.length === 0)
167
+ return;
168
+ const timers = new Set();
169
+ this.usagePublishTimers.set(sessionId, timers);
170
+ for (const delayMs of delaysMs) {
171
+ const timer = setTimeout(() => {
172
+ timers.delete(timer);
173
+ if (timers.size === 0) {
174
+ this.usagePublishTimers.delete(sessionId);
175
+ }
176
+ if (!this.isRunning)
177
+ return;
178
+ const current = this.sessionManager.getSession(sessionId);
179
+ if (current) {
180
+ this.publishUsageUpdate(current);
181
+ }
182
+ }, delayMs);
183
+ if (typeof timer.unref === "function") {
184
+ timer.unref();
185
+ }
186
+ timers.add(timer);
187
+ }
188
+ }
141
189
  async sessionState(session, forceModels = false) {
142
190
  const availableModels = await fetchAvailableModels(this.binaryPath, forceModels);
143
191
  return {
@@ -265,17 +313,9 @@ export class ACPServer extends EventEmitter {
265
313
  this.sendSuccess(id, await this.sessionState(session, true));
266
314
  this.publishCommands(session.id);
267
315
  this.publishUsageUpdate(session);
268
- // Re-publish usage after ACPAgent completes awaiting newSession and stores this.sessionId
269
- setTimeout(() => {
270
- if (this.sessionManager.getSession(session.id)) {
271
- this.publishUsageUpdate(session);
272
- }
273
- }, 150);
274
- setTimeout(() => {
275
- if (this.sessionManager.getSession(session.id)) {
276
- this.publishUsageUpdate(session);
277
- }
278
- }, 500);
316
+ // Re-publish usage across startup window so ACPAgent receives it after
317
+ // awaiting newSession + applyConfiguredOverrides() + session.subscribe()
318
+ this.scheduleUsagePublishBurst(session.id, [120, 450, 1200, 2800]);
279
319
  }
280
320
  break;
281
321
  }
@@ -311,6 +351,9 @@ export class ACPServer extends EventEmitter {
311
351
  session.setTitle(firstUser.text.slice(0, 80).replace(/\r?\n/g, " ").trim());
312
352
  }
313
353
  }
354
+ if (replayItems.length > 0) {
355
+ session.seedEstimatedContextUsage(estimateReplayContextTokens(replayItems));
356
+ }
314
357
  for (const item of replayItems) {
315
358
  switch (item.type) {
316
359
  case "user_message":
@@ -378,12 +421,7 @@ export class ACPServer extends EventEmitter {
378
421
  this.sendSuccess(id, await this.sessionState(session));
379
422
  this.publishCommands(session.id);
380
423
  this.publishUsageUpdate(session);
381
- const activeSession = session;
382
- setTimeout(() => {
383
- if (this.sessionManager.getSession(activeSession.id)) {
384
- this.publishUsageUpdate(activeSession);
385
- }
386
- }, 150);
424
+ this.scheduleUsagePublishBurst(session.id, [120, 450, 1200, 2800]);
387
425
  }
388
426
  }
389
427
  catch (err) {
@@ -414,10 +452,23 @@ export class ACPServer extends EventEmitter {
414
452
  created = true;
415
453
  }
416
454
  await session.ensureReadyForResume();
455
+ const resumeConversationId = session.process.currentConversationId;
456
+ if (resumeConversationId &&
457
+ session.usage.contextWindowUsedTokens === 0 &&
458
+ session.usage.totalTokens === 0) {
459
+ try {
460
+ const replayItems = await loadConversationTranscript(resumeConversationId);
461
+ if (replayItems.length > 0) {
462
+ session.seedEstimatedContextUsage(estimateReplayContextTokens(replayItems));
463
+ }
464
+ }
465
+ catch { }
466
+ }
417
467
  if (!isNotification) {
418
468
  this.sendSuccess(id, await this.sessionState(session));
419
469
  this.publishCommands(session.id);
420
470
  this.publishUsageUpdate(session);
471
+ this.scheduleUsagePublishBurst(session.id, [120, 450, 1200, 2800]);
421
472
  }
422
473
  }
423
474
  catch (err) {
@@ -458,6 +509,17 @@ export class ACPServer extends EventEmitter {
458
509
  mode: sourceSession?.mode,
459
510
  permission: sourceSession?.permission,
460
511
  conversationId: sourceSession?.process.currentConversationId,
512
+ usage: sourceSession
513
+ ? {
514
+ inputTokens: 0,
515
+ outputTokens: 0,
516
+ cachedInputTokens: 0,
517
+ totalTokens: 0,
518
+ totalCostUsd: 0,
519
+ contextWindowUsedTokens: sourceSession.usage.contextWindowUsedTokens,
520
+ contextWindowMaxTokens: sourceSession.usage.contextWindowMaxTokens,
521
+ }
522
+ : undefined,
461
523
  binaryPath: this.binaryPath,
462
524
  });
463
525
  if (sourceSession?.title) {
@@ -467,6 +529,7 @@ export class ACPServer extends EventEmitter {
467
529
  this.sendSuccess(id, await this.sessionState(newSession));
468
530
  this.publishCommands(newSession.id);
469
531
  this.publishUsageUpdate(newSession);
532
+ this.scheduleUsagePublishBurst(newSession.id, [120, 450, 1200]);
470
533
  }
471
534
  break;
472
535
  }
@@ -517,8 +580,11 @@ export class ACPServer extends EventEmitter {
517
580
  },
518
581
  });
519
582
  }
520
- if (!isNotification)
583
+ this.publishUsageUpdate(session);
584
+ if (!isNotification) {
521
585
  this.sendSuccess(id, { stopReason: "end_turn" });
586
+ this.scheduleUsagePublishBurst(session.id, [25, 150]);
587
+ }
522
588
  break;
523
589
  }
524
590
  let hasSentNonNarration = false;
@@ -754,6 +820,7 @@ export class ACPServer extends EventEmitter {
754
820
  if (session.isCancelled) {
755
821
  if (!isNotification) {
756
822
  this.sendSuccess(id, { stopReason: "cancelled", usage: usagePayload });
823
+ this.scheduleUsagePublishBurst(session.id, [25, 150]);
757
824
  }
758
825
  }
759
826
  else if (resultEvent.result?.status === "ERROR") {
@@ -768,10 +835,12 @@ export class ACPServer extends EventEmitter {
768
835
  });
769
836
  if (!isNotification) {
770
837
  this.sendSuccess(id, { stopReason: "refusal", usage: usagePayload });
838
+ this.scheduleUsagePublishBurst(session.id, [25, 150]);
771
839
  }
772
840
  }
773
841
  else if (!isNotification) {
774
842
  this.sendSuccess(id, { stopReason: "end_turn", usage: usagePayload });
843
+ this.scheduleUsagePublishBurst(session.id, [25, 150]);
775
844
  }
776
845
  }
777
846
  catch (promptErr) {
@@ -779,8 +848,10 @@ export class ACPServer extends EventEmitter {
779
848
  if (session.isCancelled ||
780
849
  errorMsg.includes("cancelled") ||
781
850
  errorMsg.includes("Turn was cancelled")) {
782
- if (!isNotification)
851
+ if (!isNotification) {
783
852
  this.sendSuccess(id, { stopReason: "cancelled" });
853
+ this.scheduleUsagePublishBurst(session.id, [25, 150]);
854
+ }
784
855
  }
785
856
  else {
786
857
  logger.error("Error executing prompt turn", { error: errorMsg, sessionId });
@@ -791,8 +862,10 @@ export class ACPServer extends EventEmitter {
791
862
  content: { type: "text", text: `\n\n**Error:** ${errorMsg}\n` },
792
863
  },
793
864
  });
794
- if (!isNotification)
865
+ if (!isNotification) {
795
866
  this.sendSuccess(id, { stopReason: "refusal" });
867
+ this.scheduleUsagePublishBurst(session.id, [25, 150]);
868
+ }
796
869
  }
797
870
  }
798
871
  }
@@ -816,6 +889,7 @@ export class ACPServer extends EventEmitter {
816
889
  case ACP_METHODS.SESSION_CLOSE:
817
890
  case ACP_METHODS.SESSION_CLOSE_ALIAS: {
818
891
  const sessionId = String(params.sessionId || "");
892
+ this.clearUsagePublishTimers(sessionId);
819
893
  await this.sessionManager.closeSession(sessionId);
820
894
  if (!isNotification)
821
895
  this.sendSuccess(id, {});
@@ -838,8 +912,11 @@ export class ACPServer extends EventEmitter {
838
912
  break;
839
913
  }
840
914
  session.setMode(modeId);
841
- if (!isNotification)
915
+ if (!isNotification) {
842
916
  this.sendSuccess(id, {});
917
+ this.publishUsageUpdate(session);
918
+ this.scheduleUsagePublishBurst(session.id, [80, 350, 1000]);
919
+ }
843
920
  break;
844
921
  }
845
922
  case ACP_METHODS.SESSION_SET_MODEL:
@@ -881,8 +958,11 @@ export class ACPServer extends EventEmitter {
881
958
  currentPermission: session.permission,
882
959
  availableModels: models,
883
960
  });
884
- if (!isNotification)
961
+ if (!isNotification) {
885
962
  this.sendSuccess(id, { configOptions });
963
+ this.publishUsageUpdate(session);
964
+ this.scheduleUsagePublishBurst(session.id, [80, 350, 1000]);
965
+ }
886
966
  break;
887
967
  }
888
968
  case ACP_METHODS.SESSION_SET_CONFIG_OPTION:
@@ -949,8 +1029,11 @@ export class ACPServer extends EventEmitter {
949
1029
  currentPermission: session.permission,
950
1030
  availableModels: models,
951
1031
  });
952
- if (!isNotification)
1032
+ if (!isNotification) {
953
1033
  this.sendSuccess(id, { configOptions });
1034
+ this.publishUsageUpdate(session);
1035
+ this.scheduleUsagePublishBurst(session.id, [80, 350, 1000]);
1036
+ }
954
1037
  break;
955
1038
  }
956
1039
  case "provider/usage":
@@ -977,6 +1060,7 @@ export class ACPServer extends EventEmitter {
977
1060
  if (!this.isRunning)
978
1061
  return;
979
1062
  this.isRunning = false;
1063
+ this.clearUsagePublishTimers();
980
1064
  if (this.rl) {
981
1065
  this.rl.close();
982
1066
  this.rl = null;
@@ -135,9 +135,20 @@ export function resolveDefaultAgyBinary(force = false) {
135
135
  }
136
136
  }
137
137
  else {
138
- const localPath = path.join(home, ".local", "bin", "agy");
139
- if (fs.existsSync(localPath))
140
- resolved = localPath;
138
+ const posixCandidates = [
139
+ path.join(home, ".local", "bin", "agy"),
140
+ "/opt/homebrew/bin/agy",
141
+ "/usr/local/bin/agy",
142
+ "/usr/bin/agy",
143
+ path.join(home, ".antigravity", "bin", "agy"),
144
+ path.join(home, ".gemini", "antigravity-cli", "bin", "agy"),
145
+ ];
146
+ for (const cand of posixCandidates) {
147
+ if (fs.existsSync(cand)) {
148
+ resolved = cand;
149
+ break;
150
+ }
151
+ }
141
152
  }
142
153
  }
143
154
  }
package/dist/asar.d.ts CHANGED
@@ -21,6 +21,11 @@ export declare function readArchiveHeader(archivePath: string): {
21
21
  * Lists all file paths inside an ASAR archive.
22
22
  */
23
23
  export declare function listPackage(archivePath: string): string[];
24
+ /**
25
+ * Reads a single UTF-8 text file directly from an ASAR archive without extracting
26
+ * the entire archive to disk. Returns null if the archive or entry cannot be read.
27
+ */
28
+ export declare function readFileFromPackage(archivePath: string, relPath: string): string | null;
24
29
  /**
25
30
  * Pure TypeScript, zero-dependency ASAR archive extractor.
26
31
  * Compatible with all Node.js versions (Node 18, 20, 22, 24+) across Windows, macOS, and Linux.
package/dist/asar.js CHANGED
@@ -54,6 +54,40 @@ export function listPackage(archivePath) {
54
54
  walk(header, "");
55
55
  return result;
56
56
  }
57
+ /**
58
+ * Reads a single UTF-8 text file directly from an ASAR archive without extracting
59
+ * the entire archive to disk. Returns null if the archive or entry cannot be read.
60
+ */
61
+ export function readFileFromPackage(archivePath, relPath) {
62
+ try {
63
+ const { header, dataBaseOffset } = readArchiveHeader(archivePath);
64
+ const parts = relPath.replace(/^\/+/, "").split("/").filter(Boolean);
65
+ let node = header;
66
+ for (const part of parts) {
67
+ node = node?.files?.[part];
68
+ if (!node)
69
+ return null;
70
+ }
71
+ if (node.unpacked || node.size === undefined || node.offset === undefined) {
72
+ return null;
73
+ }
74
+ if (node.size === 0)
75
+ return "";
76
+ const fileOffset = dataBaseOffset + parseInt(node.offset, 10);
77
+ const fd = fs.openSync(archivePath, "r");
78
+ try {
79
+ const buf = Buffer.alloc(node.size);
80
+ const bytesRead = fs.readSync(fd, buf, 0, node.size, fileOffset);
81
+ return buf.subarray(0, bytesRead).toString("utf8");
82
+ }
83
+ finally {
84
+ fs.closeSync(fd);
85
+ }
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
57
91
  /**
58
92
  * Pure TypeScript, zero-dependency ASAR archive extractor.
59
93
  * Compatible with all Node.js versions (Node 18, 20, 22, 24+) across Windows, macOS, and Linux.
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { execFileSync } from "node:child_process";
4
6
  import { ACPServer } from "./acp-server.js";
5
7
  import { logger } from "./logger.js";
@@ -8,6 +10,11 @@ import { ensurePaseoIntegration, restorePaseoIntegration, findPaseoServerInstall
8
10
  import { resolveDefaultAgyBinary, isWindowsBatchScript } from "./antigravity-process.js";
9
11
  import { parseAgyQuotaOutput } from "./protocol.js";
10
12
  const args = process.argv.slice(2);
13
+ const bundledPluginDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "paseo-plugin");
14
+ if (args.includes("plugin-path") || args.includes("--plugin-path")) {
15
+ process.stdout.write(`${bundledPluginDir}\n`);
16
+ process.exit(0);
17
+ }
11
18
  if (args.includes("--version") || args.includes("-v")) {
12
19
  if (args.includes("--json")) {
13
20
  process.stdout.write(JSON.stringify(resolveBuildMetadata(), null, 2) + "\n");
@@ -31,12 +38,14 @@ Commands:
31
38
  setup, patch Configure and integrate Antigravity telemetry with Paseo
32
39
  doctor Diagnose Antigravity binary, quota provider, and Paseo status
33
40
  restore Restore original Paseo files from backup (.bak)
41
+ plugin-path Print path to the update-proof Paseo telemetry plugin directory
34
42
 
35
43
  Options:
36
44
  --acp Start ACP server over stdio (default)
37
45
  --setup Integrate Antigravity with local Paseo server installation
38
46
  --doctor Run environment, binary, and telemetry diagnostics
39
47
  --restore Restore original Paseo files from backup
48
+ --plugin-path Print bundled Paseo plugin directory for 'paseo plugin install'
40
49
  --force-macos-app Force patching inside macOS .app bundle (re-signs with ad-hoc)
41
50
  --skip-naration Drop leading narration-only assistant chunks (e.g. 'I will...')
42
51
  --skip-narration Alias for --skip-naration
@@ -139,6 +148,22 @@ if (args.includes("doctor") || args.includes("--doctor")) {
139
148
  process.stdout.write("\n3. Paseo Installation Targets:\n");
140
149
  const serverPaths = findPaseoServerInstallations();
141
150
  const asarPaths = findPaseoAsarPaths();
151
+ let telemetryPluginEnabled = false;
152
+ try {
153
+ const paseoHome = process.env.PASEO_HOME || path.join(process.env.HOME || process.env.USERPROFILE || "", ".paseo");
154
+ const cfgPath = path.join(paseoHome, "config.json");
155
+ if (fs.existsSync(cfgPath)) {
156
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
157
+ if (cfg?.pluginsEnabled === true && cfg?.plugins) {
158
+ const p = cfg.plugins["antigravity-telemetry"];
159
+ if (p && p.enabled !== false) {
160
+ telemetryPluginEnabled = true;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ catch { }
166
+ process.stdout.write(` - [Plugin] antigravity-telemetry -> ${telemetryPluginEnabled ? "[ENABLED]" : "[NOT INSTALLED]"}\n`);
142
167
  const totalFound = serverPaths.length + asarPaths.length;
143
168
  if (totalFound === 0) {
144
169
  process.stdout.write(" [!] No Paseo server or desktop installations discovered in standard paths.\n");
@@ -185,21 +210,22 @@ if (args.includes("doctor") || args.includes("--doctor")) {
185
210
  process.stdout.write(" - Antigravity CLI ('agy') executable was not found.\n");
186
211
  process.stdout.write(" Action: Install Antigravity CLI or set AGY_BIN_PATH environment variable.\n");
187
212
  }
188
- if (totalFound === 0) {
213
+ if (totalFound === 0 && !telemetryPluginEnabled) {
189
214
  hasIssue = true;
190
215
  process.stdout.write(" - No Paseo installations found.\n");
191
216
  process.stdout.write(" Action: Set PASEO_SERVER_PATH or PASEO_ASAR_PATH to your Paseo install directory if located elsewhere.\n");
192
217
  }
193
- else if (unpatchedCount > 0) {
218
+ else if (unpatchedCount > 0 && !telemetryPluginEnabled) {
194
219
  hasIssue = true;
195
- if (paseoRunning) {
196
- process.stdout.write(` - Found ${unpatchedCount} unpatched Paseo target(s), but Paseo is currently running.\n`);
197
- process.stdout.write(" Action: Close Paseo completely (Stop-Process -Name \"Paseo\" -Force), then run:\n");
220
+ process.stdout.write(` - Found ${unpatchedCount} unpatched Paseo target(s) and 'antigravity-telemetry' plugin is not yet installed.\n`);
221
+ process.stdout.write(" Recommended update-proof action (survives Paseo updates without touching .app signatures):\n");
222
+ process.stdout.write(` paseo plugin install "${bundledPluginDir}"\n`);
223
+ if (isWin) {
224
+ process.stdout.write(" Or to patch standalone Paseo after closing it (Stop-Process -Name \"Paseo\" -Force):\n");
198
225
  process.stdout.write(" npx -y paseo-acp-agy setup\n");
199
226
  }
200
227
  else {
201
- process.stdout.write(` - Found ${unpatchedCount} unpatched Paseo target(s).\n`);
202
- process.stdout.write(" Action: Run setup to patch Paseo for Antigravity telemetry:\n");
228
+ process.stdout.write(" Or for standalone @getpaseo/cli servers:\n");
203
229
  process.stdout.write(" npx -y paseo-acp-agy setup\n");
204
230
  }
205
231
  }
@@ -272,8 +298,9 @@ if (args.includes("setup") ||
272
298
  process.stdout.write(`\n🛡️ Preserved macOS Code Signature:\n` +
273
299
  ` Skipped ${res.skippedPaths.length} target(s) inside macOS .app bundle to protect official Apple Developer ID signature\n` +
274
300
  ` and prevent Gatekeeper "damaged app" warnings.\n` +
275
- ` (Antigravity ACP runs standalone and does not require modifying Paseo.app).\n` +
276
- ` If you specifically wish to patch the .app bundle, run with: paseo-acp-agy patch --force-macos-app\n\n`);
301
+ ` Recommended update-proof telemetry setup (survives every Paseo update):\n` +
302
+ ` paseo plugin install "${bundledPluginDir}"\n` +
303
+ ` (Or if you specifically wish to patch the .app bundle: paseo-acp-agy patch --force-macos-app)\n\n`);
277
304
  }
278
305
  if (isPaseoRunning()) {
279
306
  const isWin = process.platform === "win32";
@@ -58,7 +58,17 @@ export declare function ensurePaseoIntegration(options?: {
58
58
  forceMacosApp?: boolean;
59
59
  }): Promise<PatchResult>;
60
60
  /**
61
- * Restores original unpatched Paseo files from backup (.bak) archives.
61
+ * Reads the Paseo version embedded inside an ASAR archive's package.json.
62
+ */
63
+ export declare function resolveAsarArchiveVersion(archivePath: string): string | null;
64
+ /**
65
+ * Resolves the authoritative Paseo version of a target app.asar installation,
66
+ * preferring macOS bundle Info.plist (CFBundleShortVersionString) when inside an .app bundle.
67
+ */
68
+ export declare function resolveTargetPaseoVersion(asarPath: string): string | null;
69
+ /**
70
+ * Restores original unpatched Paseo files from backup (.bak) archives,
71
+ * enforcing strict version matching so an older backup can never downgrade a newer Paseo app.
62
72
  */
63
73
  export declare function restorePaseoIntegration(): Promise<{
64
74
  restored: string[];