pi-bro 0.19.2 → 0.19.3
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 +7 -0
- package/backend.ts +15 -6
- package/bro.ts +20 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pi-bro are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.19.3] - 2026-09-30
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Send large Agy explain, Show, and BTW prompts over stdin instead of exceeding argument-size limits. Older Agy versions report an upgrade hint rather than silently falling back (#73).
|
|
10
|
+
- Retain newest turns and message tails when bounding Show and BTW context, preserving JSON message framing (#73).
|
|
11
|
+
|
|
5
12
|
## [0.19.2] - 2026-09-30
|
|
6
13
|
|
|
7
14
|
### Fixed
|
package/backend.ts
CHANGED
|
@@ -211,8 +211,8 @@ export function parseBtwAgyLine(line: string): { delta?: string; result?: string
|
|
|
211
211
|
return { conversationId };
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
//
|
|
215
|
-
//
|
|
214
|
+
// Explain/show and BTW use argv for small prompts and stdin NDJSON for large
|
|
215
|
+
// prompts; sandbox, cwd, timeout, and continuation semantics stay unchanged.
|
|
216
216
|
async function executeArgvPrint(
|
|
217
217
|
request: BackendRequest,
|
|
218
218
|
selection: AgySelection,
|
|
@@ -221,6 +221,9 @@ async function executeArgvPrint(
|
|
|
221
221
|
killEscalationMs: number,
|
|
222
222
|
deadlineMsOverride: number | undefined,
|
|
223
223
|
): Promise<BackendOutcome> {
|
|
224
|
+
// Keep legacy CLI compatibility for small prompts; large inputs use the advisor's
|
|
225
|
+
// stdin protocol rather than risking Linux's per-argument byte limit.
|
|
226
|
+
const stdinPrompt = Buffer.byteLength(request.prompt, "utf8") >= 120_000;
|
|
224
227
|
const isBtw = request.feature === "btw";
|
|
225
228
|
const full = isBtw && request.access === "workspace-full";
|
|
226
229
|
const deadlineMs = deadlineMsOverride ?? (isBtw ? (full ? 610_000 : 130_000) : 125_000);
|
|
@@ -251,12 +254,11 @@ async function executeArgvPrint(
|
|
|
251
254
|
"--print-timeout",
|
|
252
255
|
printTimeout,
|
|
253
256
|
...(request.continuation ? ["--conversation", request.continuation.id] : []),
|
|
254
|
-
"--print",
|
|
255
|
-
request.prompt,
|
|
257
|
+
...(stdinPrompt ? ["--input-format", "stream-json"] : ["--print", request.prompt]),
|
|
256
258
|
],
|
|
257
259
|
{
|
|
258
260
|
cwd: full ? request.cwd : runDirectory,
|
|
259
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
261
|
+
stdio: [stdinPrompt ? "pipe" : "ignore", "pipe", "pipe"],
|
|
260
262
|
windowsHide: true,
|
|
261
263
|
detached: process.platform !== "win32",
|
|
262
264
|
},
|
|
@@ -278,6 +280,11 @@ async function executeArgvPrint(
|
|
|
278
280
|
processError = error;
|
|
279
281
|
});
|
|
280
282
|
|
|
283
|
+
if (stdinPrompt) {
|
|
284
|
+
child.stdin?.on("error", () => { /* Report early exits through the close/error path. */ });
|
|
285
|
+
child.stdin?.end(`${JSON.stringify({ event: "user", message: { content: request.prompt } })}\n`);
|
|
286
|
+
}
|
|
287
|
+
|
|
281
288
|
const lines = createInterface({ input: child.stdout!, crlfDelay: Infinity });
|
|
282
289
|
try {
|
|
283
290
|
for await (const line of lines) {
|
|
@@ -327,7 +334,9 @@ async function executeArgvPrint(
|
|
|
327
334
|
if (exitSignal || code === null) {
|
|
328
335
|
return { status: "failure", message: unexpectedSignalMessage(exitSignal), partialText: partial || undefined };
|
|
329
336
|
}
|
|
330
|
-
if (code !== 0) return { status: "failure", message:
|
|
337
|
+
if (code !== 0) return { status: "failure", message: stdinPrompt && advisorFlagErrorHint(stderr)
|
|
338
|
+
? withDoctor("Large prompts require Agy 1.1.15+ with stdin support. Run `agy update`. " + stderr.trim())
|
|
339
|
+
: agyFailureMessage(action, { code, killed: false, stderr }) };
|
|
331
340
|
|
|
332
341
|
const text = final.trim();
|
|
333
342
|
if (!text) return { status: "failure", message: withDoctor(stderr.trim() || emptyTextMessage) };
|
package/bro.ts
CHANGED
|
@@ -1475,7 +1475,8 @@ function serializeShowTurns(turns: readonly ShowTurn[]): string {
|
|
|
1475
1475
|
return turns.flatMap((turn) => turn.entries).join("\n\n");
|
|
1476
1476
|
}
|
|
1477
1477
|
|
|
1478
|
-
export function captureShowTranscript(ctx: ExtensionCommandContext, turnsRequested: number): BroSource | undefined {
|
|
1478
|
+
export function captureShowTranscript(ctx: ExtensionCommandContext, turnsRequested: number, maxLength = MAX_TEXT_LENGTH): BroSource | undefined {
|
|
1479
|
+
if (!Number.isSafeInteger(maxLength) || maxLength < 128) throw new Error("Transcript limit must be an integer of at least 128 characters.");
|
|
1479
1480
|
const turns: ShowTurn[] = [];
|
|
1480
1481
|
for (const entry of ctx.sessionManager.getBranch()) {
|
|
1481
1482
|
if (entry.type !== "message") continue;
|
|
@@ -1503,7 +1504,7 @@ export function captureShowTranscript(ctx: ExtensionCommandContext, turnsRequest
|
|
|
1503
1504
|
if (seen === 0) return undefined;
|
|
1504
1505
|
|
|
1505
1506
|
let text = serializeShowTurns(turns.slice(start));
|
|
1506
|
-
while (text.length >
|
|
1507
|
+
while (text.length > maxLength && start < turns.length - 1) {
|
|
1507
1508
|
let next = turns.length;
|
|
1508
1509
|
for (let index = start + 1; index < turns.length; index += 1) {
|
|
1509
1510
|
if (turns[index]!.startsTurn) {
|
|
@@ -1515,7 +1516,22 @@ export function captureShowTranscript(ctx: ExtensionCommandContext, turnsRequest
|
|
|
1515
1516
|
start = next;
|
|
1516
1517
|
text = serializeShowTurns(turns.slice(start));
|
|
1517
1518
|
}
|
|
1518
|
-
if (text.length >
|
|
1519
|
+
if (text.length > maxLength) {
|
|
1520
|
+
// Prefer the newest complete messages even within an oversized single turn.
|
|
1521
|
+
const entries = turns.slice(start).flatMap(turn => turn.entries);
|
|
1522
|
+
const notice = "[… earlier messages truncated …]\n";
|
|
1523
|
+
while (entries.length > 1 && entries.join("\n\n").length + notice.length > maxLength) entries.shift();
|
|
1524
|
+
text = entries.join("\n\n");
|
|
1525
|
+
if (text.length + notice.length > maxLength) {
|
|
1526
|
+
const newline = text.indexOf("\n");
|
|
1527
|
+
const header = text.slice(0, newline);
|
|
1528
|
+
const content = JSON.parse(text.slice(newline + 1)) as string;
|
|
1529
|
+
const marker = "[… earlier text truncated …] ";
|
|
1530
|
+
let tail = content.slice(-(maxLength - header.length - marker.length - 8));
|
|
1531
|
+
while (`${header}\n${JSON.stringify(marker + tail)}`.length > maxLength) tail = tail.slice(Math.max(1, Math.floor(tail.length / 10)));
|
|
1532
|
+
text = `${header}\n${JSON.stringify(marker + tail)}`;
|
|
1533
|
+
} else text = notice + text;
|
|
1534
|
+
}
|
|
1519
1535
|
const kept = turns.slice(start).filter((turn) => turn.startsTurn).length;
|
|
1520
1536
|
return { text: text.trim(), label: `last ${Math.max(1, kept)} turn${kept > 1 ? "s" : ""} · conversation only` };
|
|
1521
1537
|
}
|
|
@@ -3322,10 +3338,7 @@ async function openBtwModal(
|
|
|
3322
3338
|
}
|
|
3323
3339
|
if (thread.turns.length === 0) {
|
|
3324
3340
|
thread.conversationId = undefined;
|
|
3325
|
-
|
|
3326
|
-
if (context && context.length > BTW_CONTEXT_MAX) {
|
|
3327
|
-
context = `${context.slice(0, BTW_CONTEXT_MAX)}\n[… context truncated …]`;
|
|
3328
|
-
}
|
|
3341
|
+
const context = captureShowTranscript(ctx, BTW_CONTEXT_TURNS, BTW_CONTEXT_MAX)?.text;
|
|
3329
3342
|
thread.context = context;
|
|
3330
3343
|
}
|
|
3331
3344
|
// Resume natively when possible; otherwise seed the fresh native session with the main-session
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-bro",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"description": "An Earendil Pi extension that explains pasted text, assistant responses, local documents, public webpages, and recent session turns (as shapes) in a context-isolated window, opens a sandboxed side conversation with /bro btw, and provides a second-opinion advisor tool for executor agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|