paseo-acp-agy 1.3.0 → 1.3.2

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.
@@ -679,8 +679,8 @@ export class ACPServer extends EventEmitter {
679
679
  toolCall: {
680
680
  toolCallId: toolCall.toolCallId,
681
681
  title: toolCall.title,
682
- kind: "question",
683
- content: [{ type: "text", text: promptText }],
682
+ kind: "other",
683
+ content: [{ type: "content", content: { type: "text", text: promptText } }],
684
684
  rawInput: toolCall.rawInput,
685
685
  },
686
686
  options,
@@ -119,14 +119,12 @@ export function findPaseoServerInstallations() {
119
119
  }
120
120
  }
121
121
  else {
122
- // POSIX locations (Linux / macOS)
122
+ // POSIX locations (Linux / standalone CLI — never inside signed macOS .app bundles)
123
123
  const posixLocations = [
124
124
  "/usr/lib/node_modules/@getpaseo/cli/node_modules/@getpaseo/server",
125
125
  "/usr/lib/node_modules/@getpaseo/server",
126
126
  "/usr/local/lib/node_modules/@getpaseo/cli/node_modules/@getpaseo/server",
127
127
  "/usr/local/lib/node_modules/@getpaseo/server",
128
- "/Applications/Paseo.app/Contents/Resources/app.asar.unpacked/node_modules/@getpaseo/server",
129
- "/Applications/Paseo.app/Contents/Resources/app/node_modules/@getpaseo/server",
130
128
  ];
131
129
  if (home) {
132
130
  posixLocations.push(path.join(home, ".local", "lib", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"), path.join(home, ".local", "lib", "node_modules", "@getpaseo", "server"), path.join(home, ".local", "share", "pnpm", "global", "5", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"), path.join(home, ".bun", "install", "global", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"));
@@ -199,6 +197,11 @@ export function findPaseoServerInstallations() {
199
197
  if (seen.has(key))
200
198
  continue;
201
199
  seen.add(key);
200
+ if (process.platform === "darwin" &&
201
+ dir.includes(".app/Contents/") &&
202
+ process.env.PASEO_SERVER_PATH !== dir) {
203
+ continue;
204
+ }
202
205
  if (fs.existsSync(path.join(dir, "dist")) || fs.existsSync(path.join(dir, "package.json"))) {
203
206
  verified.push(dir);
204
207
  }
@@ -135,7 +135,7 @@ export interface SessionInfo {
135
135
  updatedAt?: string | null;
136
136
  _meta?: Record<string, unknown> | null;
137
137
  }
138
- export type ToolKind = "read" | "edit" | "execute" | "fetch" | "search" | "question" | "other";
138
+ export type ToolKind = "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "switch_mode" | "other";
139
139
  export declare function mapToolNameToKind(toolName: string): ToolKind;
140
140
  export declare function extractPromptText(promptInput: unknown): string;
141
141
  export interface ModelDefinition {
@@ -18,6 +18,7 @@ export interface NormalizedToolResult {
18
18
  }
19
19
  export interface PlanEntry {
20
20
  content: string;
21
+ priority: "high" | "medium" | "low";
21
22
  status: "pending" | "in_progress" | "completed";
22
23
  }
23
24
  /**
@@ -102,7 +102,7 @@ export function normalizeToolCall(toolName, rawParams = {}, stepIndex) {
102
102
  return {
103
103
  toolCallId,
104
104
  title: "❓ Pergunta do Assistente",
105
- kind: "question",
105
+ kind: "other",
106
106
  rawInput: normalizedParams,
107
107
  questionCard: questionCard || undefined,
108
108
  };
@@ -149,8 +149,11 @@ export function normalizeToolResult(toolName, rawParams = {}, output, error) {
149
149
  else if (name === "read_file" || name === "view_file") {
150
150
  if (outputStr.trim()) {
151
151
  content.push({
152
- type: "text",
153
- text: outputStr,
152
+ type: "content",
153
+ content: {
154
+ type: "text",
155
+ text: outputStr,
156
+ },
154
157
  });
155
158
  }
156
159
  }
@@ -180,7 +183,7 @@ export function extractPlanEntries(text) {
180
183
  else if (mark === "/" || mark === "-") {
181
184
  status = "in_progress";
182
185
  }
183
- entries.push({ content, status });
186
+ entries.push({ content, priority: "medium", status });
184
187
  }
185
188
  }
186
189
  return entries.length >= 2 ? entries : null;
@@ -8,6 +8,7 @@ export interface ReplayItem {
8
8
  rawInput?: Record<string, unknown>;
9
9
  rawOutput?: string;
10
10
  status?: "completed" | "failed";
11
+ compacted?: boolean;
11
12
  }
12
13
  /**
13
14
  * Strips XML-like tags injected by harness or system prompts (e.g., <USER_REQUEST>, <ADDITIONAL_METADATA>)
@@ -85,11 +85,13 @@ export function loadConversationTranscript(conversationId) {
85
85
  const stepType = String(step.type || "");
86
86
  if (stepType === "USER_INPUT") {
87
87
  const rawContent = typeof step.content === "string" ? step.content : "";
88
+ const isCompacted = rawContent.includes("<CONTEXT_SUMMARY>");
88
89
  const cleanPrompt = extractCleanUserPrompt(rawContent);
89
90
  if (cleanPrompt) {
90
91
  replayItems.push({
91
92
  type: "user_message",
92
93
  text: cleanPrompt,
94
+ ...(isCompacted ? { compacted: true } : {}),
93
95
  });
94
96
  }
95
97
  }
@@ -163,6 +165,9 @@ export function estimateReplayContextTokens(items) {
163
165
  return 0;
164
166
  let totalChars = 0;
165
167
  for (const item of items) {
168
+ if (item.compacted) {
169
+ totalChars = 0;
170
+ }
166
171
  if (item.text) {
167
172
  totalChars += item.text.length;
168
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paseo-acp-agy",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "ACP (Agent Client Protocol) provider for Google Antigravity in Paseo and Zed",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,6 +41,8 @@ export function toneColor(tone: UsageTone, theme: PluginTheme): string {
41
41
  export function buildPillLabel(session: SessionTelemetry, quota: QuotaReport): string {
42
42
  const parts: string[] = [];
43
43
 
44
+ parts.push(`${Math.round(session.contextPct)}% ctx`);
45
+
44
46
  if (session.totalCostUsd > 0) {
45
47
  parts.push(fmtCost(session.totalCostUsd));
46
48
  } else if (session.totalTokens > 0) {
@@ -49,8 +51,6 @@ export function buildPillLabel(session: SessionTelemetry, quota: QuotaReport): s
49
51
  parts.push("$0.00");
50
52
  }
51
53
 
52
- parts.push(`ctx ${Math.round(session.contextPct)}%`);
53
-
54
54
  const primaryFiveHour =
55
55
  quota.windows.find((w) => w.id === "session") ?? quota.windows[0] ?? null;
56
56
  if (primaryFiveHour) {
@@ -5,13 +5,22 @@ import type {
5
5
  PluginClientContext,
6
6
  } from "@getpaseo/plugin/client";
7
7
  import { Icon, ScrollView } from "@getpaseo/plugin/client/react-native";
8
- import { useEffect } from "react";
8
+ import { useEffect, useRef } from "react";
9
9
  import { Text, View } from "react-native";
10
10
  import { isAntigravityProvider } from "../shared/telemetry.ts";
11
11
  import { useAntigravityTelemetry } from "./data.ts";
12
12
  import { TelemetryBody } from "./ui.tsx";
13
13
  import { buildPillLabel } from "./ui-helpers.ts";
14
14
 
15
+ function sanitizeButtonId(agentId: string): string {
16
+ const cleaned = agentId
17
+ .toLowerCase()
18
+ .replace(/[^a-z0-9-]+/g, "-")
19
+ .replace(/-+/g, "-")
20
+ .replace(/^-|-$/g, "");
21
+ return `agy-telemetry-${cleaned || "agent"}`;
22
+ }
23
+
15
24
  interface PopoverProps {
16
25
  theme: PluginTheme;
17
26
  layout: { compact: boolean };
@@ -62,21 +71,35 @@ export function contributeAntigravityPills(client: PluginClientContext): () => P
62
71
 
63
72
  function TelemetryPillIcon(props: PluginButtonIconProps) {
64
73
  const { data, error } = useAntigravityTelemetry(agentId);
74
+ const lastLabelRef = useRef<string>("AGY …");
75
+
65
76
  useEffect(() => {
66
77
  if (!handle) return;
67
- const nextLabel = error
78
+ const computed = error
68
79
  ? "AGY"
69
80
  : data
70
81
  ? buildPillLabel(data.session, data.quota)
71
82
  : "AGY …";
72
- handle.update({ label: nextLabel });
83
+ const nextLabel = computed.trim() || "AGY";
84
+ if (lastLabelRef.current !== nextLabel) {
85
+ lastLabelRef.current = nextLabel;
86
+ handle.update({ label: nextLabel });
87
+ }
73
88
  }, [data, error]);
74
89
 
90
+ const pct = data?.session.contextPct ?? 0;
91
+ const iconColor =
92
+ pct >= 90
93
+ ? "#ef4444"
94
+ : pct >= 75
95
+ ? "#f59e0b"
96
+ : props.color;
97
+
75
98
  return (
76
99
  <Icon
77
100
  name={data?.session.activeTurn ? "Activity" : "Gauge"}
78
101
  size={props.size}
79
- color={props.color}
102
+ color={iconColor}
80
103
  />
81
104
  );
82
105
  }
@@ -123,7 +146,7 @@ export function contributeAntigravityPills(client: PluginClientContext): () => P
123
146
  }
124
147
 
125
148
  handle = client.addComposerPill({
126
- id: `agy-telemetry-${agentId}`,
149
+ id: sanitizeButtonId(agentId),
127
150
  workspaceId,
128
151
  agentId,
129
152
  button: {
@@ -30,14 +30,19 @@ export default function contribute(client: PluginClientContext) {
30
30
  ).addSlashCommand;
31
31
 
32
32
  if (typeof maybeAddSlashCommand === "function") {
33
- maybeAddSlashCommand.call(client, {
34
- name: "agy-telemetry",
35
- description: "Open Antigravity session usage, context wheel & quota panel",
36
- context: "agent",
37
- async onSubmit({ openPanel }) {
38
- openPanel("telemetry");
39
- },
40
- });
33
+ try {
34
+ maybeAddSlashCommand.call(client, {
35
+ name: "agy-telemetry",
36
+ description: "Open Antigravity session usage, context wheel & quota panel",
37
+ argumentHint: "[panel]",
38
+ context: "agent",
39
+ async onSubmit({ openPanel }) {
40
+ openPanel("telemetry");
41
+ },
42
+ });
43
+ } catch (err) {
44
+ console.warn("[antigravity-telemetry] Slash command registration skipped:", err);
45
+ }
41
46
  }
42
47
 
43
48
  return contributeAntigravityPills(client);
@@ -475,6 +475,43 @@ export interface AgentSnapshotInfo {
475
475
  } | null;
476
476
  }
477
477
 
478
+ function estimateConversationContextTokens(conversationId: string | null | undefined, maxTokens: number): number | null {
479
+ if (!conversationId) return null;
480
+ const home = os.homedir();
481
+ const candidates = [
482
+ process.env.AGY_BRAIN_DIR ? path.join(process.env.AGY_BRAIN_DIR, conversationId) : "",
483
+ home ? path.join(home, ".gemini", "antigravity-cli", "brain", conversationId) : "",
484
+ home ? path.join(home, ".antigravity", "brain", conversationId) : "",
485
+ ].filter(Boolean);
486
+
487
+ for (const brainDir of candidates) {
488
+ const transcriptPath = path.join(brainDir, ".system_generated", "logs", "transcript.jsonl");
489
+ if (!fs.existsSync(transcriptPath)) continue;
490
+ try {
491
+ const content = fs.readFileSync(transcriptPath, "utf-8");
492
+ const lines = content.split(/\r?\n/).filter(Boolean);
493
+ let activeChars = 0;
494
+ for (const line of lines) {
495
+ try {
496
+ const step = JSON.parse(line) as Record<string, unknown>;
497
+ const rawContent = typeof step.content === "string" ? step.content : "";
498
+ if (step.type === "USER_INPUT" && rawContent.includes("<CONTEXT_SUMMARY>")) {
499
+ activeChars = 0;
500
+ }
501
+ activeChars += rawContent.length;
502
+ if (typeof step.thinking === "string") activeChars += step.thinking.length;
503
+ if (Array.isArray(step.tool_calls)) activeChars += JSON.stringify(step.tool_calls).length;
504
+ } catch {}
505
+ }
506
+ if (activeChars <= 0) return null;
507
+ return Math.min(maxTokens, 4_000 + Math.ceil(activeChars / 4));
508
+ } catch {
509
+ return null;
510
+ }
511
+ }
512
+ return null;
513
+ }
514
+
478
515
  export function resolveSessionTelemetry(
479
516
  agentId: string,
480
517
  snapshot: AgentSnapshotInfo | null | undefined
@@ -496,10 +533,22 @@ export function resolveSessionTelemetry(
496
533
  : defaultMax;
497
534
  // Prefer live streaming contextWindowUsedTokens from snapshot if higher/fresher mid-turn
498
535
  const liveUsed = snapshot?.lastUsage?.contextWindowUsedTokens;
499
- const rawUsed =
536
+ let rawUsed =
500
537
  snapshot?.activeTurn && typeof liveUsed === "number" && liveUsed > 0
501
538
  ? liveUsed
502
539
  : (u.contextWindowUsedTokens ?? liveUsed ?? 0);
540
+ // Repair legacy pre-v1.3.0 sessions where cumulative inputTokens was clamped to maxTokens
541
+ if (
542
+ rawUsed >= maxTokens &&
543
+ inputTokens > maxTokens &&
544
+ (!liveUsed || liveUsed >= maxTokens) &&
545
+ persisted.conversationId
546
+ ) {
547
+ const estimated = estimateConversationContextTokens(persisted.conversationId, maxTokens);
548
+ if (estimated !== null && estimated > 0 && estimated < maxTokens) {
549
+ rawUsed = estimated;
550
+ }
551
+ }
503
552
  const usedTokens = Math.min(Math.max(0, rawUsed), maxTokens);
504
553
  const totalCostUsd =
505
554
  typeof u.totalCostUsd === "number"
@@ -545,9 +594,9 @@ export function resolveSessionTelemetry(
545
594
  return {
546
595
  agentId,
547
596
  sessionId,
548
- conversationId: null,
597
+ conversationId: persisted?.conversationId ?? null,
549
598
  model,
550
- effort: null,
599
+ effort: persisted?.effort ?? null,
551
600
  activeTurn: Boolean(snapshot.activeTurn),
552
601
  inputTokens,
553
602
  outputTokens,
@@ -562,6 +611,28 @@ export function resolveSessionTelemetry(
562
611
  };
563
612
  }
564
613
 
614
+ if (persisted?.conversationId) {
615
+ const estimated = estimateConversationContextTokens(persisted.conversationId, defaultMax) ?? 0;
616
+ return {
617
+ agentId,
618
+ sessionId,
619
+ conversationId: persisted.conversationId,
620
+ model,
621
+ effort: persisted.effort ?? null,
622
+ activeTurn: Boolean(snapshot?.activeTurn),
623
+ inputTokens: 0,
624
+ outputTokens: 0,
625
+ cachedInputTokens: 0,
626
+ totalTokens: 0,
627
+ totalCostUsd: 0,
628
+ contextWindowUsedTokens: estimated,
629
+ contextWindowMaxTokens: defaultMax,
630
+ contextPct: defaultMax > 0 ? Math.min(100, Math.round((estimated / defaultMax) * 1000) / 10) : 0,
631
+ updatedAt: persisted.updatedAt ?? snapshot?.updatedAt ?? null,
632
+ source: "acp_store",
633
+ };
634
+ }
635
+
565
636
  return {
566
637
  agentId,
567
638
  sessionId,
@@ -68,7 +68,7 @@ export const TelemetryResponseSchema = z.object({
68
68
  export type TelemetryResponse = z.infer<typeof TelemetryResponseSchema>;
69
69
 
70
70
  export const agyTelemetryGet = defineRpc({
71
- name: "agyTelemetry.get",
71
+ name: "agy-telemetry.get",
72
72
  input: z.object({
73
73
  agentId: z.string(),
74
74
  forceQuota: z.boolean().optional(),