@thegitai/cli 1.0.0-preview.1 → 1.0.0-preview.11
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/README.md +16 -4
- package/dist/bin/ai.js +57 -287
- package/dist/src/api/auth.js +2 -2
- package/dist/src/api/browser-login.js +72 -3
- package/dist/src/api/chat.js +147 -30
- package/dist/src/api/http.js +16 -3
- package/dist/src/api/models.js +9 -4
- package/dist/src/help-text.js +19 -7
- package/dist/src/patcher.js +96 -9
- package/dist/src/project-index.js +13 -1
- package/dist/src/project-orientation.js +99 -0
- package/dist/src/scratch-dir.js +51 -33
- package/dist/src/session-store.js +52 -20
- package/dist/src/session.js +8 -0
- package/dist/src/tool-executor.js +38 -6
- package/dist/src/tools/delete-file.js +22 -4
- package/dist/src/tools/patch-file.js +30 -5
- package/dist/src/tools/read-file.js +3 -1
- package/dist/src/tools/replace-document-text.js +7 -1
- package/dist/src/tools/run-command.js +37 -19
- package/dist/src/tools/run-node-script.js +24 -4
- package/dist/src/tools/str-replace.js +30 -5
- package/dist/src/tools/write-file.js +25 -5
- package/dist/src/turn-failure-marker.js +11 -0
- package/dist/src/ui/prompt-history-store.js +1 -1
- package/dist/src/ui/repl.js +197 -51
- package/dist/src/ui/tui/bridge.js +3 -0
- package/dist/src/ui/tui/build-frame.js +179 -82
- package/dist/src/ui/tui/markdown-render.js +72 -73
- package/dist/src/ui/tui/shell-input.js +42 -13
- package/dist/src/ui/tui/terminal-title.js +3 -0
- package/dist/src/ui/tui/terminal-writes.js +48 -0
- package/dist/src/ui/tui/text.js +158 -4
- package/dist/src/utils.js +9 -0
- package/package.json +18 -6
- package/dist/src/markdown-renderer.js +0 -112
package/dist/src/ui/repl.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { createRatatuiBridge } from './tui/bridge.js';
|
|
2
2
|
import { buildTuiFrame, formatJobElapsed, formatTodoProgress, renderTranscriptEntryLines, } from './tui/build-frame.js';
|
|
3
3
|
import { createTerminalTitleController } from './tui/terminal-title.js';
|
|
4
|
+
import { captureTerminalWrites, releaseTerminalWrites, } from './tui/terminal-writes.js';
|
|
4
5
|
export { getSlashCommandSuggestions } from './tui/build-frame.js';
|
|
5
6
|
import { agentModeLabel, nextAgentMode, } from '../agent-mode.js';
|
|
6
7
|
import { chat, models } from '../api/index.js';
|
|
7
8
|
import { isTurnCancelledError } from '../api/chat.js';
|
|
8
9
|
import { getJobBufferedOutput, getJobOutputPreview, hasRunningBackgroundJobs, killAllBackgroundJobs, killBackgroundJob, listBackgroundJobs, setBackgroundJobSession, setBackgroundJobUpdateHook, } from '../background-jobs.js';
|
|
9
10
|
import { clearTodos, listTodos, setTodoSession } from '../todo-list.js';
|
|
11
|
+
import { setScratchSession } from '../scratch-dir.js';
|
|
10
12
|
import { cancelActiveCommand } from '../executor.js';
|
|
13
|
+
import { isTurnFailureMarker } from '../turn-failure-marker.js';
|
|
14
|
+
import { clearCliAuthConfig } from '../api/auth.js';
|
|
15
|
+
import { authenticationErrorMessage, isAuthenticationError, } from '../api/http.js';
|
|
11
16
|
import { setCommandOutputHook, withTuiMode } from '../runtime-mode.js';
|
|
12
17
|
import { collectBackgroundJobUiKillMutations, collectBackgroundJobUiOutputMutations, } from '../tool-executor.js';
|
|
13
|
-
import {
|
|
14
|
-
import { applySessionSnapshot, listSessionMetadata, loadSessionSnapshot, saveSessionState, } from '../session-store.js';
|
|
18
|
+
import { startNewConversation, } from '../session.js';
|
|
19
|
+
import { applySessionSnapshot, listSessionMetadata, loadSessionSnapshot, saveSessionState, sessionHasUserMessage, } from '../session-store.js';
|
|
15
20
|
import { truncate } from '../utils.js';
|
|
16
21
|
import { writeClipboardText } from '../core/clipboard.js';
|
|
17
22
|
import { openUrl } from '../core/open-url.js';
|
|
@@ -111,8 +116,8 @@ export const SLASH_COMMANDS = [
|
|
|
111
116
|
description: 'Background jobs: pick to view output or kill',
|
|
112
117
|
},
|
|
113
118
|
{
|
|
114
|
-
command: '/
|
|
115
|
-
description: '
|
|
119
|
+
command: '/new',
|
|
120
|
+
description: 'start a new conversation; this session remains saved',
|
|
116
121
|
},
|
|
117
122
|
{
|
|
118
123
|
command: '/exit',
|
|
@@ -558,6 +563,31 @@ function buildFileChangeEntry(event) {
|
|
|
558
563
|
title: skipped ? `${verb} skipped: ${filePath}` : `${verb} failed: ${filePath}`,
|
|
559
564
|
};
|
|
560
565
|
}
|
|
566
|
+
if (result?.scratch === true) {
|
|
567
|
+
const scratchName = truncate(filePath.split(/[\\/]/).filter(Boolean).at(-1) ?? filePath, 72);
|
|
568
|
+
if (call.name === 'delete_file') {
|
|
569
|
+
return {
|
|
570
|
+
body: '',
|
|
571
|
+
filePath,
|
|
572
|
+
kind: 'tool',
|
|
573
|
+
title: result?.deleted === true
|
|
574
|
+
? `Removed scratch file: ${scratchName}`
|
|
575
|
+
: `Scratch delete skipped: ${scratchName}`,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
const content = typeof call.args?.content === 'string' ? call.args.content : '';
|
|
579
|
+
const lineSummary = call.name === 'write_file' && content
|
|
580
|
+
? ` (${splitDiffLines(content).length} lines)`
|
|
581
|
+
: '';
|
|
582
|
+
return {
|
|
583
|
+
body: '',
|
|
584
|
+
filePath,
|
|
585
|
+
kind: 'tool',
|
|
586
|
+
title: call.name === 'write_file'
|
|
587
|
+
? `Wrote scratch file: ${scratchName}${lineSummary}`
|
|
588
|
+
: `Edited scratch file: ${scratchName}`,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
561
591
|
if (call.name === 'undo_edit') {
|
|
562
592
|
const dryRun = result?.dryRun === true ||
|
|
563
593
|
result?.dry_run === true ||
|
|
@@ -754,7 +784,7 @@ function displayUserTextFromHistoryEntry(entry) {
|
|
|
754
784
|
.slice(contentStart, contentEnd === -1 ? text.length : contentEnd)
|
|
755
785
|
.trim();
|
|
756
786
|
}
|
|
757
|
-
function buildTranscriptFromSessionHistory(history) {
|
|
787
|
+
export function buildTranscriptFromSessionHistory(history) {
|
|
758
788
|
const entries = [];
|
|
759
789
|
const pendingCalls = new Map();
|
|
760
790
|
for (const entry of history) {
|
|
@@ -800,6 +830,14 @@ function buildTranscriptFromSessionHistory(history) {
|
|
|
800
830
|
}
|
|
801
831
|
const text = textFromHistoryEntry(entry);
|
|
802
832
|
if ((entry.role === 'model' || entry.role === 'assistant') && text) {
|
|
833
|
+
if (isTurnFailureMarker(text)) {
|
|
834
|
+
entries.push({
|
|
835
|
+
body: 'This request did not complete.',
|
|
836
|
+
kind: 'system',
|
|
837
|
+
title: 'Previous turn',
|
|
838
|
+
});
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
803
841
|
entries.push({ body: text, kind: 'assistant', title: 'Response' });
|
|
804
842
|
}
|
|
805
843
|
}
|
|
@@ -925,6 +963,7 @@ function createInitialShellState(session, serverModels, debugUi) {
|
|
|
925
963
|
activeTurnInput: '',
|
|
926
964
|
activeTurnInputPreformatted: false,
|
|
927
965
|
agentMode: session.agentMode,
|
|
966
|
+
analyzingImages: 0,
|
|
928
967
|
approvalCursor: getDefaultApprovalCursor(),
|
|
929
968
|
approvalPrompt: null,
|
|
930
969
|
autoYes: session.autoYes,
|
|
@@ -1034,6 +1073,13 @@ function splitThinkingLines(text) {
|
|
|
1034
1073
|
.map((part) => part.trim())
|
|
1035
1074
|
.filter(Boolean));
|
|
1036
1075
|
}
|
|
1076
|
+
const LIVE_STATUS_PANEL_MAX_CHARS = 72;
|
|
1077
|
+
function liveStatusPanelTitle(status) {
|
|
1078
|
+
const text = String(status ?? '').trim();
|
|
1079
|
+
if (!text || text.includes('\n'))
|
|
1080
|
+
return '';
|
|
1081
|
+
return text.length <= LIVE_STATUS_PANEL_MAX_CHARS ? text : '';
|
|
1082
|
+
}
|
|
1037
1083
|
function thinkingPanelFromStatus(status) {
|
|
1038
1084
|
const rawText = thinkingNoteFromStatus(status);
|
|
1039
1085
|
if (!rawText)
|
|
@@ -1212,7 +1258,11 @@ export function buildModelPickerOptions(currentModelId, serverModels) {
|
|
|
1212
1258
|
return serverModels.map((model) => ({
|
|
1213
1259
|
id: model.id,
|
|
1214
1260
|
label: model.label,
|
|
1215
|
-
|
|
1261
|
+
publicId: model.id,
|
|
1262
|
+
costRating: model.costRating,
|
|
1263
|
+
current: model.id === currentModelId,
|
|
1264
|
+
disabled: false,
|
|
1265
|
+
note: model.description,
|
|
1216
1266
|
}));
|
|
1217
1267
|
}
|
|
1218
1268
|
function getDefaultModelPickerIndex(currentModelId, serverModels) {
|
|
@@ -1240,23 +1290,22 @@ function appendCommandLog(state, text) {
|
|
|
1240
1290
|
};
|
|
1241
1291
|
}
|
|
1242
1292
|
async function saveSessionBoth({ serverSessionClient, session, }) {
|
|
1293
|
+
if (!sessionHasUserMessage(session))
|
|
1294
|
+
return;
|
|
1243
1295
|
saveSessionState(session);
|
|
1244
1296
|
await serverSessionClient.save(session);
|
|
1245
1297
|
}
|
|
1246
|
-
function shouldUseRatatuiShell() {
|
|
1247
|
-
if (process.env.THEGITAI_PLAIN === '1')
|
|
1248
|
-
return false;
|
|
1249
|
-
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
1250
|
-
}
|
|
1251
|
-
export function shouldUseClientRatatuiShell() {
|
|
1252
|
-
return shouldUseRatatuiShell();
|
|
1253
|
-
}
|
|
1254
1298
|
export async function runClientInteractive({ appendPromptHistory, authConfig, debugUi, projectIndex, serverModels, serverSessionClient, session, usageText, initialPrompt, }) {
|
|
1255
|
-
if (
|
|
1256
|
-
throw new Error('
|
|
1299
|
+
if (process.stdin.isTTY !== true) {
|
|
1300
|
+
throw new Error('stdin is not a terminal');
|
|
1301
|
+
}
|
|
1302
|
+
if (process.stdout.isTTY !== true) {
|
|
1303
|
+
throw new Error('stdout is not a terminal');
|
|
1257
1304
|
}
|
|
1305
|
+
let fatalError = null;
|
|
1258
1306
|
await withTuiMode(async () => {
|
|
1259
1307
|
setBackgroundJobSession(session.sessionId);
|
|
1308
|
+
setScratchSession(session.sessionId);
|
|
1260
1309
|
setTodoSession(session.sessionId);
|
|
1261
1310
|
const store = createShellStore(createInitialShellState(session, serverModels, debugUi));
|
|
1262
1311
|
store.replaceTranscript(createSessionTranscript(session));
|
|
@@ -1268,6 +1317,7 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1268
1317
|
let cleanupSudoPasswordPrompt = null;
|
|
1269
1318
|
let sudoPasswordBuffer = '';
|
|
1270
1319
|
const bridge = createRatatuiBridge();
|
|
1320
|
+
captureTerminalWrites();
|
|
1271
1321
|
const { handleShellKeyEvent } = await import('./tui/shell-input.js');
|
|
1272
1322
|
let terminalCols = 80;
|
|
1273
1323
|
let terminalRows = 24;
|
|
@@ -1280,6 +1330,7 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1280
1330
|
let latestUsageSummary = null;
|
|
1281
1331
|
let pendingTurnEntries = [];
|
|
1282
1332
|
let activeTurnAbort = null;
|
|
1333
|
+
let newConversationInFlight = false;
|
|
1283
1334
|
let todosTouchedThisTurn = false;
|
|
1284
1335
|
const syncTodosState = () => {
|
|
1285
1336
|
store.update((current) => ({ ...current, todos: listTodos() }));
|
|
@@ -1312,7 +1363,9 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1312
1363
|
store.update((current) => current.status === status ? { ...current, status: 'Ready' } : current);
|
|
1313
1364
|
}, 2500);
|
|
1314
1365
|
};
|
|
1315
|
-
const terminalTitle = createTerminalTitleController(
|
|
1366
|
+
const terminalTitle = createTerminalTitleController({
|
|
1367
|
+
write: (title) => bridge.setTitle(title),
|
|
1368
|
+
});
|
|
1316
1369
|
const syncTerminalTitle = () => {
|
|
1317
1370
|
const state = store.getState();
|
|
1318
1371
|
terminalTitle.sync({
|
|
@@ -1479,7 +1532,7 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1479
1532
|
}));
|
|
1480
1533
|
};
|
|
1481
1534
|
const cancelActiveTurn = () => {
|
|
1482
|
-
if (!store.getState().busy)
|
|
1535
|
+
if (!store.getState().busy || newConversationInFlight)
|
|
1483
1536
|
return;
|
|
1484
1537
|
disarmExitConfirm();
|
|
1485
1538
|
dismissPendingApproval();
|
|
@@ -1523,14 +1576,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1523
1576
|
scheduleLiveFrameRemount();
|
|
1524
1577
|
void remountTui();
|
|
1525
1578
|
};
|
|
1526
|
-
const saveActiveSession = async () => {
|
|
1527
|
-
try {
|
|
1528
|
-
await saveSessionBoth({ serverSessionClient, session });
|
|
1529
|
-
}
|
|
1530
|
-
catch (error) {
|
|
1531
|
-
appendError(`Session save failed: ${error.message}`);
|
|
1532
|
-
}
|
|
1533
|
-
};
|
|
1534
1579
|
const syncShellStateFromSession = () => {
|
|
1535
1580
|
store.update((current) => ({
|
|
1536
1581
|
...current,
|
|
@@ -1618,9 +1663,45 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1618
1663
|
status: 'Exiting...',
|
|
1619
1664
|
}));
|
|
1620
1665
|
void bridge.close().then(() => {
|
|
1666
|
+
releaseTerminalWrites();
|
|
1621
1667
|
resolveDone?.();
|
|
1622
1668
|
});
|
|
1623
1669
|
};
|
|
1670
|
+
const exitForAuthenticationError = (error) => {
|
|
1671
|
+
if (!isAuthenticationError(error))
|
|
1672
|
+
return false;
|
|
1673
|
+
clearCliAuthConfig(session.env);
|
|
1674
|
+
if (sessionHasUserMessage(session)) {
|
|
1675
|
+
saveSessionState(session);
|
|
1676
|
+
}
|
|
1677
|
+
fatalError = new Error(authenticationErrorMessage(error));
|
|
1678
|
+
requestExit();
|
|
1679
|
+
return true;
|
|
1680
|
+
};
|
|
1681
|
+
const saveActiveSession = async () => {
|
|
1682
|
+
try {
|
|
1683
|
+
await saveSessionBoth({ serverSessionClient, session });
|
|
1684
|
+
return true;
|
|
1685
|
+
}
|
|
1686
|
+
catch (error) {
|
|
1687
|
+
if (exitForAuthenticationError(error))
|
|
1688
|
+
return false;
|
|
1689
|
+
appendError(`Session save failed: ${error.message}`);
|
|
1690
|
+
return true;
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
const saveActiveSessionOrAbort = async () => {
|
|
1694
|
+
try {
|
|
1695
|
+
await saveSessionBoth({ serverSessionClient, session });
|
|
1696
|
+
return true;
|
|
1697
|
+
}
|
|
1698
|
+
catch (error) {
|
|
1699
|
+
if (exitForAuthenticationError(error))
|
|
1700
|
+
return false;
|
|
1701
|
+
appendError(`Session save failed: ${error.message}`);
|
|
1702
|
+
return false;
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1624
1705
|
const openSudoPasswordPrompt = (command, prompt, signal) => new Promise((resolve) => {
|
|
1625
1706
|
if (exiting || signal?.aborted) {
|
|
1626
1707
|
resolve(null);
|
|
@@ -1757,7 +1838,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1757
1838
|
selectedModelId: selected,
|
|
1758
1839
|
serverModels: fresh,
|
|
1759
1840
|
});
|
|
1760
|
-
await saveActiveSession()
|
|
1841
|
+
if (!(await saveActiveSession()))
|
|
1842
|
+
return;
|
|
1761
1843
|
syncShellStateFromSession();
|
|
1762
1844
|
appendStaticEntry({
|
|
1763
1845
|
body: `Switched to ${formatModelLabel(selected, fresh.models)}. Conversation history preserved.`,
|
|
@@ -1876,6 +1958,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1876
1958
|
await switchToModel(selected.id);
|
|
1877
1959
|
}
|
|
1878
1960
|
catch (error) {
|
|
1961
|
+
if (exitForAuthenticationError(error))
|
|
1962
|
+
return;
|
|
1879
1963
|
appendError(error.message);
|
|
1880
1964
|
}
|
|
1881
1965
|
};
|
|
@@ -1933,10 +2017,10 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1933
2017
|
const snapshot = await loadInteractiveSession(selected.id);
|
|
1934
2018
|
applySessionSnapshot(session, snapshot);
|
|
1935
2019
|
setBackgroundJobSession(session.sessionId);
|
|
2020
|
+
setScratchSession(session.sessionId);
|
|
1936
2021
|
syncBackgroundJobsState();
|
|
1937
2022
|
setTodoSession(session.sessionId);
|
|
1938
2023
|
syncTodosState();
|
|
1939
|
-
await saveActiveSession();
|
|
1940
2024
|
syncShellStateFromSession();
|
|
1941
2025
|
store.update((next) => ({
|
|
1942
2026
|
...next,
|
|
@@ -1959,6 +2043,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
1959
2043
|
await remountTui();
|
|
1960
2044
|
}
|
|
1961
2045
|
catch (error) {
|
|
2046
|
+
if (exitForAuthenticationError(error))
|
|
2047
|
+
return;
|
|
1962
2048
|
store.update((next) => ({
|
|
1963
2049
|
...next,
|
|
1964
2050
|
busy: false,
|
|
@@ -2111,14 +2197,18 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2111
2197
|
});
|
|
2112
2198
|
}
|
|
2113
2199
|
catch (error) {
|
|
2200
|
+
if (exitForAuthenticationError(error))
|
|
2201
|
+
return;
|
|
2114
2202
|
appendError(error.message);
|
|
2115
2203
|
}
|
|
2116
2204
|
finally {
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2205
|
+
if (!exiting) {
|
|
2206
|
+
store.update((current) => ({
|
|
2207
|
+
...current,
|
|
2208
|
+
busy: false,
|
|
2209
|
+
status: 'Ready',
|
|
2210
|
+
}));
|
|
2211
|
+
}
|
|
2122
2212
|
}
|
|
2123
2213
|
return;
|
|
2124
2214
|
}
|
|
@@ -2132,14 +2222,18 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2132
2222
|
await openResumePicker();
|
|
2133
2223
|
}
|
|
2134
2224
|
catch (error) {
|
|
2225
|
+
if (exitForAuthenticationError(error))
|
|
2226
|
+
return;
|
|
2135
2227
|
appendError(error.message);
|
|
2136
2228
|
}
|
|
2137
2229
|
finally {
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2230
|
+
if (!exiting) {
|
|
2231
|
+
store.update((current) => ({
|
|
2232
|
+
...current,
|
|
2233
|
+
busy: false,
|
|
2234
|
+
status: current.resumePickerOpen ? 'Select a session to resume' : 'Ready',
|
|
2235
|
+
}));
|
|
2236
|
+
}
|
|
2143
2237
|
}
|
|
2144
2238
|
return;
|
|
2145
2239
|
}
|
|
@@ -2159,33 +2253,68 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2159
2253
|
}
|
|
2160
2254
|
}
|
|
2161
2255
|
catch (error) {
|
|
2256
|
+
if (exitForAuthenticationError(error))
|
|
2257
|
+
return;
|
|
2162
2258
|
appendError(error.message);
|
|
2163
2259
|
}
|
|
2164
2260
|
finally {
|
|
2261
|
+
if (!exiting) {
|
|
2262
|
+
store.update((current) => ({
|
|
2263
|
+
...current,
|
|
2264
|
+
busy: false,
|
|
2265
|
+
status: current.modelPickerOpen ? 'Select a model' : 'Ready',
|
|
2266
|
+
}));
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
if (input === '/new') {
|
|
2272
|
+
newConversationInFlight = true;
|
|
2273
|
+
store.update((current) => ({
|
|
2274
|
+
...current,
|
|
2275
|
+
busy: true,
|
|
2276
|
+
busySince: Date.now(),
|
|
2277
|
+
imageAttachments: [],
|
|
2278
|
+
queuedMessage: null,
|
|
2279
|
+
status: 'Starting a new conversation...',
|
|
2280
|
+
}));
|
|
2281
|
+
if (!(await saveActiveSessionOrAbort())) {
|
|
2282
|
+
newConversationInFlight = false;
|
|
2165
2283
|
store.update((current) => ({
|
|
2166
2284
|
...current,
|
|
2167
2285
|
busy: false,
|
|
2168
|
-
|
|
2286
|
+
busySince: null,
|
|
2287
|
+
status: 'Ready',
|
|
2169
2288
|
}));
|
|
2289
|
+
await flushQueuedMessage();
|
|
2290
|
+
return;
|
|
2170
2291
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2292
|
+
startNewConversation(session);
|
|
2293
|
+
setBackgroundJobSession(session.sessionId);
|
|
2294
|
+
setScratchSession(session.sessionId);
|
|
2295
|
+
setTodoSession(session.sessionId);
|
|
2175
2296
|
clearTodos();
|
|
2297
|
+
syncBackgroundJobsState();
|
|
2176
2298
|
syncTodosState();
|
|
2177
2299
|
latestUsageSummary = null;
|
|
2178
|
-
await saveActiveSession();
|
|
2179
2300
|
store.replaceTranscript([
|
|
2180
2301
|
{
|
|
2181
|
-
body: '
|
|
2302
|
+
body: 'Started a new conversation. The previous session remains saved.',
|
|
2182
2303
|
kind: 'system',
|
|
2183
|
-
title: '
|
|
2304
|
+
title: 'Session',
|
|
2184
2305
|
},
|
|
2185
2306
|
]);
|
|
2307
|
+
await saveActiveSession();
|
|
2186
2308
|
syncShellStateFromSession();
|
|
2187
|
-
|
|
2309
|
+
newConversationInFlight = false;
|
|
2310
|
+
store.update((current) => ({
|
|
2311
|
+
...current,
|
|
2312
|
+
busy: false,
|
|
2313
|
+
busySince: null,
|
|
2314
|
+
status: 'Ready',
|
|
2315
|
+
}));
|
|
2188
2316
|
await remountTui();
|
|
2317
|
+
await flushQueuedMessage();
|
|
2189
2318
|
return;
|
|
2190
2319
|
}
|
|
2191
2320
|
latestUsageSummary = null;
|
|
@@ -2196,6 +2325,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2196
2325
|
activeTurnAbort = turnAbort;
|
|
2197
2326
|
lastTurnStartedAt = turnStartedAt;
|
|
2198
2327
|
todosTouchedThisTurn = false;
|
|
2328
|
+
clearTodos();
|
|
2329
|
+
syncTodosState();
|
|
2199
2330
|
const userEntry = {
|
|
2200
2331
|
body: input,
|
|
2201
2332
|
kind: 'user',
|
|
@@ -2208,6 +2339,7 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2208
2339
|
...current,
|
|
2209
2340
|
activeTurnInput: input,
|
|
2210
2341
|
activeTurnInputPreformatted: preformatted,
|
|
2342
|
+
analyzingImages: 0,
|
|
2211
2343
|
busy: true,
|
|
2212
2344
|
busySince: turnStartedAt,
|
|
2213
2345
|
clockNow: turnStartedAt,
|
|
@@ -2232,7 +2364,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2232
2364
|
return;
|
|
2233
2365
|
latestUsageSummary = result.usageSummary ?? null;
|
|
2234
2366
|
const turnEntries = takePendingTurnEntries();
|
|
2235
|
-
await saveActiveSession()
|
|
2367
|
+
if (!(await saveActiveSession()))
|
|
2368
|
+
return;
|
|
2236
2369
|
syncShellStateFromSession();
|
|
2237
2370
|
store.update((current) => ({
|
|
2238
2371
|
...current,
|
|
@@ -2283,6 +2416,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2283
2416
|
return;
|
|
2284
2417
|
}
|
|
2285
2418
|
const cancelled = isTurnCancelledError(error);
|
|
2419
|
+
if (exitForAuthenticationError(error))
|
|
2420
|
+
return;
|
|
2286
2421
|
store.update((current) => ({
|
|
2287
2422
|
...current,
|
|
2288
2423
|
activeTurnInput: '',
|
|
@@ -2307,7 +2442,8 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2307
2442
|
title: 'System',
|
|
2308
2443
|
},
|
|
2309
2444
|
]);
|
|
2310
|
-
await saveActiveSession()
|
|
2445
|
+
if (!(await saveActiveSession()))
|
|
2446
|
+
return;
|
|
2311
2447
|
}
|
|
2312
2448
|
else {
|
|
2313
2449
|
appendStaticEntries(turnEntries);
|
|
@@ -2327,13 +2463,19 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2327
2463
|
}
|
|
2328
2464
|
}
|
|
2329
2465
|
};
|
|
2466
|
+
session.onImageAnalysis = (activeImageCount) => {
|
|
2467
|
+
store.update((current) => ({
|
|
2468
|
+
...current,
|
|
2469
|
+
analyzingImages: Math.max(0, activeImageCount),
|
|
2470
|
+
}));
|
|
2471
|
+
};
|
|
2330
2472
|
session.onStatus = (message) => {
|
|
2331
2473
|
const panel = thinkingPanelFromStatus(message);
|
|
2332
2474
|
store.update((current) => ({
|
|
2333
2475
|
...current,
|
|
2334
2476
|
status: message,
|
|
2335
|
-
thinkingTitle: panel
|
|
2336
|
-
thinkingNotes: panel
|
|
2477
|
+
thinkingTitle: panel ? panel.title : liveStatusPanelTitle(message),
|
|
2478
|
+
thinkingNotes: panel ? panel.notes : [],
|
|
2337
2479
|
tokenUsage: formatClientTokenUsage(current.busySince == null ? null : Date.now() - current.busySince, latestUsageSummary),
|
|
2338
2480
|
}));
|
|
2339
2481
|
};
|
|
@@ -2531,10 +2673,14 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
|
|
|
2531
2673
|
terminalTitle.dispose();
|
|
2532
2674
|
killAllBackgroundJobs();
|
|
2533
2675
|
setBackgroundJobSession(null);
|
|
2676
|
+
setScratchSession(null);
|
|
2534
2677
|
setTodoSession(null);
|
|
2535
2678
|
setBackgroundJobUpdateHook(null);
|
|
2536
2679
|
await bridge.close();
|
|
2680
|
+
releaseTerminalWrites();
|
|
2537
2681
|
setCommandOutputHook(null);
|
|
2538
2682
|
}
|
|
2539
2683
|
});
|
|
2684
|
+
if (fatalError)
|
|
2685
|
+
throw fatalError;
|
|
2540
2686
|
}
|