@pi-unipi/btw 3.0.0-alpha.35 → 3.0.0-alpha.37
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/extensions/btw.ts +66 -17
- package/package.json +2 -2
package/extensions/btw.ts
CHANGED
|
@@ -147,10 +147,17 @@ export function buildSeedEntries(ctx: {
|
|
|
147
147
|
// ─── Page history (session-memory only, never persisted) ───────────────────
|
|
148
148
|
|
|
149
149
|
export interface BtwPage {
|
|
150
|
+
/** Stable id (`btw-<pid>-<n>`), shared by the TUI panel and the app bridge
|
|
151
|
+
* (the bridge's `btw_delta`/`btw_end` `id` and `btw_list` page ids). */
|
|
152
|
+
id: string;
|
|
150
153
|
question: string;
|
|
151
154
|
/** One line per tool call: "… Reading <p>" while running → "✓ <desc>" */
|
|
152
155
|
toolLines: string[];
|
|
153
156
|
answer: string;
|
|
157
|
+
/** Text streamed so far (the final `answer` lands at the end). */
|
|
158
|
+
partial: string;
|
|
159
|
+
/** Model reasoning streamed so far (kept for `btw_list` replays). */
|
|
160
|
+
thinking: string;
|
|
154
161
|
error?: string;
|
|
155
162
|
aborted?: boolean;
|
|
156
163
|
done: boolean;
|
|
@@ -159,11 +166,44 @@ export interface BtwPage {
|
|
|
159
166
|
}
|
|
160
167
|
|
|
161
168
|
const pages: BtwPage[] = [];
|
|
169
|
+
let pageSeq = 0;
|
|
162
170
|
|
|
163
|
-
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
/** A fresh page with a process-unique id. */
|
|
172
|
+
export function newPage(question: string): BtwPage {
|
|
173
|
+
return { id: `btw-${process.pid}-${++pageSeq}`, question, toolLines: [], answer: "", partial: "", thinking: "", done: false };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** One page as the app bridge's `btw_list` sends it. `question`/`answer`/
|
|
177
|
+
* `error` keep their original meaning (older apps read only those); `id`,
|
|
178
|
+
* `done`/`running` and `toolLines` let the app reconcile its own pages.
|
|
179
|
+
* A running page's `answer` is the text streamed so far. */
|
|
180
|
+
export interface BtwListPage {
|
|
181
|
+
id: string;
|
|
182
|
+
question: string;
|
|
183
|
+
answer: string;
|
|
184
|
+
error?: string;
|
|
185
|
+
done: boolean;
|
|
186
|
+
running: boolean;
|
|
187
|
+
toolLines?: string[];
|
|
188
|
+
/** Running pages only: the tail of the model's reasoning so far. */
|
|
189
|
+
thinking?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Tail kept for a running page's `thinking` in `btw_list`. */
|
|
193
|
+
const LIST_THINKING_TAIL = 2000;
|
|
194
|
+
|
|
195
|
+
/** Read-only snapshot of recent pages for a `btw_list{}` reply — never mutate the result. */
|
|
196
|
+
export function listPages(): BtwListPage[] {
|
|
197
|
+
return pages.map((p) => ({
|
|
198
|
+
id: p.id,
|
|
199
|
+
question: p.question,
|
|
200
|
+
answer: p.done ? p.answer : p.partial,
|
|
201
|
+
...(p.error ? { error: p.error } : {}),
|
|
202
|
+
done: p.done,
|
|
203
|
+
running: !p.done,
|
|
204
|
+
...(p.toolLines.length ? { toolLines: [...p.toolLines] } : {}),
|
|
205
|
+
...(!p.done && p.thinking ? { thinking: p.thinking.slice(-LIST_THINKING_TAIL) } : {}),
|
|
206
|
+
}));
|
|
167
207
|
}
|
|
168
208
|
|
|
169
209
|
/** ↑/↓ paging reducer — clamp the target index into [0, count-1]. */
|
|
@@ -229,7 +269,9 @@ function runQuestion(
|
|
|
229
269
|
page: BtwPage,
|
|
230
270
|
thinkingLevel: AiThinkingLevel,
|
|
231
271
|
onUpdate: () => void,
|
|
232
|
-
|
|
272
|
+
/** `index` (tool deltas only): the `page.toolLines` slot this line fills —
|
|
273
|
+
* a tool's "done" line replaces its own "running" line. */
|
|
274
|
+
onDelta?: (kind: BtwDeltaKind, text: string, index?: number) => void,
|
|
233
275
|
): BtwRun {
|
|
234
276
|
let session: AgentSession | null = null;
|
|
235
277
|
let unsubscribe: (() => void) | null = null;
|
|
@@ -270,7 +312,7 @@ function runQuestion(
|
|
|
270
312
|
const line = formatToolLine(e.toolName, e.args, ctx.cwd, "running");
|
|
271
313
|
page.toolLines.push(line);
|
|
272
314
|
onUpdate();
|
|
273
|
-
onDelta?.("tool", line);
|
|
315
|
+
onDelta?.("tool", line, page.toolLines.length - 1);
|
|
274
316
|
} else if (event.type === "tool_execution_end") {
|
|
275
317
|
const e = event as { toolCallId: string; toolName: string; isError: boolean };
|
|
276
318
|
const start = pendingStarts.get(e.toolCallId);
|
|
@@ -280,11 +322,17 @@ function runQuestion(
|
|
|
280
322
|
else page.toolLines.push(line);
|
|
281
323
|
pendingStarts.delete(e.toolCallId);
|
|
282
324
|
onUpdate();
|
|
283
|
-
onDelta?.("tool", line);
|
|
325
|
+
onDelta?.("tool", line, idx ?? page.toolLines.length - 1);
|
|
284
326
|
} else if (event.type === "message_update") {
|
|
285
327
|
const e = event as { assistantMessageEvent?: { type?: string; delta?: string } };
|
|
286
|
-
if (e.assistantMessageEvent?.type === "text_delta" && e.assistantMessageEvent.delta)
|
|
287
|
-
|
|
328
|
+
if (e.assistantMessageEvent?.type === "text_delta" && e.assistantMessageEvent.delta) {
|
|
329
|
+
page.partial += e.assistantMessageEvent.delta;
|
|
330
|
+
onDelta?.("text", e.assistantMessageEvent.delta);
|
|
331
|
+
}
|
|
332
|
+
else if (e.assistantMessageEvent?.type === "thinking_delta" && e.assistantMessageEvent.delta) {
|
|
333
|
+
page.thinking += e.assistantMessageEvent.delta;
|
|
334
|
+
onDelta?.("thinking", e.assistantMessageEvent.delta);
|
|
335
|
+
}
|
|
288
336
|
}
|
|
289
337
|
});
|
|
290
338
|
|
|
@@ -481,7 +529,7 @@ class BtwPanel implements Component {
|
|
|
481
529
|
|
|
482
530
|
/** One streamed update from `BtwApi.ask` (mirrors the bridge's `btw_delta`/`btw_end`). */
|
|
483
531
|
export type BtwEvent =
|
|
484
|
-
| { type: "delta"; kind: BtwDeltaKind; text: string }
|
|
532
|
+
| { type: "delta"; kind: BtwDeltaKind; text: string; index?: number }
|
|
485
533
|
| { type: "end"; answer: string; error?: string; usage?: { input: number; output: number; totalTokens: number } };
|
|
486
534
|
|
|
487
535
|
export interface BtwApi {
|
|
@@ -489,8 +537,8 @@ export interface BtwApi {
|
|
|
489
537
|
* Only one question runs at a time (shared with the TUI panel): a second call while
|
|
490
538
|
* one is in flight ends immediately with an error. */
|
|
491
539
|
ask(cctx: ExtensionCommandContext, question: string, onEvent: (event: BtwEvent) => void): { id: string; finished: Promise<void> };
|
|
492
|
-
/** Recent pages
|
|
493
|
-
list():
|
|
540
|
+
/** Recent pages for this pi session — shared with the TUI panel. */
|
|
541
|
+
list(): BtwListPage[];
|
|
494
542
|
}
|
|
495
543
|
|
|
496
544
|
const BTW_API_KEY = Symbol.for("unipi.btw.api");
|
|
@@ -502,12 +550,13 @@ function publishUiFreeApi(pi: ExtensionAPI, getActiveRun: () => BtwRun | null, s
|
|
|
502
550
|
let idSeq = 0;
|
|
503
551
|
const api: BtwApi = {
|
|
504
552
|
ask(cctx, question, onEvent) {
|
|
505
|
-
const id = `api-${process.pid}-${++idSeq}`;
|
|
506
553
|
if (getActiveRun()) {
|
|
554
|
+
const id = `btw-${process.pid}-busy-${++idSeq}`;
|
|
507
555
|
const finished = Promise.resolve().then(() => onEvent({ type: "end", answer: "", error: "btw is already answering another question" }));
|
|
508
556
|
return { id, finished };
|
|
509
557
|
}
|
|
510
|
-
const page
|
|
558
|
+
const page = newPage(question);
|
|
559
|
+
const id = page.id;
|
|
511
560
|
pages.push(page);
|
|
512
561
|
const run = runQuestion(
|
|
513
562
|
cctx,
|
|
@@ -515,7 +564,7 @@ function publishUiFreeApi(pi: ExtensionAPI, getActiveRun: () => BtwRun | null, s
|
|
|
515
564
|
page,
|
|
516
565
|
pi.getThinkingLevel() as AiThinkingLevel,
|
|
517
566
|
() => {},
|
|
518
|
-
(kind, text) => onEvent({ type: "delta", kind, text }),
|
|
567
|
+
(kind, text, index) => onEvent(index === undefined ? { type: "delta", kind, text } : { type: "delta", kind, text, index }),
|
|
519
568
|
);
|
|
520
569
|
setActiveRun(run);
|
|
521
570
|
const finished = run.finished.then(() => {
|
|
@@ -545,7 +594,7 @@ export default function btwExtension(pi: ExtensionAPI): void {
|
|
|
545
594
|
|
|
546
595
|
function startRun(ctx: ExtensionCommandContext, question: string): void {
|
|
547
596
|
if (activeRun) return; // never two answers at once
|
|
548
|
-
const page
|
|
597
|
+
const page = newPage(question);
|
|
549
598
|
const index = pages.length;
|
|
550
599
|
pages.push(page);
|
|
551
600
|
const run = runQuestion(ctx, question, page, pi.getThinkingLevel() as AiThinkingLevel, () => panelRef?.tick());
|
|
@@ -589,7 +638,7 @@ export default function btwExtension(pi: ExtensionAPI): void {
|
|
|
589
638
|
}
|
|
590
639
|
|
|
591
640
|
async function headlessAsk(ctx: ExtensionCommandContext, question: string): Promise<void> {
|
|
592
|
-
const page
|
|
641
|
+
const page = newPage(question);
|
|
593
642
|
pages.push(page);
|
|
594
643
|
notify(ctx, `btw: ${question}`, "info");
|
|
595
644
|
const run = runQuestion(ctx, question, page, pi.getThinkingLevel() as AiThinkingLevel, () => {});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/btw",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.37",
|
|
4
4
|
"description": "A pi extension for parallel side conversations with /unipi:btw — part of the Unipi suite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "extensions/btw.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@earendil-works/pi-tui": "^0.87.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
40
|
+
"@pi-unipi/core": "3.0.0-alpha.37"
|
|
41
41
|
},
|
|
42
42
|
"pi": {
|
|
43
43
|
"extensions": [
|