@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 +17 -0
- package/README.md +6 -6
- package/extensions/index.ts +52 -8
- package/package.json +1 -1
- package/src/acp/connection.ts +12 -0
- package/src/acp/driver.ts +8 -2
- package/src/acp/events.ts +17 -0
- package/src/daily-log.ts +11 -10
- package/src/driver-types.ts +4 -0
- package/src/provider.ts +7 -5
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
package/extensions/index.ts
CHANGED
|
@@ -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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|
package/src/acp/connection.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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
|
-
// -
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
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),
|
|
34
|
-
*
|
|
35
|
-
*
|
|
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:
|
|
170
|
-
//
|
|
171
|
-
|
|
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() {
|
package/src/driver-types.ts
CHANGED
|
@@ -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
|
|
118
|
-
* agy picked its native ask_question over the bridge's
|
|
119
|
-
* and the question never displayed
|
|
120
|
-
*
|
|
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.
|