@mariozechner/pi-coding-agent 0.36.0 → 0.37.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/CHANGELOG.md +24 -0
- package/README.md +27 -2
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +25 -8
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +9 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +115 -18
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/export-html/template.css +55 -0
- package/dist/core/export-html/template.js +112 -7
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +1 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +9 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -3
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +50 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +39 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
- package/dist/modes/interactive/components/login-dialog.js +135 -0
- package/dist/modes/interactive/components/login-dialog.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +211 -84
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +4 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +2 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/sdk.md +12 -4
- package/examples/extensions/claude-rules.ts +83 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +7 -1
- package/examples/sdk/06-extensions.ts +5 -7
- package/examples/sdk/12-full-control.ts +2 -6
- package/package.json +12 -8
|
@@ -7,8 +7,9 @@ import * as fs from "node:fs";
|
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import Clipboard from "@crosscopy/clipboard";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { getOAuthProviders } from "@mariozechner/pi-ai";
|
|
11
|
+
import { CombinedAutocompleteProvider, Container, getEditorKeybindings, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
|
|
12
|
+
import { spawn, spawnSync } from "child_process";
|
|
12
13
|
import { APP_NAME, getAuthPath, getDebugLogPath } from "../../config.js";
|
|
13
14
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
14
15
|
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
@@ -30,6 +31,7 @@ import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
|
30
31
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
31
32
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
32
33
|
import { FooterComponent } from "./components/footer.js";
|
|
34
|
+
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
33
35
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
34
36
|
import { OAuthSelectorComponent } from "./components/oauth-selector.js";
|
|
35
37
|
import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
@@ -86,6 +88,8 @@ export class InteractiveMode {
|
|
|
86
88
|
// Auto-retry state
|
|
87
89
|
retryLoader = undefined;
|
|
88
90
|
retryEscapeHandler;
|
|
91
|
+
// Messages queued while compaction is running
|
|
92
|
+
compactionQueuedMessages = [];
|
|
89
93
|
// Extension UI state
|
|
90
94
|
extensionSelector = undefined;
|
|
91
95
|
extensionInput = undefined;
|
|
@@ -361,6 +365,7 @@ export class InteractiveMode {
|
|
|
361
365
|
// Clear UI state
|
|
362
366
|
this.chatContainer.clear();
|
|
363
367
|
this.pendingMessagesContainer.clear();
|
|
368
|
+
this.compactionQueuedMessages = [];
|
|
364
369
|
this.streamingComponent = undefined;
|
|
365
370
|
this.streamingMessage = undefined;
|
|
366
371
|
this.pendingTools.clear();
|
|
@@ -852,13 +857,7 @@ export class InteractiveMode {
|
|
|
852
857
|
if (text === "/compact" || text.startsWith("/compact ")) {
|
|
853
858
|
const customInstructions = text.startsWith("/compact ") ? text.slice(9).trim() : undefined;
|
|
854
859
|
this.editor.setText("");
|
|
855
|
-
this.
|
|
856
|
-
try {
|
|
857
|
-
await this.handleCompactCommand(customInstructions);
|
|
858
|
-
}
|
|
859
|
-
finally {
|
|
860
|
-
this.editor.disableSubmit = false;
|
|
861
|
-
}
|
|
860
|
+
await this.handleCompactCommand(customInstructions);
|
|
862
861
|
return;
|
|
863
862
|
}
|
|
864
863
|
if (text === "/debug") {
|
|
@@ -898,8 +897,16 @@ export class InteractiveMode {
|
|
|
898
897
|
return;
|
|
899
898
|
}
|
|
900
899
|
}
|
|
901
|
-
//
|
|
900
|
+
// Queue input during compaction (extension commands execute immediately)
|
|
902
901
|
if (this.session.isCompacting) {
|
|
902
|
+
if (this.isExtensionCommand(text)) {
|
|
903
|
+
this.editor.addToHistory(text);
|
|
904
|
+
this.editor.setText("");
|
|
905
|
+
await this.session.prompt(text);
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
this.queueCompactionMessage(text, "steer");
|
|
909
|
+
}
|
|
903
910
|
return;
|
|
904
911
|
}
|
|
905
912
|
// If streaming, use prompt() with steer behavior
|
|
@@ -1060,8 +1067,7 @@ export class InteractiveMode {
|
|
|
1060
1067
|
this.ui.requestRender();
|
|
1061
1068
|
break;
|
|
1062
1069
|
case "auto_compaction_start": {
|
|
1063
|
-
//
|
|
1064
|
-
this.editor.disableSubmit = true;
|
|
1070
|
+
// Keep editor active; submissions are queued during compaction.
|
|
1065
1071
|
// Set up escape to abort auto-compaction
|
|
1066
1072
|
this.autoCompactionEscapeHandler = this.editor.onEscape;
|
|
1067
1073
|
this.editor.onEscape = () => {
|
|
@@ -1076,8 +1082,6 @@ export class InteractiveMode {
|
|
|
1076
1082
|
break;
|
|
1077
1083
|
}
|
|
1078
1084
|
case "auto_compaction_end": {
|
|
1079
|
-
// Re-enable submit
|
|
1080
|
-
this.editor.disableSubmit = false;
|
|
1081
1085
|
// Restore escape handler
|
|
1082
1086
|
if (this.autoCompactionEscapeHandler) {
|
|
1083
1087
|
this.editor.onEscape = this.autoCompactionEscapeHandler;
|
|
@@ -1106,6 +1110,7 @@ export class InteractiveMode {
|
|
|
1106
1110
|
});
|
|
1107
1111
|
this.footer.invalidate();
|
|
1108
1112
|
}
|
|
1113
|
+
void this.flushCompactionQueue({ willRetry: event.willRetry });
|
|
1109
1114
|
this.ui.requestRender();
|
|
1110
1115
|
break;
|
|
1111
1116
|
}
|
|
@@ -1356,6 +1361,18 @@ export class InteractiveMode {
|
|
|
1356
1361
|
const text = this.editor.getText().trim();
|
|
1357
1362
|
if (!text)
|
|
1358
1363
|
return;
|
|
1364
|
+
// Queue input during compaction (extension commands execute immediately)
|
|
1365
|
+
if (this.session.isCompacting) {
|
|
1366
|
+
if (this.isExtensionCommand(text)) {
|
|
1367
|
+
this.editor.addToHistory(text);
|
|
1368
|
+
this.editor.setText("");
|
|
1369
|
+
await this.session.prompt(text);
|
|
1370
|
+
}
|
|
1371
|
+
else {
|
|
1372
|
+
this.queueCompactionMessage(text, "followUp");
|
|
1373
|
+
}
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1359
1376
|
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
|
1360
1377
|
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
1361
1378
|
if (this.session.isStreaming) {
|
|
@@ -1501,8 +1518,14 @@ export class InteractiveMode {
|
|
|
1501
1518
|
}
|
|
1502
1519
|
updatePendingMessagesDisplay() {
|
|
1503
1520
|
this.pendingMessagesContainer.clear();
|
|
1504
|
-
const steeringMessages =
|
|
1505
|
-
|
|
1521
|
+
const steeringMessages = [
|
|
1522
|
+
...this.session.getSteeringMessages(),
|
|
1523
|
+
...this.compactionQueuedMessages.filter((msg) => msg.mode === "steer").map((msg) => msg.text),
|
|
1524
|
+
];
|
|
1525
|
+
const followUpMessages = [
|
|
1526
|
+
...this.session.getFollowUpMessages(),
|
|
1527
|
+
...this.compactionQueuedMessages.filter((msg) => msg.mode === "followUp").map((msg) => msg.text),
|
|
1528
|
+
];
|
|
1506
1529
|
if (steeringMessages.length > 0 || followUpMessages.length > 0) {
|
|
1507
1530
|
this.pendingMessagesContainer.addChild(new Spacer(1));
|
|
1508
1531
|
for (const message of steeringMessages) {
|
|
@@ -1515,6 +1538,92 @@ export class InteractiveMode {
|
|
|
1515
1538
|
}
|
|
1516
1539
|
}
|
|
1517
1540
|
}
|
|
1541
|
+
queueCompactionMessage(text, mode) {
|
|
1542
|
+
this.compactionQueuedMessages.push({ text, mode });
|
|
1543
|
+
this.editor.addToHistory(text);
|
|
1544
|
+
this.editor.setText("");
|
|
1545
|
+
this.updatePendingMessagesDisplay();
|
|
1546
|
+
this.showStatus("Queued message for after compaction");
|
|
1547
|
+
}
|
|
1548
|
+
isExtensionCommand(text) {
|
|
1549
|
+
if (!text.startsWith("/"))
|
|
1550
|
+
return false;
|
|
1551
|
+
const extensionRunner = this.session.extensionRunner;
|
|
1552
|
+
if (!extensionRunner)
|
|
1553
|
+
return false;
|
|
1554
|
+
const spaceIndex = text.indexOf(" ");
|
|
1555
|
+
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
1556
|
+
return !!extensionRunner.getCommand(commandName);
|
|
1557
|
+
}
|
|
1558
|
+
async flushCompactionQueue(options) {
|
|
1559
|
+
if (this.compactionQueuedMessages.length === 0) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
const queuedMessages = [...this.compactionQueuedMessages];
|
|
1563
|
+
this.compactionQueuedMessages = [];
|
|
1564
|
+
this.updatePendingMessagesDisplay();
|
|
1565
|
+
const restoreQueue = (error) => {
|
|
1566
|
+
this.session.clearQueue();
|
|
1567
|
+
this.compactionQueuedMessages = queuedMessages;
|
|
1568
|
+
this.updatePendingMessagesDisplay();
|
|
1569
|
+
this.showError(`Failed to send queued message${queuedMessages.length > 1 ? "s" : ""}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1570
|
+
};
|
|
1571
|
+
try {
|
|
1572
|
+
if (options?.willRetry) {
|
|
1573
|
+
// When retry is pending, queue messages for the retry turn
|
|
1574
|
+
for (const message of queuedMessages) {
|
|
1575
|
+
if (this.isExtensionCommand(message.text)) {
|
|
1576
|
+
await this.session.prompt(message.text);
|
|
1577
|
+
}
|
|
1578
|
+
else if (message.mode === "followUp") {
|
|
1579
|
+
await this.session.followUp(message.text);
|
|
1580
|
+
}
|
|
1581
|
+
else {
|
|
1582
|
+
await this.session.steer(message.text);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
this.updatePendingMessagesDisplay();
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
// Find first non-extension-command message to use as prompt
|
|
1589
|
+
const firstPromptIndex = queuedMessages.findIndex((message) => !this.isExtensionCommand(message.text));
|
|
1590
|
+
if (firstPromptIndex === -1) {
|
|
1591
|
+
// All extension commands - execute them all
|
|
1592
|
+
for (const message of queuedMessages) {
|
|
1593
|
+
await this.session.prompt(message.text);
|
|
1594
|
+
}
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
// Execute any extension commands before the first prompt
|
|
1598
|
+
const preCommands = queuedMessages.slice(0, firstPromptIndex);
|
|
1599
|
+
const firstPrompt = queuedMessages[firstPromptIndex];
|
|
1600
|
+
const rest = queuedMessages.slice(firstPromptIndex + 1);
|
|
1601
|
+
for (const message of preCommands) {
|
|
1602
|
+
await this.session.prompt(message.text);
|
|
1603
|
+
}
|
|
1604
|
+
// Send first prompt (starts streaming)
|
|
1605
|
+
const promptPromise = this.session.prompt(firstPrompt.text).catch((error) => {
|
|
1606
|
+
restoreQueue(error);
|
|
1607
|
+
});
|
|
1608
|
+
// Queue remaining messages
|
|
1609
|
+
for (const message of rest) {
|
|
1610
|
+
if (this.isExtensionCommand(message.text)) {
|
|
1611
|
+
await this.session.prompt(message.text);
|
|
1612
|
+
}
|
|
1613
|
+
else if (message.mode === "followUp") {
|
|
1614
|
+
await this.session.followUp(message.text);
|
|
1615
|
+
}
|
|
1616
|
+
else {
|
|
1617
|
+
await this.session.steer(message.text);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
this.updatePendingMessagesDisplay();
|
|
1621
|
+
void promptPromise;
|
|
1622
|
+
}
|
|
1623
|
+
catch (error) {
|
|
1624
|
+
restoreQueue(error);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1518
1627
|
/** Move pending bash components from pending area to chat */
|
|
1519
1628
|
flushPendingBashComponents() {
|
|
1520
1629
|
for (const component of this.pendingBashComponents) {
|
|
@@ -1776,6 +1885,7 @@ export class InteractiveMode {
|
|
|
1776
1885
|
this.statusContainer.clear();
|
|
1777
1886
|
// Clear UI state
|
|
1778
1887
|
this.pendingMessagesContainer.clear();
|
|
1888
|
+
this.compactionQueuedMessages = [];
|
|
1779
1889
|
this.streamingComponent = undefined;
|
|
1780
1890
|
this.streamingMessage = undefined;
|
|
1781
1891
|
this.pendingTools.clear();
|
|
@@ -1799,78 +1909,16 @@ export class InteractiveMode {
|
|
|
1799
1909
|
const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, async (providerId) => {
|
|
1800
1910
|
done();
|
|
1801
1911
|
if (mode === "login") {
|
|
1802
|
-
this.
|
|
1803
|
-
try {
|
|
1804
|
-
await this.session.modelRegistry.authStorage.login(providerId, {
|
|
1805
|
-
onAuth: (info) => {
|
|
1806
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
1807
|
-
// OSC 8 hyperlink for desktop terminals that support clicking
|
|
1808
|
-
const hyperlink = `\x1b]8;;${info.url}\x07Click here to login\x1b]8;;\x07`;
|
|
1809
|
-
this.chatContainer.addChild(new Text(theme.fg("accent", hyperlink), 1, 0));
|
|
1810
|
-
// OSC 52 to copy URL to clipboard (works over SSH, e.g., Termux)
|
|
1811
|
-
const urlBase64 = Buffer.from(info.url).toString("base64");
|
|
1812
|
-
process.stdout.write(`\x1b]52;c;${urlBase64}\x07`);
|
|
1813
|
-
this.chatContainer.addChild(new Text(theme.fg("dim", "(URL copied to clipboard - paste in browser)"), 1, 0));
|
|
1814
|
-
if (info.instructions) {
|
|
1815
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
1816
|
-
this.chatContainer.addChild(new Text(theme.fg("warning", info.instructions), 1, 0));
|
|
1817
|
-
}
|
|
1818
|
-
this.ui.requestRender();
|
|
1819
|
-
// Try to open browser on desktop
|
|
1820
|
-
const openCmd = process.platform === "darwin"
|
|
1821
|
-
? "open"
|
|
1822
|
-
: process.platform === "win32"
|
|
1823
|
-
? "start"
|
|
1824
|
-
: "xdg-open";
|
|
1825
|
-
exec(`${openCmd} "${info.url}"`);
|
|
1826
|
-
},
|
|
1827
|
-
onPrompt: async (prompt) => {
|
|
1828
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
1829
|
-
this.chatContainer.addChild(new Text(theme.fg("warning", prompt.message), 1, 0));
|
|
1830
|
-
if (prompt.placeholder) {
|
|
1831
|
-
this.chatContainer.addChild(new Text(theme.fg("dim", prompt.placeholder), 1, 0));
|
|
1832
|
-
}
|
|
1833
|
-
this.ui.requestRender();
|
|
1834
|
-
return new Promise((resolve) => {
|
|
1835
|
-
const codeInput = new Input();
|
|
1836
|
-
codeInput.onSubmit = () => {
|
|
1837
|
-
const code = codeInput.getValue();
|
|
1838
|
-
this.editorContainer.clear();
|
|
1839
|
-
this.editorContainer.addChild(this.editor);
|
|
1840
|
-
this.ui.setFocus(this.editor);
|
|
1841
|
-
resolve(code);
|
|
1842
|
-
};
|
|
1843
|
-
this.editorContainer.clear();
|
|
1844
|
-
this.editorContainer.addChild(codeInput);
|
|
1845
|
-
this.ui.setFocus(codeInput);
|
|
1846
|
-
this.ui.requestRender();
|
|
1847
|
-
});
|
|
1848
|
-
},
|
|
1849
|
-
onProgress: (message) => {
|
|
1850
|
-
this.chatContainer.addChild(new Text(theme.fg("dim", message), 1, 0));
|
|
1851
|
-
this.ui.requestRender();
|
|
1852
|
-
},
|
|
1853
|
-
});
|
|
1854
|
-
// Refresh models to pick up new baseUrl (e.g., github-copilot)
|
|
1855
|
-
this.session.modelRegistry.refresh();
|
|
1856
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
1857
|
-
this.chatContainer.addChild(new Text(theme.fg("success", `✓ Successfully logged in to ${providerId}`), 1, 0));
|
|
1858
|
-
this.chatContainer.addChild(new Text(theme.fg("dim", `Credentials saved to ${getAuthPath()}`), 1, 0));
|
|
1859
|
-
this.ui.requestRender();
|
|
1860
|
-
}
|
|
1861
|
-
catch (error) {
|
|
1862
|
-
this.showError(`Login failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1863
|
-
}
|
|
1912
|
+
await this.showLoginDialog(providerId);
|
|
1864
1913
|
}
|
|
1865
1914
|
else {
|
|
1915
|
+
// Logout flow
|
|
1916
|
+
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
|
1917
|
+
const providerName = providerInfo?.name || providerId;
|
|
1866
1918
|
try {
|
|
1867
1919
|
this.session.modelRegistry.authStorage.logout(providerId);
|
|
1868
|
-
// Refresh models to reset baseUrl
|
|
1869
1920
|
this.session.modelRegistry.refresh();
|
|
1870
|
-
this.
|
|
1871
|
-
this.chatContainer.addChild(new Text(theme.fg("success", `✓ Successfully logged out of ${providerId}`), 1, 0));
|
|
1872
|
-
this.chatContainer.addChild(new Text(theme.fg("dim", `Credentials removed from ${getAuthPath()}`), 1, 0));
|
|
1873
|
-
this.ui.requestRender();
|
|
1921
|
+
this.showStatus(`Logged out of ${providerName}`);
|
|
1874
1922
|
}
|
|
1875
1923
|
catch (error) {
|
|
1876
1924
|
this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1883,6 +1931,83 @@ export class InteractiveMode {
|
|
|
1883
1931
|
return { component: selector, focus: selector };
|
|
1884
1932
|
});
|
|
1885
1933
|
}
|
|
1934
|
+
async showLoginDialog(providerId) {
|
|
1935
|
+
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
|
1936
|
+
const providerName = providerInfo?.name || providerId;
|
|
1937
|
+
// Providers that use callback servers (can paste redirect URL)
|
|
1938
|
+
const usesCallbackServer = providerId === "openai-codex" || providerId === "google-gemini-cli" || providerId === "google-antigravity";
|
|
1939
|
+
// Create login dialog component
|
|
1940
|
+
const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => {
|
|
1941
|
+
// Completion handled below
|
|
1942
|
+
});
|
|
1943
|
+
// Show dialog in editor container
|
|
1944
|
+
this.editorContainer.clear();
|
|
1945
|
+
this.editorContainer.addChild(dialog);
|
|
1946
|
+
this.ui.setFocus(dialog);
|
|
1947
|
+
this.ui.requestRender();
|
|
1948
|
+
// Promise for manual code input (racing with callback server)
|
|
1949
|
+
let manualCodeResolve;
|
|
1950
|
+
let manualCodeReject;
|
|
1951
|
+
const manualCodePromise = new Promise((resolve, reject) => {
|
|
1952
|
+
manualCodeResolve = resolve;
|
|
1953
|
+
manualCodeReject = reject;
|
|
1954
|
+
});
|
|
1955
|
+
// Restore editor helper
|
|
1956
|
+
const restoreEditor = () => {
|
|
1957
|
+
this.editorContainer.clear();
|
|
1958
|
+
this.editorContainer.addChild(this.editor);
|
|
1959
|
+
this.ui.setFocus(this.editor);
|
|
1960
|
+
this.ui.requestRender();
|
|
1961
|
+
};
|
|
1962
|
+
try {
|
|
1963
|
+
await this.session.modelRegistry.authStorage.login(providerId, {
|
|
1964
|
+
onAuth: (info) => {
|
|
1965
|
+
dialog.showAuth(info.url, info.instructions);
|
|
1966
|
+
if (usesCallbackServer) {
|
|
1967
|
+
// Show input for manual paste, racing with callback
|
|
1968
|
+
dialog
|
|
1969
|
+
.showManualInput("Paste redirect URL below, or complete login in browser:")
|
|
1970
|
+
.then((value) => {
|
|
1971
|
+
if (value && manualCodeResolve) {
|
|
1972
|
+
manualCodeResolve(value);
|
|
1973
|
+
manualCodeResolve = undefined;
|
|
1974
|
+
}
|
|
1975
|
+
})
|
|
1976
|
+
.catch(() => {
|
|
1977
|
+
if (manualCodeReject) {
|
|
1978
|
+
manualCodeReject(new Error("Login cancelled"));
|
|
1979
|
+
manualCodeReject = undefined;
|
|
1980
|
+
}
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
else if (providerId === "github-copilot") {
|
|
1984
|
+
// GitHub Copilot polls after onAuth
|
|
1985
|
+
dialog.showWaiting("Waiting for browser authentication...");
|
|
1986
|
+
}
|
|
1987
|
+
// For Anthropic: onPrompt is called immediately after
|
|
1988
|
+
},
|
|
1989
|
+
onPrompt: async (prompt) => {
|
|
1990
|
+
return dialog.showPrompt(prompt.message, prompt.placeholder);
|
|
1991
|
+
},
|
|
1992
|
+
onProgress: (message) => {
|
|
1993
|
+
dialog.showProgress(message);
|
|
1994
|
+
},
|
|
1995
|
+
onManualCodeInput: () => manualCodePromise,
|
|
1996
|
+
signal: dialog.signal,
|
|
1997
|
+
});
|
|
1998
|
+
// Success
|
|
1999
|
+
restoreEditor();
|
|
2000
|
+
this.session.modelRegistry.refresh();
|
|
2001
|
+
this.showStatus(`Logged in to ${providerName}. Credentials saved to ${getAuthPath()}`);
|
|
2002
|
+
}
|
|
2003
|
+
catch (error) {
|
|
2004
|
+
restoreEditor();
|
|
2005
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2006
|
+
if (errorMsg !== "Login cancelled") {
|
|
2007
|
+
this.showError(`Failed to login to ${providerName}: ${errorMsg}`);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1886
2011
|
// =========================================================================
|
|
1887
2012
|
// Command handlers
|
|
1888
2013
|
// =========================================================================
|
|
@@ -2107,7 +2232,7 @@ export class InteractiveMode {
|
|
|
2107
2232
|
| Key | Action |
|
|
2108
2233
|
|-----|--------|
|
|
2109
2234
|
| \`${submit}\` | Send message |
|
|
2110
|
-
| \`${newLine}\` | New line |
|
|
2235
|
+
| \`${newLine}\` | New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""} |
|
|
2111
2236
|
| \`${deleteWordBackward}\` | Delete word backwards |
|
|
2112
2237
|
| \`${deleteToLineStart}\` | Delete to start of line |
|
|
2113
2238
|
| \`${deleteToLineEnd}\` | Delete to end of line |
|
|
@@ -2166,6 +2291,7 @@ export class InteractiveMode {
|
|
|
2166
2291
|
// Clear UI state
|
|
2167
2292
|
this.chatContainer.clear();
|
|
2168
2293
|
this.pendingMessagesContainer.clear();
|
|
2294
|
+
this.compactionQueuedMessages = [];
|
|
2169
2295
|
this.streamingComponent = undefined;
|
|
2170
2296
|
this.streamingMessage = undefined;
|
|
2171
2297
|
this.pendingTools.clear();
|
|
@@ -2287,6 +2413,7 @@ export class InteractiveMode {
|
|
|
2287
2413
|
this.statusContainer.clear();
|
|
2288
2414
|
this.editor.onEscape = originalOnEscape;
|
|
2289
2415
|
}
|
|
2416
|
+
void this.flushCompactionQueue({ willRetry: false });
|
|
2290
2417
|
}
|
|
2291
2418
|
stop() {
|
|
2292
2419
|
if (this.loadingAnimation) {
|