@estebanforge/pi-antigravity-bridge 1.4.8 → 1.4.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.4.9] - 2026-09-05
6
+
7
+ ### Added
8
+
9
+ - Warn toasts in the pi UI: bridge tool failures, stalls, timeouts, process exits, and other warnings surface as native warning notifications the moment they happen, so users see them and can report them. Over SSH or without a UI the same text falls back to stderr. Deliberate aborts, connection exits (the turn's own error block carries real crashes), and `call-tool-fail` (same-instant duplicate of `round-trip-fail`) stay silent.
10
+ - Gate B watch in `/agy doctor`: the ACP connection latches the first `session/update` payload that carries usage/token fields, and doctor prints one "acp tokens: AVAILABLE" line when that happens. Silent until then; the day the line appears, wiring real token counts becomes a small mapping job. Auth-style string `"token"` keys inside tool frames cannot trip the latch.
11
+
12
+ ### Fixed
13
+
14
+ - Esc-abort on the ACP engine no longer dumps the dying server's raw stderr tail (a google3 stack trace) into the transcript. Teardown exits (abort kill, shutdown, idle recycle) are marked expected and never render; a genuine mid-turn crash surfaces as the turn's own clean error block instead. Raw tails still land in the file log for post-mortems.
15
+
16
+ ### Changed
17
+
18
+ - The ACP engine is now beta. Parity is verified live against RC01 (text streaming, multi-turn resume, bridge tools, effort switching, serialization, abort recovery - see `scripts/parity-live.mjs`), so it graduates from opt-in-experimental to a supported alternative engine. It stays behind `config.engine` for now; the two known RC01 gaps (no usage fields, kill+reload abort) are documented in the README.
19
+ - Daily log volume: the default tier now writes ONLY errors, so a regular session costs the disk nothing. Warns live in the UI (see above); `AGY_DEBUG=1` restores the full debug/info/warn/error trail for reproducing a problem. Docs: README debug-logs section, `/agy doctor` hint.
20
+ - The tool-priority note now steers agy's native `view_file` (artifact-sandboxed on RC01) to the Pi Bridge file tools (`read`, `ls`, `grep`, `find`, `edit`, `execute`) for any real filesystem path. Observed live: repeated `invalid_args` rejections on `operator/pkg/tmux/client.go` before the model fell back to `edit`.
21
+
5
22
  ## [1.4.8] - 2026-09-05
6
23
 
7
24
  ### Added
package/README.md CHANGED
@@ -21,11 +21,11 @@ Multi-turn works. The provider binds a pi session to an agy conversation id (per
21
21
  Turns run through one of two engines behind the same provider surface (`config.engine`, default `stream-json`):
22
22
 
23
23
  - **stream-json** (default): the persistent `agy` CLI process. The tested default; live token usage; conversation resume via `--conversation`.
24
- - **acp**: Google's official ACP server (`agy_acp_server.par`), JSON-RPC 2.0 over stdio. Opt-in while it matures: the current build (RC01) ships no usage fields (token display shows zero) and no cancel (abort tears the server down and reloads it next turn). Everything else is parity-verified live - text streaming, multi-turn resume via `session/load`, bridge tools, effort switching, serialization, abort recovery - see `scripts/parity-live.mjs`.
24
+ - **acp** (beta): Google's official ACP server (`agy_acp_server.par`), JSON-RPC 2.0 over stdio. Beta: parity-verified live against the current build (RC01) - text streaming, multi-turn resume via `session/load`, bridge tools, effort switching, serialization, abort recovery (see `scripts/parity-live.mjs`). Two known RC01 gaps remain: no usage fields (token display shows zero) and no cancel (abort tears the server down and reloads it next turn).
25
25
 
26
26
  Engine-dependent features: pi image attachments ride natively only on the ACP engine (the picker offers image attach automatically when `config.engine` is `acp`; the stream-json CLI prompt is text-only). With the optional G1 digest enabled, its delivery also differs: ACP ships it as a native `embeddedContext` resource block, stream-json prepends it to the prompt text. The `AskAntigravity` delegation tool is unaffected by `config.engine` and runs the `stream-json` CLI (`agy -p`) across both configurations.
27
27
 
28
- | Capability | `stream-json` (default) | `acp` |
28
+ | Capability | `stream-json` (default) | `acp` (beta) |
29
29
  | --- | --- | --- |
30
30
  | Show thinking text | No (token count only, floor 64, no text body) | Yes (streams thought text via `agent_thought_chunk`; sparse on RC01 where reasoning often arrives in message text) |
31
31
  | Live token usage | Yes (live metrics from CLI step events) | No (absent in RC01, displays zero tokens) |
@@ -157,7 +157,7 @@ The `activate_skill` catalog mirrors pi's directory-based skill discovery: the t
157
157
  /agy system-prompt on|off send pi's system prompt + AGENTS.md + the Pi Bridge tool-priority note to new agy conversations (default on)
158
158
  /agy bridge all|mcp|none which pi tools the MCP bridge exposes to agy (default all; none = bridge off)
159
159
  /agy acp-bin <path|auto> point the ACP engine at a specific server binary (auto = setup installs, or AGY_ACP_BIN; applies on the next ACP turn)
160
- /agy engine acp|stream-json switch the turn engine (restart to apply; default stream-json; acp runs self-service setup: binary install + auth bootstrap)
160
+ /agy engine acp|stream-json switch the turn engine (restart to apply; default stream-json; acp is beta and runs self-service setup: binary install + auth bootstrap)
161
161
  /agy auth-manual manual ACP credential setup (fallback; auto-setup normally covers this; default login = your Antigravity subscription, same account as the agy CLI)
162
162
  /agy patch-cleanup restore the original pi files if an older version patched them
163
163
  /agy clear drop all session bindings (force fresh conversations)
@@ -188,7 +188,7 @@ For isolation when running any agent that executes commands without a confirmati
188
188
  | `AGY_SKIP_PERMISSIONS` | `1`/`true` (default) to pass `--dangerously-skip-permissions` so commands don't hang on an unanswerable prompt in `-p` mode. `0`/`false` to prompt (hangs any `run_command` non-interactively). Wins over the config file. |
189
189
  | `AGY_DEFAULT_MODEL` | Default model alias for the `AskAntigravity` tool (`flash`/`pro`/`gemini`, or a tier/version qualifier). Wins over the config file. |
190
190
  | `AGY_DEFAULT_THINKING` | Default thinking tier for the `AskAntigravity` tool: `low`/`medium`/`high`. Anything else falls back to `medium`. Wins over the config file. |
191
- | `AGY_DEBUG` | `1`/`true`/`on` writes verbose debug records to the daily log (driver lifecycle, raw bridge traffic). Default off: only the light info/warn/error stream. |
191
+ | `AGY_DEBUG` | `1`/`true`/`on` writes the full trail to the daily log (driver lifecycle, raw bridge traffic). Default off: **only `error` records land on disk** - routine disk writes are zero for regular users, and warnings surface as UI toasts instead. |
192
192
 
193
193
  ## Debug logs
194
194
 
@@ -198,14 +198,14 @@ The extension keeps a daily log on your machine, sorted by day:
198
198
  ~/.pi/extensions-data/estebanforge/pi-antigravity-bridge/logs/<YYYY-MM-DD>.ndjson
199
199
  ```
200
200
 
201
- One JSON record per line. Two verbosity tiers keep the disk cost negligible for regular users: by default only `info`/`warn`/`error` records land on disk, which is the useful skeleton: turn starts and outcomes with error text (both engines), bridge tool calls, escalations and poll traffic, late deliveries, and round-trip failures, `AskAntigravity` runs, `/agy` commands, ACP setup/self-heal, auth URLs, and every driver failure (stall, abort, timeout, nonzero exit). Set `AGY_DEBUG=1` before reproducing a problem for the full trail: per-event driver lifecycle (spawn, exit, session load/new, unparks), list-tools traffic, recycle causes, and the raw bridge chatter. `/agy doctor` prints the log directory.
201
+ One JSON record per line. Two verbosity tiers keep the disk cost at zero for regular users: by default only `error` records land on disk - everything routine (turn starts, tool calls, escalations, commands, driver failures) is withheld. Warnings are not lost: the extension toasts each one in the pi UI (bridge tool failures, stalls, timeouts, process exits), so users see them and can report them; deliberate aborts and failures pi already renders as the turn's own error block stay silent. Set `AGY_DEBUG=1` before reproducing a problem for the full trail: per-event driver lifecycle (spawn, exit, session load/new, unparks), turn starts and outcomes, bridge tool calls, escalations and poll traffic, late deliveries, list-tools traffic, recycle causes, and the raw bridge chatter. `/agy doctor` prints the log directory.
202
202
 
203
203
  Notes:
204
204
 
205
205
  - Retention: 14 days. Older files are pruned automatically.
206
206
  - Privacy: prompt text, tool arguments, and tool output never land in the log. Secret-shaped values (tokens, API keys, credentials, header blocks) are redacted, and long strings are truncated. The `auth-url` record strips the login URL's query string.
207
207
  - Logging never throws: an unwritable directory is skipped silently and retried on the next record.
208
- - SSD wear: default volume is a handful of records per turn. Verbose mode (`AGY_DEBUG=1`) writes more; turn it off after reproducing.
208
+ - SSD wear: default volume is effectively zero until something actually errors. Verbose mode (`AGY_DEBUG=1`) writes more; turn it off after reproducing.
209
209
 
210
210
  When you report an issue, attach the last day or two of files from that directory. For anything that needs reproduction, run with `AGY_DEBUG=1` once and attach that day's file. They usually contain the exact failure sequence (engine, session, bridge call, error) with no need to guess.
211
211
 
@@ -116,6 +116,36 @@ export default async function (pi: ExtensionAPI): Promise<void> {
116
116
  // src/daily-log.ts). Fire-and-forget, secrets redacted, old days pruned.
117
117
  // Support flow: "attach the last days' files from that dir".
118
118
  const fileLog = createDailyLogger({ dir: logsDir() });
119
+ // Warn tier = user-facing: the default file log keeps ONLY errors (zero
120
+ // routine disk traffic), so a warn that never reaches the UI is lost.
121
+ // Every warn toasts here instead; AGY_DEBUG=1 restores the full file
122
+ // trail. Silent exceptions: deliberate aborts (pi already shows
123
+ // "Operation aborted"), connection exits (the turn's own error block
124
+ // carries real crashes), and call-tool-fail (same-instant duplicate of
125
+ // round-trip-fail). No UI (headless): warn text falls back to stderr.
126
+ const rawFileLog = fileLog.log.bind(fileLog);
127
+ fileLog.log = (event, data, level) => {
128
+ rawFileLog(event, data, level);
129
+ if (level !== "warn") return;
130
+ if (event.startsWith("abort:") || event === "connection-exited" || event === "call-tool-fail") return;
131
+ const d = (data ?? {}) as Record<string, unknown>;
132
+ let text: string;
133
+ if (event === "round-trip-fail") {
134
+ text = `Bridge tool call failed: ${String(d.name ?? "tool")} (${String(d.reason ?? "unknown")})`;
135
+ } else if (event.startsWith("stall:")) {
136
+ text = "Antigravity stalled with no output; the turn was stopped";
137
+ } else if (event.startsWith("timeout:")) {
138
+ text = "Antigravity turn timed out";
139
+ } else if (event.startsWith("exit:") && event !== "exit:0") {
140
+ text = "Antigravity process exited unexpectedly";
141
+ } else if (event === "turn-error") {
142
+ text = `Antigravity turn issue: ${String(d.reason ?? "unknown")}`;
143
+ } else {
144
+ text = `Antigravity warning: ${event}`;
145
+ }
146
+ if (activeUi) activeUi.notify(text, "warning");
147
+ else console.error(`[antigravity-bridge] ${text}`);
148
+ };
119
149
  fileLog.log(
120
150
  "extension-load",
121
151
  { engine, models: models.length, fallback: usingFallback, bridge: loadConfig().bridgeTools, askTool: loadConfig().askTool },
@@ -183,12 +213,19 @@ export default async function (pi: ExtensionAPI): Promise<void> {
183
213
  : data;
184
214
  // Failures warn; turn-start + auth-url are the always-on skeleton;
185
215
  // routine per-event lifecycle (spawn, session-load, unparked, ...) is
186
- // verbose-only.
187
- const level = acpFailures.has(msg)
188
- ? "warn"
189
- : msg === "turn-start" || msg === "auth-url"
190
- ? "info"
191
- : "debug";
216
+ // verbose-only. Deliberate teardown exits (Esc abort kill, shutdown,
217
+ // idle recycle) demote to debug: routine, and the warn tier stays the
218
+ // "what broke" grep.
219
+ const expectedExit =
220
+ msg === "connection-exited" &&
221
+ (data as { expected?: boolean } | undefined)?.expected === true;
222
+ const level = expectedExit
223
+ ? "debug"
224
+ : acpFailures.has(msg)
225
+ ? "warn"
226
+ : msg === "turn-start" || msg === "auth-url"
227
+ ? "info"
228
+ : "debug";
192
229
  fileLog.log(msg, fileData, level);
193
230
  if (msg === "auth-url") {
194
231
  const { url, port } = (data ?? {}) as { url?: string; port?: number | null };
@@ -209,7 +246,9 @@ export default async function (pi: ExtensionAPI): Promise<void> {
209
246
  return;
210
247
  }
211
248
  if (!acpFailures.has(msg)) return;
212
- console.error(`[antigravity-bridge acp] ${msg}${data !== undefined ? " " + JSON.stringify(data) : ""}`);
249
+ // Console noise is gone: warns toast through the fileLog wrapper (with
250
+ // a stderr fallback when no UI exists), and raw tails stay in the file
251
+ // log behind AGY_DEBUG.
213
252
  };
214
253
  const acpDriver = new AcpDriver({
215
254
  // Resolved per connection: the setup flow can install the binary and
@@ -793,7 +832,7 @@ function registerAgyCommand(pi: ExtensionAPI, ctx: AgyCommandCtx): void {
793
832
  ` sessions: ${ctx.store.size} bound`,
794
833
  ` models: ${ctx.entries.length} ${ctx.usingFallback ? "FALLBACK (agy models failed)" : "discovered"}`,
795
834
  ` config: ${CONFIG_PATH}`,
796
- ` logs: ${logsDir()} (attach recent days' files when reporting issues)`,
835
+ ` logs: ${logsDir()} (attach recent days' files when reporting issues; set AGY_DEBUG=1 to capture details)`,
797
836
  ];
798
837
  if (snap.engine === "acp" && snap.acp) {
799
838
  lines.push(
@@ -801,6 +840,11 @@ function registerAgyCommand(pi: ExtensionAPI, ctx: AgyCommandCtx): void {
801
840
  ` acp server: ${snap.acp.serverVersion ?? "unknown"}${snap.acp.agentTitle ? ` (${snap.acp.agentTitle})` : ""}`,
802
841
  ` acp stats: prompts=${snap.acp.prompts} created=${snap.acp.sessionsCreated} loaded=${snap.acp.sessionsLoaded} kills=${snap.acp.kills} reconnects=${snap.acp.reconnects} cancel=${snap.acp.cancelSupported === null ? "unprobed" : snap.acp.cancelSupported ? "supported" : "unsupported (kill+reload)"}`,
803
842
  );
843
+ // Gate B watch: silent while the server offers no token counts; one
844
+ // line the day it starts (then real usage mapping is worth wiring).
845
+ if (snap.acp.usageSeen) {
846
+ lines.push(" acp tokens: AVAILABLE in server payloads (wire real usage mapping next)");
847
+ }
804
848
  }
805
849
  if (snap.lifecycle.length > 0) {
806
850
  lines.push(" lifecycle (last 5):");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estebanforge/pi-antigravity-bridge",
3
- "version": "1.4.8",
3
+ "version": "1.4.9",
4
4
  "description": "Gemini provider for Pi on the Antigravity ACP server (official Google ACP) or the stream-json agy CLI. antigravity/* models in Pi's /model picker, no-patch MCP bridge: agy runs Pi's tools. ToS safe to use.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -24,6 +24,7 @@ import fs from "node:fs";
24
24
  import os from "node:os";
25
25
  import path from "node:path";
26
26
  import { parseAuthPort, readLastUrl } from "./browser-capture.js";
27
+ import { frameCarriesUsage } from "./events.js";
27
28
  import { JsonRpcResponseError, JsonRpcSession } from "./jsonrpc.js";
28
29
 
29
30
  export interface AcpMcpServer {
@@ -80,6 +81,7 @@ export class AcpConnection {
80
81
  #child: ChildProcess | undefined;
81
82
  #rpc: JsonRpcSession | undefined;
82
83
  #stderrTail = "";
84
+ #usageSeen = false;
83
85
  #exited = false;
84
86
  #killed = false;
85
87
  #suppressUpdates = false;
@@ -106,6 +108,13 @@ export class AcpConnection {
106
108
  return this.#stderrTail;
107
109
  }
108
110
 
111
+ /** Gate B watch: latched true once any session/update frame carried
112
+ * usage/token fields. /agy doctor surfaces it the day upstream starts
113
+ * sending token counts; silent until then. */
114
+ get usageSeen(): boolean {
115
+ return this.#usageSeen;
116
+ }
117
+
109
118
  /** While true, session/update notifications are dropped: they are the
110
119
  * full-text history replay that precedes a session/load response, never
111
120
  * live generation (run 6). */
@@ -299,6 +308,9 @@ export class AcpConnection {
299
308
 
300
309
  #onNotification(method: string, params: unknown): void {
301
310
  if (method === "session/update") {
311
+ // Latched before the suppression gate: history-replay frames are the
312
+ // server's payloads too, and they are the cheapest signal source.
313
+ if (!this.#usageSeen && frameCarriesUsage(params)) this.#usageSeen = true;
302
314
  if (this.#suppressUpdates) return; // load replay: history, not live text
303
315
  const p = typeof params === "object" && params !== null ? (params as Record<string, unknown>) : {};
304
316
  const sessionId = typeof p.sessionId === "string" ? p.sessionId : this.#updateSessionId;
package/src/acp/driver.ts CHANGED
@@ -409,7 +409,12 @@ export class AcpDriver implements TurnDriver {
409
409
  const turn = this.#active;
410
410
  this.#conn = undefined;
411
411
  this.#state = "dead";
412
- this.#log("connection-exited", { tail: info.stderrTail.slice(-200) });
412
+ // Teardown exits (abort kill, shutdown, idle recycle) are deliberate:
413
+ // their stderr tail must never reach the UI. Only a live turn dying
414
+ // mid-flight is user-reportable, and it surfaces as a normal turn
415
+ // error, not as raw console noise.
416
+ const turnActive = Boolean(turn && !turn.closed && !turn.aborted && !turn.sawResult);
417
+ this.#log("connection-exited", { expected: !turnActive, tail: info.stderrTail.slice(-200) });
413
418
  if (!turn || turn.closed) return;
414
419
  if (turn.aborted || turn.sawResult) {
415
420
  this.#settle(turn, {
@@ -421,7 +426,7 @@ export class AcpDriver implements TurnDriver {
421
426
  });
422
427
  return;
423
428
  }
424
- this.#failTurn(turn, info.stderrTail.trim() || "ACP server exited mid-turn");
429
+ this.#failTurn(turn, "Antigravity session exited unexpectedly; it restarts on the next turn");
425
430
  }
426
431
 
427
432
  #ensureConnection(request: DriverTurnRequest): Promise<AcpConnection> {
@@ -721,6 +726,7 @@ export class AcpDriver implements TurnDriver {
721
726
  reconnects: Math.max(0, this.#stats.spawns - 1),
722
727
  agentName: this.#agentInfo?.name,
723
728
  agentTitle: this.#agentInfo?.title,
729
+ usageSeen: this.#conn?.usageSeen ?? false,
724
730
  },
725
731
  };
726
732
  }
package/src/acp/events.ts CHANGED
@@ -248,3 +248,20 @@ export class TextAccumulator {
248
248
  return this.#acc;
249
249
  }
250
250
  }
251
+
252
+ const USAGE_KEY = /usage|tokens?/i;
253
+
254
+ /** Gate B watch: true when a server payload carries usage/token fields.
255
+ * Key-name based so future shapes (usage blocks, _meta.tokenCount, top-level
256
+ * token totals) all trip it. String values are exempt so auth-style "token"
257
+ * args inside tool-call frames cannot false-positive the latch; depth-capped
258
+ * so the walk stays cheap on every frame until it latches. */
259
+ export function frameCarriesUsage(value: unknown, depth = 0): boolean {
260
+ if (depth > 8 || typeof value !== "object" || value === null) return false;
261
+ if (Array.isArray(value)) return value.some((v) => frameCarriesUsage(v, depth + 1));
262
+ for (const [key, v] of Object.entries(value)) {
263
+ if (USAGE_KEY.test(key) && (typeof v === "number" || typeof v === "object")) return true;
264
+ if (frameCarriesUsage(v, depth + 1)) return true;
265
+ }
266
+ return false;
267
+ }
package/src/daily-log.ts CHANGED
@@ -15,10 +15,10 @@
15
15
  // redacted and long strings are truncated before they reach disk.
16
16
  // - Retention: files older than `retentionDays` are pruned once per
17
17
  // process, so the dir cannot grow unbounded.
18
- // - Two tiers, to keep SSD wear negligible for regular users: only
19
- // info/warn/error records (failures, turn/tool boundaries, commands,
20
- // setup) are written by default. Full verbose trails (per-event driver
21
- // lifecycle, raw bridge traffic) require AGY_DEBUG=1.
18
+ // - Volume tiers: default installs write ONLY errors routine logging
19
+ // costs the disk nothing. Warns surface as UI toasts instead (the
20
+ // extension wraps this logger); AGY_DEBUG=1 restores the full trail
21
+ // (debug/info/warn/error).
22
22
 
23
23
  import { appendFile, mkdir, readdir, unlink } from "node:fs/promises";
24
24
  import path from "node:path";
@@ -30,9 +30,9 @@ export interface DailyLoggerOptions {
30
30
  dir: string;
31
31
  /** Files older than this many days are pruned once per process. Default 14. */
32
32
  retentionDays?: number;
33
- /** Verbose gate. When false (default), debug-level records are dropped:
34
- * only info/warn/error land on disk, the light stream regular users
35
- * keep. AGY_DEBUG=1 (or this option) restores the full trail. */
33
+ /** Verbose gate. When false (default), only error-level records land on
34
+ * disk; debug/info/warn are dropped so regular users write nothing
35
+ * routine. AGY_DEBUG=1 (or this option) restores the full trail. */
36
36
  debug?: boolean;
37
37
  /** Injectable clock for tests. */
38
38
  now?: () => Date;
@@ -166,9 +166,10 @@ export function createDailyLogger(opts: DailyLoggerOptions): DailyLogger {
166
166
 
167
167
  return {
168
168
  log(event, data, level = "debug") {
169
- // Volume gate: debug is the verbose tier. Default installs write
170
- // only info/warn/error so the disk cost stays negligible.
171
- if (level === "debug" && !verbose) return;
169
+ // Volume gate: default installs write only errors so routine disk
170
+ // traffic is zero for regular users; the extension toasts warns
171
+ // instead. AGY_DEBUG=1 restores the full trail.
172
+ if (!verbose && level !== "error") return;
172
173
  write(level, event, data);
173
174
  },
174
175
  flush() {
@@ -125,6 +125,10 @@ export interface DriverSnapshot {
125
125
  /** From the initialize handshake agentInfo block. */
126
126
  agentName?: string;
127
127
  agentTitle?: string;
128
+ /** Gate B watch: true once this server process sent usage/token fields
129
+ * in any session/update frame. /agy doctor surfaces it when true and
130
+ * stays silent otherwise. */
131
+ usageSeen: boolean;
128
132
  };
129
133
  }
130
134
 
package/src/provider.ts CHANGED
@@ -114,12 +114,14 @@ export const SYSTEM_PROMPT_END = "[END SYSTEM PROMPT]";
114
114
 
115
115
  /** Brief tool-priority note appended inside every system prompt block: agy
116
116
  * runs embedded in pi, so its native interactive tools never reach the
117
- * user. Equivalent Pi Bridge tools must win. Concrete clash observed live:
118
- * agy picked its native ask_question over the bridge's ask_user_question
119
- * and the question never displayed. Rides the systemPrompt gate: the note
120
- * ships only when the system prompt ships. */
117
+ * user. Equivalent Pi Bridge tools must win. Concrete clashes observed
118
+ * live: agy picked its native ask_question over the bridge's
119
+ * ask_user_question and the question never displayed; on ACP its native
120
+ * view_file rejects real filesystem paths (artifact sandbox), so reads of
121
+ * the user's machine must go through the bridge. Rides the systemPrompt
122
+ * gate: the note ships only when the system prompt ships. */
121
123
  export const TOOL_PRIORITY_NOTE =
122
- "[Tool priority: this conversation runs inside pi, not as a standalone agy session; the user only sees what surfaces in pi. Native interactive tools, for example ask_question, never reach the user. When a Pi Bridge tool covers the same purpose, always use the Pi Bridge tool; for user questions use ask_user_question. Long-running bridge calls do not fail: after ~20 seconds the bridge answers STILL RUNNING with a callId; fetch the result with bridge_poll_result and poll until it lands. For work you already know is long, prefer exec_command's session-output pattern or background agents so you keep working while it runs.]";
124
+ "[Tool priority: this conversation runs inside pi, not as a standalone agy session; the user only sees what surfaces in pi. Native interactive tools, for example ask_question, never reach the user. When a Pi Bridge tool covers the same purpose, always use the Pi Bridge tool; for user questions use ask_user_question. Your native file tools such as view_file only read brain artifacts and reject real filesystem paths; for any path on the user's machine use the Pi Bridge tools (read, ls, grep, find, edit, execute). Long-running bridge calls do not fail: after ~20 seconds the bridge answers STILL RUNNING with a callId; fetch the result with bridge_poll_result and poll until it lands. For work you already know is long, prefer exec_command's session-output pattern or background agents so you keep working while it runs.]";
123
125
 
124
126
  /** Assemble the full agy prompt: system prompt block, pi-side digest, user
125
127
  * prompt. Empty parts are dropped. Pure; exported for unit testing.