@pi-unipi/btw 2.6.1 → 2.9.0
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 +34 -47
- package/package.json +12 -8
package/extensions/btw.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
type ExtensionAPI,
|
|
27
27
|
type ExtensionCommandContext,
|
|
28
28
|
type ExtensionContext,
|
|
29
|
+
type ModelRuntime,
|
|
29
30
|
type ResourceLoader,
|
|
30
31
|
} from "@earendil-works/pi-coding-agent";
|
|
31
32
|
import { type AssistantMessage, type Message, type ThinkingLevel as AiThinkingLevel, type UserMessage } from "@earendil-works/pi-ai";
|
|
@@ -161,6 +162,17 @@ function stripDynamicSystemPromptFooter(systemPrompt: string): string {
|
|
|
161
162
|
.trim();
|
|
162
163
|
}
|
|
163
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Extract the canonical ModelRuntime from the extension-facing ModelRegistry
|
|
167
|
+
* facade. The SDK (>=0.84) exposes `modelRuntime` on AgentSession and expects
|
|
168
|
+
* it in CreateAgentSessionOptions; the extension context only hands us the
|
|
169
|
+
* ModelRegistry wrapper, whose `runtime` field is the same instance the
|
|
170
|
+
* parent session uses (agent-session wires `new ModelRegistry(this._modelRuntime)`).
|
|
171
|
+
*/
|
|
172
|
+
function sessionModelRuntime(ctx: ExtensionCommandContext): ModelRuntime {
|
|
173
|
+
return (ctx.modelRegistry as unknown as { runtime: ModelRuntime }).runtime;
|
|
174
|
+
}
|
|
175
|
+
|
|
164
176
|
function createBtwResourceLoader(
|
|
165
177
|
ctx: ExtensionCommandContext,
|
|
166
178
|
appendSystemPrompt: string[] = [BTW_SYSTEM_PROMPT],
|
|
@@ -175,7 +187,9 @@ function createBtwResourceLoader(
|
|
|
175
187
|
getThemes: () => ({ themes: [], diagnostics: [] }),
|
|
176
188
|
getAgentsFiles: () => ({ agentsFiles: [] }),
|
|
177
189
|
getSystemPrompt: () => systemPrompt,
|
|
190
|
+
getSystemPromptSource: () => undefined,
|
|
178
191
|
getAppendSystemPrompt: () => appendSystemPrompt,
|
|
192
|
+
getAppendSystemPromptSources: () => [],
|
|
179
193
|
extendResources: () => {},
|
|
180
194
|
reload: async () => {},
|
|
181
195
|
};
|
|
@@ -606,9 +620,8 @@ function applyAssistantMessageToTranscript(
|
|
|
606
620
|
message: AssistantMessage,
|
|
607
621
|
streaming: boolean,
|
|
608
622
|
): void {
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
const answer = extractMessageText(assistantMessage);
|
|
623
|
+
const thinking = extractThinking(message);
|
|
624
|
+
const answer = extractMessageText(message);
|
|
612
625
|
|
|
613
626
|
if (thinking) {
|
|
614
627
|
upsertTranscriptTextEntry(state, turnId, "thinking", thinking, streaming);
|
|
@@ -972,11 +985,6 @@ function getOverlayTitle(mode: BtwThreadMode): string {
|
|
|
972
985
|
|
|
973
986
|
class BtwOverlayComponent extends Container implements Focusable {
|
|
974
987
|
private readonly input: Input;
|
|
975
|
-
private readonly transcript: Container;
|
|
976
|
-
private readonly statusText: Text;
|
|
977
|
-
private readonly modeText: Text;
|
|
978
|
-
private readonly summaryText: Text;
|
|
979
|
-
private readonly hintsText: Text;
|
|
980
988
|
private readonly readTranscriptEntries: () => BtwTranscript;
|
|
981
989
|
private readonly getStatus: () => string | null;
|
|
982
990
|
private readonly getMode: () => BtwThreadMode;
|
|
@@ -1025,11 +1033,6 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1025
1033
|
this.onDismissCallback = onDismiss;
|
|
1026
1034
|
this.onUnfocusCallback = onUnfocus;
|
|
1027
1035
|
|
|
1028
|
-
this.modeText = new Text("", 1, 0);
|
|
1029
|
-
this.summaryText = new Text("", 1, 0);
|
|
1030
|
-
this.transcript = new Container();
|
|
1031
|
-
this.statusText = new Text("", 1, 0);
|
|
1032
|
-
|
|
1033
1036
|
this.input = new Input();
|
|
1034
1037
|
this.input.onSubmit = (value) => {
|
|
1035
1038
|
this.followTranscript = true;
|
|
@@ -1039,8 +1042,6 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1039
1042
|
this.onDismissCallback();
|
|
1040
1043
|
};
|
|
1041
1044
|
|
|
1042
|
-
this.hintsText = new Text("", 1, 0);
|
|
1043
|
-
|
|
1044
1045
|
const originalHandleInput = this.input.handleInput.bind(this.input);
|
|
1045
1046
|
this.input.handleInput = (data: string) => {
|
|
1046
1047
|
if (keybindings.matches(data, "tui.select.cancel")) {
|
|
@@ -1092,14 +1093,14 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1092
1093
|
return;
|
|
1093
1094
|
}
|
|
1094
1095
|
|
|
1095
|
-
if (matchesKey(data, Key.pageUp)) {
|
|
1096
|
+
if (matchesKey(data, Key.pageUp) || matchesKey(data, Key.ctrl("pageUp")) || matchesKey(data, Key.alt("pageUp"))) {
|
|
1096
1097
|
this.followTranscript = false;
|
|
1097
1098
|
this.transcriptScrollOffset = Math.max(0, this.transcriptScrollOffset - Math.max(1, this.transcriptViewportHeight - 1));
|
|
1098
1099
|
this.tui.requestRender();
|
|
1099
1100
|
return;
|
|
1100
1101
|
}
|
|
1101
1102
|
|
|
1102
|
-
if (matchesKey(data, Key.pageDown)) {
|
|
1103
|
+
if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("pageDown")) || matchesKey(data, Key.alt("pageDown"))) {
|
|
1103
1104
|
this.transcriptScrollOffset += Math.max(1, this.transcriptViewportHeight - 1);
|
|
1104
1105
|
this.tui.requestRender();
|
|
1105
1106
|
return;
|
|
@@ -1182,30 +1183,18 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1182
1183
|
return this.input.getValue();
|
|
1183
1184
|
}
|
|
1184
1185
|
|
|
1185
|
-
getTranscriptEntries(): BtwTranscript {
|
|
1186
|
-
return this.readTranscriptEntries().map((entry) => ({ ...entry }));
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
1186
|
refresh(): void {
|
|
1190
1187
|
this.modeTextValue = `${getOverlayTitle(this.getMode())} · hidden thread preserved`;
|
|
1191
|
-
this.modeText.setText(this.modeTextValue);
|
|
1192
1188
|
const entries = this.readTranscriptEntries();
|
|
1193
1189
|
const exchanges = getCompletedExchangeCount(entries);
|
|
1194
1190
|
const active = hasStreamingTranscriptEntry(entries) ? " · streaming" : " · idle";
|
|
1195
1191
|
this.summaryTextValue = `${exchanges} exchange${exchanges === 1 ? "" : "s"}${active}`;
|
|
1196
|
-
this.summaryText.setText(this.summaryTextValue);
|
|
1197
1192
|
|
|
1198
1193
|
this.transcriptLines = buildOverlayTranscript(entries, this.theme);
|
|
1199
|
-
this.transcript.clear();
|
|
1200
|
-
for (const line of this.transcriptLines) {
|
|
1201
|
-
this.transcript.addChild(new Text(line, 1, 0));
|
|
1202
|
-
}
|
|
1203
1194
|
|
|
1204
1195
|
const status = this.getStatus() ?? "Ready. Enter submits; Escape dismisses without clearing.";
|
|
1205
1196
|
this.statusTextValue = status;
|
|
1206
|
-
this.
|
|
1207
|
-
this.hintsTextValue = "Enter submit · Alt+/ toggle focus · Escape dismiss · PgUp/PgDn scroll";
|
|
1208
|
-
this.hintsText.setText(this.hintsTextValue);
|
|
1197
|
+
this.hintsTextValue = "Enter submit · Alt+/ toggle focus · Escape dismiss · PgUp/Ctrl+PgUp scroll up · PgDn/Ctrl+PgDn scroll down";
|
|
1209
1198
|
this.tui.requestRender();
|
|
1210
1199
|
}
|
|
1211
1200
|
}
|
|
@@ -1364,7 +1353,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1364
1353
|
const { session } = await createAgentSession({
|
|
1365
1354
|
sessionManager: SessionManager.inMemory(),
|
|
1366
1355
|
model: ctx.model,
|
|
1367
|
-
|
|
1356
|
+
modelRuntime: sessionModelRuntime(ctx),
|
|
1368
1357
|
thinkingLevel: pi.getThinkingLevel() as SessionThinkingLevel,
|
|
1369
1358
|
tools: ["read", "bash", "edit", "write"],
|
|
1370
1359
|
resourceLoader: createBtwResourceLoader(ctx),
|
|
@@ -1498,7 +1487,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1498
1487
|
});
|
|
1499
1488
|
}
|
|
1500
1489
|
|
|
1501
|
-
async function dispatchBtwCommand(name: string, args: string, ctx: ExtensionCommandContext): Promise<
|
|
1490
|
+
async function dispatchBtwCommand(name: string, args: string, ctx: ExtensionCommandContext): Promise<void> {
|
|
1502
1491
|
const trimmedArgs = args.trim();
|
|
1503
1492
|
|
|
1504
1493
|
if (name === "btw") {
|
|
@@ -1506,7 +1495,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1506
1495
|
if (!question) {
|
|
1507
1496
|
await ensureBtwSession(ctx, pendingMode);
|
|
1508
1497
|
await ensureOverlay(ctx);
|
|
1509
|
-
return
|
|
1498
|
+
return;
|
|
1510
1499
|
}
|
|
1511
1500
|
|
|
1512
1501
|
if (pendingMode !== "contextual") {
|
|
@@ -1514,7 +1503,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1514
1503
|
}
|
|
1515
1504
|
|
|
1516
1505
|
await runBtw(ctx, question, save, "contextual");
|
|
1517
|
-
return
|
|
1506
|
+
return;
|
|
1518
1507
|
}
|
|
1519
1508
|
|
|
1520
1509
|
if (name === "btw:tangent") {
|
|
@@ -1526,11 +1515,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
1526
1515
|
if (!question) {
|
|
1527
1516
|
await ensureBtwSession(ctx, "tangent");
|
|
1528
1517
|
await ensureOverlay(ctx);
|
|
1529
|
-
return
|
|
1518
|
+
return;
|
|
1530
1519
|
}
|
|
1531
1520
|
|
|
1532
1521
|
await runBtw(ctx, question, save, "tangent");
|
|
1533
|
-
return
|
|
1522
|
+
return;
|
|
1534
1523
|
}
|
|
1535
1524
|
|
|
1536
1525
|
if (name === "btw:new") {
|
|
@@ -1544,20 +1533,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
1544
1533
|
await ensureOverlay(ctx);
|
|
1545
1534
|
notify(ctx, "Started a fresh BTW thread.", "info");
|
|
1546
1535
|
}
|
|
1547
|
-
return
|
|
1536
|
+
return;
|
|
1548
1537
|
}
|
|
1549
1538
|
|
|
1550
1539
|
if (name === "btw:clear") {
|
|
1551
1540
|
await resetThread(ctx);
|
|
1552
1541
|
dismissOverlay();
|
|
1553
1542
|
notify(ctx, "Cleared BTW thread.", "info");
|
|
1554
|
-
return
|
|
1543
|
+
return;
|
|
1555
1544
|
}
|
|
1556
1545
|
|
|
1557
1546
|
if (name === "btw:inject") {
|
|
1558
1547
|
if (pendingThread.length === 0) {
|
|
1559
1548
|
notify(ctx, "No BTW thread to inject.", "warning");
|
|
1560
|
-
return
|
|
1549
|
+
return;
|
|
1561
1550
|
}
|
|
1562
1551
|
|
|
1563
1552
|
setOverlayStatus("⏳ injecting into the main session...", ctx);
|
|
@@ -1579,13 +1568,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
1579
1568
|
setOverlayStatus("Inject failed. Thread preserved for retry or summarize.", ctx);
|
|
1580
1569
|
notify(ctx, error instanceof Error ? error.message : String(error), "error");
|
|
1581
1570
|
}
|
|
1582
|
-
return
|
|
1571
|
+
return;
|
|
1583
1572
|
}
|
|
1584
1573
|
|
|
1585
1574
|
if (name === "btw:summarize") {
|
|
1586
1575
|
if (pendingThread.length === 0) {
|
|
1587
1576
|
notify(ctx, "No BTW thread to summarize.", "warning");
|
|
1588
|
-
return
|
|
1577
|
+
return;
|
|
1589
1578
|
}
|
|
1590
1579
|
|
|
1591
1580
|
setOverlayStatus("⏳ summarizing...", ctx);
|
|
@@ -1608,10 +1597,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
1608
1597
|
setOverlayStatus("Summarize failed. Thread preserved for retry or injection.", ctx);
|
|
1609
1598
|
notify(ctx, error instanceof Error ? error.message : String(error), "error");
|
|
1610
1599
|
}
|
|
1611
|
-
return
|
|
1600
|
+
return;
|
|
1612
1601
|
}
|
|
1613
|
-
|
|
1614
|
-
return false;
|
|
1615
1602
|
}
|
|
1616
1603
|
|
|
1617
1604
|
function parseOverlayBtwCommand(value: string): { name: string; args: string } | null {
|
|
@@ -1817,7 +1804,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1817
1804
|
|
|
1818
1805
|
async function getBtwHandoffThread(
|
|
1819
1806
|
ctx: ExtensionCommandContext,
|
|
1820
|
-
): Promise<{
|
|
1807
|
+
): Promise<{ thread: BtwHandoffExchange[] }> {
|
|
1821
1808
|
const sessionRuntime = activeBtwSession ?? (await ensureBtwSession(ctx, pendingMode));
|
|
1822
1809
|
const thread = sessionRuntime ? extractBtwHandoffThread(sessionRuntime) : [];
|
|
1823
1810
|
const resolvedThread = thread.length > 0 ? thread : getPendingThreadForHandoff();
|
|
@@ -1826,7 +1813,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1826
1813
|
throw new Error("No BTW thread available for handoff.");
|
|
1827
1814
|
}
|
|
1828
1815
|
|
|
1829
|
-
return {
|
|
1816
|
+
return { thread: resolvedThread };
|
|
1830
1817
|
}
|
|
1831
1818
|
|
|
1832
1819
|
async function summarizeThread(ctx: ExtensionCommandContext, thread: BtwHandoffExchange[]): Promise<string> {
|
|
@@ -1843,7 +1830,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1843
1830
|
const { session } = await createAgentSession({
|
|
1844
1831
|
sessionManager: SessionManager.inMemory(),
|
|
1845
1832
|
model,
|
|
1846
|
-
|
|
1833
|
+
modelRuntime: sessionModelRuntime(ctx),
|
|
1847
1834
|
thinkingLevel: "off",
|
|
1848
1835
|
tools: [],
|
|
1849
1836
|
resourceLoader: createBtwResourceLoader(ctx, [BTW_SUMMARIZE_SYSTEM_PROMPT]),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/btw",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "A pi extension for parallel side conversations with /unipi:btw
|
|
3
|
+
"version": "2.9.0",
|
|
4
|
+
"description": "A pi extension for parallel side conversations with /unipi:btw \u2014 part of the Unipi suite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "extensions/btw.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -32,16 +32,20 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@earendil-works/pi-ai": "^0.
|
|
36
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
37
|
-
"@earendil-works/pi-tui": "^0.
|
|
35
|
+
"@earendil-works/pi-ai": "^0.84.0",
|
|
36
|
+
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
37
|
+
"@earendil-works/pi-tui": "^0.84.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@pi-unipi/core": "2.
|
|
40
|
+
"@pi-unipi/core": "2.9.0"
|
|
41
41
|
},
|
|
42
42
|
"pi": {
|
|
43
|
-
"extensions": [
|
|
44
|
-
|
|
43
|
+
"extensions": [
|
|
44
|
+
"./extensions/btw.ts"
|
|
45
|
+
],
|
|
46
|
+
"skills": [
|
|
47
|
+
"./skills"
|
|
48
|
+
],
|
|
45
49
|
"prompts": [],
|
|
46
50
|
"themes": []
|
|
47
51
|
}
|