@bubblebrain-ai/bubble 0.0.13 → 0.0.14
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/dist/agent/execution-governor.js +1 -1
- package/dist/agent/tool-intent.js +1 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +589 -316
- package/dist/approval/controller.d.ts +1 -0
- package/dist/approval/controller.js +20 -3
- package/dist/approval/tool-helper.js +2 -0
- package/dist/approval/types.d.ts +14 -1
- package/dist/context/compact.js +9 -3
- package/dist/context/projector.js +27 -12
- package/dist/debug-trace.d.ts +27 -0
- package/dist/debug-trace.js +385 -0
- package/dist/feishu/agent-host/approval-card.js +9 -0
- package/dist/feishu/serve.js +7 -1
- package/dist/main.js +28 -0
- package/dist/model-catalog.js +1 -0
- package/dist/orchestrator/default-hooks.js +19 -8
- package/dist/orchestrator/hooks.d.ts +1 -0
- package/dist/prompt/environment.js +2 -0
- package/dist/prompt/reminders.d.ts +5 -6
- package/dist/prompt/reminders.js +8 -9
- package/dist/prompt/runtime.js +2 -2
- package/dist/provider-openai-codex.d.ts +7 -0
- package/dist/provider-openai-codex.js +265 -124
- package/dist/provider-registry.d.ts +2 -0
- package/dist/provider-registry.js +58 -9
- package/dist/provider.d.ts +3 -0
- package/dist/provider.js +5 -1
- package/dist/session-log.js +13 -1
- package/dist/slash-commands/commands.js +12 -0
- package/dist/slash-commands/types.d.ts +2 -0
- package/dist/stats/usage.d.ts +52 -0
- package/dist/stats/usage.js +414 -0
- package/dist/tools/apply-patch.d.ts +9 -0
- package/dist/tools/apply-patch.js +330 -0
- package/dist/tools/bash.js +205 -44
- package/dist/tools/edit-apply.d.ts +5 -2
- package/dist/tools/edit-apply.js +221 -31
- package/dist/tools/edit.js +12 -3
- package/dist/tools/file-mutation-queue.d.ts +1 -0
- package/dist/tools/file-mutation-queue.js +12 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +7 -1
- package/dist/tools/patch-apply.d.ts +41 -0
- package/dist/tools/patch-apply.js +312 -0
- package/dist/tools/server-manager.d.ts +36 -0
- package/dist/tools/server-manager.js +234 -0
- package/dist/tools/server.d.ts +6 -0
- package/dist/tools/server.js +245 -0
- package/dist/tools/write.d.ts +3 -6
- package/dist/tools/write.js +26 -46
- package/dist/tui/display-history.d.ts +1 -0
- package/dist/tui/display-history.js +5 -4
- package/dist/tui/edit-diff.js +6 -1
- package/dist/tui/model-picker-data.d.ts +10 -0
- package/dist/tui/model-picker-data.js +32 -0
- package/dist/tui/run.js +632 -89
- package/dist/tui/tool-renderers/fallback.js +1 -1
- package/dist/tui/tool-renderers/write-preview.js +2 -0
- package/dist/tui/trace-groups.js +10 -3
- package/dist/tui-ink/app.js +1 -4
- package/dist/tui-ink/approval/approval-dialog.js +7 -1
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +5 -4
- package/dist/tui-ink/message-list.js +14 -8
- package/dist/tui-ink/trace-groups.js +1 -1
- package/dist/tui-opentui/app.js +2 -0
- package/dist/tui-opentui/approval/approval-dialog.js +7 -1
- package/dist/tui-opentui/display-history.d.ts +1 -0
- package/dist/tui-opentui/display-history.js +5 -4
- package/dist/tui-opentui/edit-diff.js +6 -1
- package/dist/tui-opentui/message-list.js +6 -3
- package/dist/tui-opentui/trace-groups.js +10 -3
- package/dist/types.d.ts +12 -2
- package/package.json +1 -1
package/dist/tui/run.js
CHANGED
|
@@ -9,10 +9,11 @@ import { homedir } from "node:os";
|
|
|
9
9
|
import { AgentAbortError } from "../agent.js";
|
|
10
10
|
import { AgentRunInputQueue } from "../agent/input-controller.js";
|
|
11
11
|
import { debugReasoningStream, summarizeDebugText } from "../reasoning-debug.js";
|
|
12
|
+
import { summarizeAgentEventForTrace, summarizeTraceError, summarizeTraceValue, traceEvent, } from "../debug-trace.js";
|
|
12
13
|
import { BUILTIN_PROVIDERS, decodeModel, displayModel, isUserVisibleProvider } from "../provider-registry.js";
|
|
13
|
-
import { listBuiltinModels } from "../model-catalog.js";
|
|
14
14
|
import { calculateUsageCost } from "../model-pricing.js";
|
|
15
15
|
import { getAvailableThinkingLevels } from "../provider-transform.js";
|
|
16
|
+
import { collectUsageStatsBundle, formatStatsPanelBody } from "../stats/usage.js";
|
|
16
17
|
import { parseSkillInvocation } from "../skills/invocation.js";
|
|
17
18
|
import { registry as slashRegistry } from "../slash-commands/index.js";
|
|
18
19
|
import { sourceRank } from "../slash-commands/unified.js";
|
|
@@ -24,6 +25,7 @@ import { markdownInlineSegments } from "./markdown-inline.js";
|
|
|
24
25
|
import { hashString } from "./render-signature.js";
|
|
25
26
|
import { findToolRenderer } from "./tool-renderers/registry.js";
|
|
26
27
|
import { writeToolKey } from "./tool-renderers/write.js";
|
|
28
|
+
import { discoverModelProviderGroups, getVisibleModelProviders, localModelsForProvider, } from "./model-picker-data.js";
|
|
27
29
|
import { formatWritePreview, isWritePreviewTool } from "./tool-renderers/write-preview.js";
|
|
28
30
|
import { extractStreamingArgsHint } from "./streaming-tool-args.js";
|
|
29
31
|
import { getNextPermissionMode, PERMISSION_MODE_INFO } from "../permission/mode.js";
|
|
@@ -396,6 +398,7 @@ function OpenTuiApp(props) {
|
|
|
396
398
|
redrawProviderDialog();
|
|
397
399
|
redrawApprovalPanel();
|
|
398
400
|
redrawQuestionPanel();
|
|
401
|
+
redrawStatsPanel();
|
|
399
402
|
redrawFeishuSetupPanel();
|
|
400
403
|
setSidebarTick((tick) => tick + 1);
|
|
401
404
|
renderer.requestRender();
|
|
@@ -483,6 +486,7 @@ function OpenTuiApp(props) {
|
|
|
483
486
|
const [pendingQuestion, setPendingQuestion] = createSignal();
|
|
484
487
|
const [pendingFeedback, setPendingFeedback] = createSignal();
|
|
485
488
|
const [pendingFeishuSetup, setPendingFeishuSetup] = createSignal();
|
|
489
|
+
let statsPanel;
|
|
486
490
|
const questionSyncTimers = new Set();
|
|
487
491
|
let feishuSetupAbortController;
|
|
488
492
|
let pendingApprovalRef;
|
|
@@ -490,6 +494,8 @@ function OpenTuiApp(props) {
|
|
|
490
494
|
const [approvalOptionIdx, setApprovalOptionIdx] = createSignal(0);
|
|
491
495
|
let picker;
|
|
492
496
|
let providerDialog;
|
|
497
|
+
let providerDialogModelItems;
|
|
498
|
+
let providerDialogModelRefreshId = 0;
|
|
493
499
|
let previousPickerForKey;
|
|
494
500
|
let homePromptRef;
|
|
495
501
|
let sessionPromptRef;
|
|
@@ -546,6 +552,17 @@ function OpenTuiApp(props) {
|
|
|
546
552
|
let feedbackPreviewShell;
|
|
547
553
|
let feedbackPreviewText;
|
|
548
554
|
let feedbackFooterText;
|
|
555
|
+
let statsRoot;
|
|
556
|
+
let statsPanelBox;
|
|
557
|
+
let statsTitle;
|
|
558
|
+
let statsEsc;
|
|
559
|
+
let statsTab7Box;
|
|
560
|
+
let statsTab30Box;
|
|
561
|
+
let statsTab7Text;
|
|
562
|
+
let statsTab30Text;
|
|
563
|
+
let statsBodyScroll;
|
|
564
|
+
let statsBodyText;
|
|
565
|
+
let statsFooterText;
|
|
549
566
|
let feishuSetupRoot;
|
|
550
567
|
let feishuSetupPanel;
|
|
551
568
|
let feishuSetupTitle;
|
|
@@ -713,6 +730,12 @@ function OpenTuiApp(props) {
|
|
|
713
730
|
feedbackRoot?.focus();
|
|
714
731
|
}, 0);
|
|
715
732
|
}
|
|
733
|
+
function focusStatsPanel() {
|
|
734
|
+
setTimeout(() => {
|
|
735
|
+
if (statsPanel)
|
|
736
|
+
statsRoot?.focus();
|
|
737
|
+
}, 0);
|
|
738
|
+
}
|
|
716
739
|
function focusFeishuSetupPanel() {
|
|
717
740
|
setTimeout(() => {
|
|
718
741
|
const state = pendingFeishuSetup();
|
|
@@ -852,7 +875,7 @@ function OpenTuiApp(props) {
|
|
|
852
875
|
return false;
|
|
853
876
|
return !!event.shift;
|
|
854
877
|
};
|
|
855
|
-
const canInsertPromptNewline = () => !pendingApproval() && !pendingPlan() && !pendingQuestion() && !pendingFeedback() && !pendingFeishuSetup();
|
|
878
|
+
const canInsertPromptNewline = () => !pendingApproval() && !pendingPlan() && !pendingQuestion() && !pendingFeedback() && !statsPanel && !pendingFeishuSetup();
|
|
856
879
|
const sidebarFits = () => dimensions().width > SESSION_SIDEBAR_WIDTH + 40;
|
|
857
880
|
const sidebarVisible = () => {
|
|
858
881
|
if (!sessionActive())
|
|
@@ -1193,6 +1216,7 @@ function OpenTuiApp(props) {
|
|
|
1193
1216
|
switch (request.type) {
|
|
1194
1217
|
case "bash": return "Bash";
|
|
1195
1218
|
case "edit": return "Edit";
|
|
1219
|
+
case "patch": return "Patch";
|
|
1196
1220
|
case "write": return "Write";
|
|
1197
1221
|
case "lsp": return "Lsp";
|
|
1198
1222
|
}
|
|
@@ -1798,6 +1822,71 @@ function OpenTuiApp(props) {
|
|
|
1798
1822
|
}
|
|
1799
1823
|
return false;
|
|
1800
1824
|
}
|
|
1825
|
+
function openStatsPanel() {
|
|
1826
|
+
picker = undefined;
|
|
1827
|
+
providerDialog = undefined;
|
|
1828
|
+
redrawProviderDialog();
|
|
1829
|
+
statsPanel = {
|
|
1830
|
+
range: "30d",
|
|
1831
|
+
bundle: collectUsageStatsBundle(),
|
|
1832
|
+
};
|
|
1833
|
+
activePrompt()?.clear();
|
|
1834
|
+
activePrompt()?.blur();
|
|
1835
|
+
promptText = "";
|
|
1836
|
+
syncStatsUI(true);
|
|
1837
|
+
}
|
|
1838
|
+
function closeStatsPanel() {
|
|
1839
|
+
statsPanel = undefined;
|
|
1840
|
+
syncStatsUI(false);
|
|
1841
|
+
restorePromptAfterModal();
|
|
1842
|
+
if (queuedInputCount() > 0)
|
|
1843
|
+
scheduleQueuedInputDrain();
|
|
1844
|
+
}
|
|
1845
|
+
function syncStatsUI(focus = false) {
|
|
1846
|
+
redrawStatsPanel();
|
|
1847
|
+
syncPromptSurfaces();
|
|
1848
|
+
redrawDock();
|
|
1849
|
+
rootBox?.requestRender();
|
|
1850
|
+
scrollbox?.requestRender();
|
|
1851
|
+
if (focus || statsPanel)
|
|
1852
|
+
focusStatsPanel();
|
|
1853
|
+
}
|
|
1854
|
+
function setStatsRange(range) {
|
|
1855
|
+
if (!statsPanel || statsPanel.range === range)
|
|
1856
|
+
return;
|
|
1857
|
+
statsPanel = { ...statsPanel, range };
|
|
1858
|
+
redrawStatsPanel();
|
|
1859
|
+
}
|
|
1860
|
+
function handleStatsKey(event) {
|
|
1861
|
+
if (!statsPanel)
|
|
1862
|
+
return false;
|
|
1863
|
+
const name = keyNameFromEvent(event);
|
|
1864
|
+
if (name === "escape") {
|
|
1865
|
+
closeStatsPanel();
|
|
1866
|
+
event.preventDefault?.();
|
|
1867
|
+
event.stopPropagation?.();
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
if (name === "left" || name === "h") {
|
|
1871
|
+
setStatsRange("7d");
|
|
1872
|
+
event.preventDefault?.();
|
|
1873
|
+
event.stopPropagation?.();
|
|
1874
|
+
return true;
|
|
1875
|
+
}
|
|
1876
|
+
if (name === "right" || name === "l") {
|
|
1877
|
+
setStatsRange("30d");
|
|
1878
|
+
event.preventDefault?.();
|
|
1879
|
+
event.stopPropagation?.();
|
|
1880
|
+
return true;
|
|
1881
|
+
}
|
|
1882
|
+
if (name === "tab") {
|
|
1883
|
+
setStatsRange(statsPanel.range === "30d" ? "7d" : "30d");
|
|
1884
|
+
event.preventDefault?.();
|
|
1885
|
+
event.stopPropagation?.();
|
|
1886
|
+
return true;
|
|
1887
|
+
}
|
|
1888
|
+
return true;
|
|
1889
|
+
}
|
|
1801
1890
|
function openFeishuSetup() {
|
|
1802
1891
|
picker = undefined;
|
|
1803
1892
|
providerDialog = undefined;
|
|
@@ -2048,6 +2137,8 @@ function OpenTuiApp(props) {
|
|
|
2048
2137
|
return "question";
|
|
2049
2138
|
if (pendingFeedback())
|
|
2050
2139
|
return "feedback";
|
|
2140
|
+
if (statsPanel)
|
|
2141
|
+
return "stats";
|
|
2051
2142
|
if (providerDialog)
|
|
2052
2143
|
return "provider";
|
|
2053
2144
|
if (pendingFeishuSetup())
|
|
@@ -2067,6 +2158,8 @@ function OpenTuiApp(props) {
|
|
|
2067
2158
|
return handleQuestionKey(event);
|
|
2068
2159
|
case "feedback":
|
|
2069
2160
|
return handleFeedbackKey(event);
|
|
2161
|
+
case "stats":
|
|
2162
|
+
return handleStatsKey(event);
|
|
2070
2163
|
case "provider":
|
|
2071
2164
|
return handleProviderDialogKey(event);
|
|
2072
2165
|
case "feishu":
|
|
@@ -2084,6 +2177,8 @@ function OpenTuiApp(props) {
|
|
|
2084
2177
|
}
|
|
2085
2178
|
if (owner === "feedback")
|
|
2086
2179
|
return pendingFeedback()?.stage !== "edit";
|
|
2180
|
+
if (owner === "stats")
|
|
2181
|
+
return true;
|
|
2087
2182
|
if (owner === "feishu")
|
|
2088
2183
|
return pendingFeishuSetup()?.kind !== "binding";
|
|
2089
2184
|
return false;
|
|
@@ -2223,14 +2318,14 @@ function OpenTuiApp(props) {
|
|
|
2223
2318
|
return currentTranscriptMessages(extra).some((message) => hasRenderableMessage(message, effectiveShowThinking()));
|
|
2224
2319
|
}
|
|
2225
2320
|
function isHomeSurfaceActive(extra) {
|
|
2226
|
-
return !hasTranscriptMessages(extra) && !pendingPlan() && !pendingQuestion() && !pendingFeedback() && !pendingFeishuSetup();
|
|
2321
|
+
return !hasTranscriptMessages(extra) && !pendingPlan() && !pendingQuestion() && !pendingFeedback() && !statsPanel && !pendingFeishuSetup();
|
|
2227
2322
|
}
|
|
2228
2323
|
function syncPromptSurfaces(focus = false) {
|
|
2229
2324
|
const homeActive = isHomeSurfaceActive(streamingDisplay);
|
|
2230
2325
|
const nextSessionActive = !homeActive;
|
|
2231
2326
|
const surfaceChanged = sessionActive() !== nextSessionActive;
|
|
2232
2327
|
setSessionActive(nextSessionActive);
|
|
2233
|
-
const modalComposerHidden = !!pendingQuestion() || !!pendingFeedback() || !!pendingFeishuSetup();
|
|
2328
|
+
const modalComposerHidden = !!pendingQuestion() || !!pendingFeedback() || !!statsPanel || !!pendingFeishuSetup();
|
|
2234
2329
|
if (homeSurfaceShell)
|
|
2235
2330
|
homeSurfaceShell.visible = homeActive;
|
|
2236
2331
|
if (homeComposerShell)
|
|
@@ -2333,6 +2428,18 @@ function OpenTuiApp(props) {
|
|
|
2333
2428
|
redrawTranscriptWithQueuedDisplays();
|
|
2334
2429
|
return changed;
|
|
2335
2430
|
}
|
|
2431
|
+
function removeQueuedUserDisplay(displayId) {
|
|
2432
|
+
if (!displayId)
|
|
2433
|
+
return false;
|
|
2434
|
+
const beforeDisplayCount = displayMessages.length;
|
|
2435
|
+
const beforeQueuedCount = queuedDisplayMessages.length;
|
|
2436
|
+
displayMessages = displayMessages.filter((message) => message.clientId !== displayId);
|
|
2437
|
+
queuedDisplayMessages = queuedDisplayMessages.filter((message) => message.clientId !== displayId);
|
|
2438
|
+
const changed = displayMessages.length !== beforeDisplayCount || queuedDisplayMessages.length !== beforeQueuedCount;
|
|
2439
|
+
if (changed)
|
|
2440
|
+
redrawTranscriptWithQueuedDisplays();
|
|
2441
|
+
return changed;
|
|
2442
|
+
}
|
|
2336
2443
|
function promoteQueuedUserDisplay(displayId, fallbackContent) {
|
|
2337
2444
|
if (!displayId)
|
|
2338
2445
|
return false;
|
|
@@ -2425,6 +2532,7 @@ function OpenTuiApp(props) {
|
|
|
2425
2532
|
|| pendingPlan()
|
|
2426
2533
|
|| pendingQuestion()
|
|
2427
2534
|
|| pendingFeedback()
|
|
2535
|
+
|| statsPanel
|
|
2428
2536
|
|| providerDialog
|
|
2429
2537
|
|| picker) {
|
|
2430
2538
|
return;
|
|
@@ -2522,6 +2630,10 @@ function OpenTuiApp(props) {
|
|
|
2522
2630
|
function cancelActiveAgentRun() {
|
|
2523
2631
|
if (!activeRun || activeRun.abortController.signal.aborted)
|
|
2524
2632
|
return false;
|
|
2633
|
+
traceEvent("tui_running_cancel", {
|
|
2634
|
+
runId: activeRun.id,
|
|
2635
|
+
pendingQueuedInputs: queuedInputCount(),
|
|
2636
|
+
}, { surface: "tui" });
|
|
2525
2637
|
clearRunningCancelHint();
|
|
2526
2638
|
activeRun.abortController.abort(new AgentAbortError("Agent run cancelled by user."));
|
|
2527
2639
|
setNotice("Agent run cancelled");
|
|
@@ -2556,6 +2668,7 @@ function OpenTuiApp(props) {
|
|
|
2556
2668
|
if (!activeRun || activeRun.abortController.signal.aborted)
|
|
2557
2669
|
return false;
|
|
2558
2670
|
const shouldCancel = armRunningCancelHint(activeRun);
|
|
2671
|
+
traceKeyRoute(event ? "key" : "raw", name, !shouldCancel ? "armed_cancel" : "confirm_cancel");
|
|
2559
2672
|
if (!shouldCancel) {
|
|
2560
2673
|
if (event)
|
|
2561
2674
|
preventGlobalKey(event);
|
|
@@ -2573,33 +2686,72 @@ function OpenTuiApp(props) {
|
|
|
2573
2686
|
if (!isRunning() || activeModalKeyOwner())
|
|
2574
2687
|
return false;
|
|
2575
2688
|
queuePromptFromComposer({ notice: "Queued next message" });
|
|
2689
|
+
traceKeyRoute(event ? "key" : "raw", name, "queued_next_message");
|
|
2576
2690
|
if (event)
|
|
2577
2691
|
preventGlobalKey(event);
|
|
2578
2692
|
return true;
|
|
2579
2693
|
}
|
|
2694
|
+
function traceKeyRoute(source, name, result) {
|
|
2695
|
+
const shouldTrace = result !== "unhandled"
|
|
2696
|
+
|| name === "escape"
|
|
2697
|
+
|| name === "enter"
|
|
2698
|
+
|| name === "tab"
|
|
2699
|
+
|| name === "up"
|
|
2700
|
+
|| name === "down"
|
|
2701
|
+
|| name === "left"
|
|
2702
|
+
|| name === "right"
|
|
2703
|
+
|| name === "ctrl-c"
|
|
2704
|
+
|| !!activeModalKeyOwner()
|
|
2705
|
+
|| isRunning();
|
|
2706
|
+
if (!shouldTrace)
|
|
2707
|
+
return;
|
|
2708
|
+
traceEvent("tui_key_route", {
|
|
2709
|
+
source,
|
|
2710
|
+
key: name,
|
|
2711
|
+
result,
|
|
2712
|
+
modalOwner: activeModalKeyOwner(),
|
|
2713
|
+
running: isRunning(),
|
|
2714
|
+
activeRunId: activeRun?.id,
|
|
2715
|
+
pendingApproval: !!pendingApproval(),
|
|
2716
|
+
pendingPlan: !!pendingPlan(),
|
|
2717
|
+
pendingQuestion: !!pendingQuestion(),
|
|
2718
|
+
providerDialog: !!providerDialog,
|
|
2719
|
+
picker: !!picker,
|
|
2720
|
+
}, { surface: "tui" });
|
|
2721
|
+
}
|
|
2580
2722
|
function routeGlobalRawSequence(sequence) {
|
|
2581
2723
|
if (isCtrlCSequence(sequence)) {
|
|
2582
2724
|
void requestExit({ direct: true });
|
|
2725
|
+
traceKeyRoute("raw", "ctrl-c", "exit");
|
|
2583
2726
|
return true;
|
|
2584
2727
|
}
|
|
2585
2728
|
const name = keyNameFromSequence(sequence);
|
|
2586
2729
|
const modalName = modalKeyNameFromSequence(sequence);
|
|
2587
|
-
if (routeModalRawSequence(sequence))
|
|
2730
|
+
if (routeModalRawSequence(sequence)) {
|
|
2731
|
+
traceKeyRoute("raw", modalName || name, "modal");
|
|
2588
2732
|
return true;
|
|
2733
|
+
}
|
|
2589
2734
|
if (routeRunningCancel(name))
|
|
2590
2735
|
return true;
|
|
2591
2736
|
if (routeRunningQueue(modalName))
|
|
2592
2737
|
return true;
|
|
2593
|
-
if (cycleModeFromRawSequence(sequence))
|
|
2738
|
+
if (cycleModeFromRawSequence(sequence)) {
|
|
2739
|
+
traceKeyRoute("raw", name, "mode_cycle");
|
|
2594
2740
|
return true;
|
|
2741
|
+
}
|
|
2742
|
+
traceKeyRoute("raw", name, "unhandled");
|
|
2595
2743
|
return false;
|
|
2596
2744
|
}
|
|
2597
2745
|
function routeGlobalKeyEvent(event) {
|
|
2598
|
-
if (routeCtrlCExit(event))
|
|
2746
|
+
if (routeCtrlCExit(event)) {
|
|
2747
|
+
traceKeyRoute("key", "ctrl-c", "exit");
|
|
2599
2748
|
return true;
|
|
2749
|
+
}
|
|
2600
2750
|
const name = keyNameFromEvent(event);
|
|
2601
|
-
if (routeModalKey(event))
|
|
2751
|
+
if (routeModalKey(event)) {
|
|
2752
|
+
traceKeyRoute("key", name, "modal");
|
|
2602
2753
|
return true;
|
|
2754
|
+
}
|
|
2603
2755
|
if (routeRunningCancel(name, event))
|
|
2604
2756
|
return true;
|
|
2605
2757
|
if (routeRunningQueue(name, event))
|
|
@@ -2609,25 +2761,32 @@ function OpenTuiApp(props) {
|
|
|
2609
2761
|
if (event.ctrl && event.shift && name === "m") {
|
|
2610
2762
|
openMcpReconnectPicker();
|
|
2611
2763
|
event.preventDefault?.();
|
|
2764
|
+
traceKeyRoute("key", name, "mcp_picker");
|
|
2612
2765
|
return true;
|
|
2613
2766
|
}
|
|
2614
2767
|
if (event.ctrl && name === "t" && !picker) {
|
|
2615
2768
|
toggleThinkingVisibility();
|
|
2616
2769
|
event.preventDefault?.();
|
|
2770
|
+
traceKeyRoute("key", name, "toggle_thinking");
|
|
2617
2771
|
return true;
|
|
2618
2772
|
}
|
|
2619
2773
|
if (event.ctrl && name === "o" && !picker) {
|
|
2620
2774
|
toggleVerboseTrace();
|
|
2621
2775
|
event.preventDefault?.();
|
|
2776
|
+
traceKeyRoute("key", name, "toggle_verbose_trace");
|
|
2622
2777
|
return true;
|
|
2623
2778
|
}
|
|
2624
|
-
if (cycleModeFromKey(event))
|
|
2779
|
+
if (cycleModeFromKey(event)) {
|
|
2780
|
+
traceKeyRoute("key", name, "mode_cycle");
|
|
2625
2781
|
return true;
|
|
2782
|
+
}
|
|
2626
2783
|
if (event.ctrl && name === "p" && !picker && !isRunning()) {
|
|
2627
2784
|
openCommandPalette();
|
|
2628
2785
|
event.preventDefault?.();
|
|
2786
|
+
traceKeyRoute("key", name, "command_palette");
|
|
2629
2787
|
return true;
|
|
2630
2788
|
}
|
|
2789
|
+
traceKeyRoute("key", name, "unhandled");
|
|
2631
2790
|
return false;
|
|
2632
2791
|
}
|
|
2633
2792
|
function transcriptOptions() {
|
|
@@ -2739,6 +2898,7 @@ function OpenTuiApp(props) {
|
|
|
2739
2898
|
sessionActive();
|
|
2740
2899
|
syncSidebarChrome();
|
|
2741
2900
|
redrawQuestionPanel();
|
|
2901
|
+
redrawStatsPanel();
|
|
2742
2902
|
redrawFeishuSetupPanel();
|
|
2743
2903
|
scrollbox?.requestRender();
|
|
2744
2904
|
scheduleTranscriptScrollAfterUpdate(shouldFollow);
|
|
@@ -2800,6 +2960,13 @@ function OpenTuiApp(props) {
|
|
|
2800
2960
|
dock?.requestRender();
|
|
2801
2961
|
}
|
|
2802
2962
|
function openProviderDialog(step = "providers", providerId) {
|
|
2963
|
+
if (step === "models") {
|
|
2964
|
+
providerDialogModelItems = undefined;
|
|
2965
|
+
}
|
|
2966
|
+
else {
|
|
2967
|
+
providerDialogModelRefreshId++;
|
|
2968
|
+
providerDialogModelItems = undefined;
|
|
2969
|
+
}
|
|
2803
2970
|
const items = providerDialogItemsFor(step, providerId);
|
|
2804
2971
|
picker = undefined;
|
|
2805
2972
|
providerDialog = {
|
|
@@ -2815,9 +2982,14 @@ function OpenTuiApp(props) {
|
|
|
2815
2982
|
redrawDock();
|
|
2816
2983
|
redrawProviderDialog();
|
|
2817
2984
|
setTimeout(() => providerDialogInput?.focus(), 0);
|
|
2985
|
+
if (step === "models") {
|
|
2986
|
+
void refreshProviderDialogModelItems(providerId, items);
|
|
2987
|
+
}
|
|
2818
2988
|
}
|
|
2819
2989
|
function closeProviderDialog() {
|
|
2820
2990
|
providerDialog = undefined;
|
|
2991
|
+
providerDialogModelRefreshId++;
|
|
2992
|
+
providerDialogModelItems = undefined;
|
|
2821
2993
|
providerDialogRoot && (providerDialogRoot.visible = false);
|
|
2822
2994
|
providerDialogPanel && (providerDialogPanel.visible = false);
|
|
2823
2995
|
providerDialogRoot?.requestRender();
|
|
@@ -2833,6 +3005,9 @@ function OpenTuiApp(props) {
|
|
|
2833
3005
|
if (step === "skills")
|
|
2834
3006
|
return buildSkillItems();
|
|
2835
3007
|
if (step === "models") {
|
|
3008
|
+
if (providerDialogModelItems?.key === modelPickerCacheKey(providerId)) {
|
|
3009
|
+
return providerDialogModelItems.items;
|
|
3010
|
+
}
|
|
2836
3011
|
const modelItems = buildPickerItems("model", providerId);
|
|
2837
3012
|
if (modelItems.length || providerId)
|
|
2838
3013
|
return modelItems;
|
|
@@ -2843,6 +3018,34 @@ function OpenTuiApp(props) {
|
|
|
2843
3018
|
}
|
|
2844
3019
|
return [];
|
|
2845
3020
|
}
|
|
3021
|
+
function modelPickerCacheKey(providerId) {
|
|
3022
|
+
return providerId || "__all__";
|
|
3023
|
+
}
|
|
3024
|
+
async function refreshProviderDialogModelItems(providerId, localItems) {
|
|
3025
|
+
const refreshId = ++providerDialogModelRefreshId;
|
|
3026
|
+
const cacheKey = modelPickerCacheKey(providerId);
|
|
3027
|
+
const localPreferredIndex = preferredPickerIndex("model", localItems);
|
|
3028
|
+
try {
|
|
3029
|
+
const remoteItems = await buildRemoteModelPickerItems(providerId);
|
|
3030
|
+
if (refreshId !== providerDialogModelRefreshId)
|
|
3031
|
+
return;
|
|
3032
|
+
if (remoteItems.length === 0)
|
|
3033
|
+
return;
|
|
3034
|
+
const state = providerDialog;
|
|
3035
|
+
if (!state || state.step !== "models" || modelPickerCacheKey(state.providerId) !== cacheKey)
|
|
3036
|
+
return;
|
|
3037
|
+
providerDialogModelItems = { key: cacheKey, items: remoteItems };
|
|
3038
|
+
const remotePreferredIndex = preferredPickerIndex("model", remoteItems);
|
|
3039
|
+
const nextIndex = state.index === localPreferredIndex
|
|
3040
|
+
? remotePreferredIndex
|
|
3041
|
+
: Math.min(state.index, Math.max(0, remoteItems.length - 1));
|
|
3042
|
+
providerDialog = { ...state, index: nextIndex };
|
|
3043
|
+
redrawProviderDialog();
|
|
3044
|
+
}
|
|
3045
|
+
catch {
|
|
3046
|
+
// Keep the already-rendered local catalog when remote model discovery fails.
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
2846
3049
|
function providerDialogFilteredItems(state = providerDialog) {
|
|
2847
3050
|
if (!state || state.step === "key")
|
|
2848
3051
|
return [];
|
|
@@ -3434,6 +3637,127 @@ function OpenTuiApp(props) {
|
|
|
3434
3637
|
feedbackRoot.requestRender();
|
|
3435
3638
|
feedbackInput?.requestRender();
|
|
3436
3639
|
}
|
|
3640
|
+
function redrawStatsPanel() {
|
|
3641
|
+
if (!statsRoot)
|
|
3642
|
+
return;
|
|
3643
|
+
const state = statsPanel;
|
|
3644
|
+
if (!state) {
|
|
3645
|
+
statsRoot.visible = false;
|
|
3646
|
+
statsPanelBox && (statsPanelBox.visible = false);
|
|
3647
|
+
statsRoot.requestRender();
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3650
|
+
const terminalWidth = dimensions().width;
|
|
3651
|
+
const terminalHeight = dimensions().height;
|
|
3652
|
+
const width = Math.max(56, Math.min(84, terminalWidth - 4));
|
|
3653
|
+
const bodyWidth = Math.max(48, width - 8);
|
|
3654
|
+
const stats = state.bundle.ranges[state.range];
|
|
3655
|
+
const body = formatStatsPanelBody(stats, bodyWidth);
|
|
3656
|
+
const bodyLines = body.split("\n");
|
|
3657
|
+
const height = Math.min(Math.max(22, bodyLines.length + 7), Math.max(18, terminalHeight - 4));
|
|
3658
|
+
const bodyHeight = Math.max(8, height - 8);
|
|
3659
|
+
statsRoot.visible = true;
|
|
3660
|
+
statsRoot.width = terminalWidth;
|
|
3661
|
+
statsRoot.height = terminalHeight;
|
|
3662
|
+
statsRoot.left = 0;
|
|
3663
|
+
statsRoot.top = 0;
|
|
3664
|
+
statsRoot.backgroundColor = modalBackdropColor();
|
|
3665
|
+
if (statsPanelBox) {
|
|
3666
|
+
statsPanelBox.visible = true;
|
|
3667
|
+
statsPanelBox.width = width;
|
|
3668
|
+
statsPanelBox.height = height;
|
|
3669
|
+
statsPanelBox.left = Math.max(0, Math.floor((terminalWidth - width) / 2));
|
|
3670
|
+
statsPanelBox.top = Math.max(0, Math.floor((terminalHeight - height) / 3));
|
|
3671
|
+
statsPanelBox.backgroundColor = theme.backgroundPanel;
|
|
3672
|
+
statsPanelBox.borderColor = theme.backgroundPanel;
|
|
3673
|
+
}
|
|
3674
|
+
if (statsTitle)
|
|
3675
|
+
statsTitle.content = "Stats";
|
|
3676
|
+
if (statsEsc)
|
|
3677
|
+
statsEsc.content = "esc";
|
|
3678
|
+
syncStatsTab(statsTab7Box, statsTab7Text, state.range === "7d", "7 days");
|
|
3679
|
+
syncStatsTab(statsTab30Box, statsTab30Text, state.range === "30d", "30 days");
|
|
3680
|
+
if (statsBodyText) {
|
|
3681
|
+
statsBodyText.content = statsPanelBodyStyledText(stats, bodyWidth);
|
|
3682
|
+
statsBodyText.width = bodyWidth;
|
|
3683
|
+
}
|
|
3684
|
+
if (statsBodyScroll) {
|
|
3685
|
+
statsBodyScroll.width = bodyWidth;
|
|
3686
|
+
statsBodyScroll.height = bodyHeight;
|
|
3687
|
+
statsBodyScroll.requestRender();
|
|
3688
|
+
}
|
|
3689
|
+
if (statsFooterText) {
|
|
3690
|
+
statsFooterText.content = statsFooterHint(state.range);
|
|
3691
|
+
statsFooterText.width = bodyWidth;
|
|
3692
|
+
statsFooterText.bg = theme.backgroundPanel;
|
|
3693
|
+
}
|
|
3694
|
+
statsPanelBox?.requestRender();
|
|
3695
|
+
statsRoot.requestRender();
|
|
3696
|
+
}
|
|
3697
|
+
function statsFooterHint(range) {
|
|
3698
|
+
return `left/right:range|tab:toggle|esc:close|view:${range}`;
|
|
3699
|
+
}
|
|
3700
|
+
function statsPanelBodyStyledText(stats, width) {
|
|
3701
|
+
const chunks = [];
|
|
3702
|
+
const lines = formatStatsPanelBody(stats, width).split("\n");
|
|
3703
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
3704
|
+
appendStatsPanelLine(chunks, lines[index]);
|
|
3705
|
+
if (index < lines.length - 1)
|
|
3706
|
+
chunks.push(fg(theme.text)("\n"));
|
|
3707
|
+
}
|
|
3708
|
+
return new StyledText(chunks);
|
|
3709
|
+
}
|
|
3710
|
+
function appendStatsPanelLine(chunks, line) {
|
|
3711
|
+
if (isStatsHeatmapWeekdayLine(line)) {
|
|
3712
|
+
chunks.push(fg(theme.text)(line.slice(0, 5)));
|
|
3713
|
+
appendStatsHeatmapDots(chunks, line.slice(5));
|
|
3714
|
+
return;
|
|
3715
|
+
}
|
|
3716
|
+
if (line.trim() === "Less . o O @ More") {
|
|
3717
|
+
appendStatsHeatmapLegend(chunks, line.length - line.trimStart().length);
|
|
3718
|
+
return;
|
|
3719
|
+
}
|
|
3720
|
+
chunks.push(fg(theme.text)(line));
|
|
3721
|
+
}
|
|
3722
|
+
function isStatsHeatmapWeekdayLine(line) {
|
|
3723
|
+
return /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) /.test(line);
|
|
3724
|
+
}
|
|
3725
|
+
function appendStatsHeatmapDots(chunks, text) {
|
|
3726
|
+
const colors = statsHeatmapDotColors();
|
|
3727
|
+
const colorByLevel = {
|
|
3728
|
+
".": colors[0],
|
|
3729
|
+
o: colors[1],
|
|
3730
|
+
O: colors[2],
|
|
3731
|
+
"@": colors[3],
|
|
3732
|
+
};
|
|
3733
|
+
for (const char of text) {
|
|
3734
|
+
const color = colorByLevel[char];
|
|
3735
|
+
chunks.push(color ? fg(color)("•") : fg(theme.text)(char));
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
function appendStatsHeatmapLegend(chunks, indent) {
|
|
3739
|
+
const colors = statsHeatmapDotColors();
|
|
3740
|
+
chunks.push(fg(theme.textMuted)(`${" ".repeat(indent)}Less `));
|
|
3741
|
+
colors.forEach((color, index) => {
|
|
3742
|
+
if (index > 0)
|
|
3743
|
+
chunks.push(fg(theme.textMuted)(" "));
|
|
3744
|
+
chunks.push(fg(color)("•"));
|
|
3745
|
+
});
|
|
3746
|
+
chunks.push(fg(theme.textMuted)(" More"));
|
|
3747
|
+
}
|
|
3748
|
+
function statsHeatmapDotColors() {
|
|
3749
|
+
return isLightTheme()
|
|
3750
|
+
? ["#D9B98E", "#BE7D37", "#A56218", theme.warning]
|
|
3751
|
+
: ["#6B471D", "#9D6728", "#D18830", theme.warning];
|
|
3752
|
+
}
|
|
3753
|
+
function syncStatsTab(box, text, active, label) {
|
|
3754
|
+
if (box)
|
|
3755
|
+
box.backgroundColor = active ? theme.primary : theme.backgroundElement;
|
|
3756
|
+
if (text) {
|
|
3757
|
+
text.content = label;
|
|
3758
|
+
text.fg = active ? contrastText(theme.primary) : theme.textMuted;
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3437
3761
|
function redrawFeishuSetupPanel() {
|
|
3438
3762
|
if (!feishuSetupRoot)
|
|
3439
3763
|
return;
|
|
@@ -4340,6 +4664,7 @@ function OpenTuiApp(props) {
|
|
|
4340
4664
|
toggleSidebar,
|
|
4341
4665
|
setSidebarMode: applySidebarMode,
|
|
4342
4666
|
openFeedback,
|
|
4667
|
+
openStats: openStatsPanel,
|
|
4343
4668
|
});
|
|
4344
4669
|
if (!handled)
|
|
4345
4670
|
return false;
|
|
@@ -4518,6 +4843,60 @@ function OpenTuiApp(props) {
|
|
|
4518
4843
|
await openPicker(item.after.mode, item.after.providerId);
|
|
4519
4844
|
}
|
|
4520
4845
|
}
|
|
4846
|
+
function buildLocalModelPickerItems(providerId) {
|
|
4847
|
+
const groups = getVisibleModelProviders(registry, providerId).map((provider) => ({
|
|
4848
|
+
provider,
|
|
4849
|
+
models: localModelsForProvider(registry, provider),
|
|
4850
|
+
}));
|
|
4851
|
+
return buildModelPickerItemsFromGroups(groups, providerId);
|
|
4852
|
+
}
|
|
4853
|
+
async function buildRemoteModelPickerItems(providerId) {
|
|
4854
|
+
const groups = await discoverModelProviderGroups(registry, providerId);
|
|
4855
|
+
return buildModelPickerItemsFromGroups(groups, providerId);
|
|
4856
|
+
}
|
|
4857
|
+
function buildModelPickerItemsFromGroups(groups, providerId) {
|
|
4858
|
+
const items = [];
|
|
4859
|
+
for (const { provider, models } of groups) {
|
|
4860
|
+
for (const model of models) {
|
|
4861
|
+
const reasoningLevels = getModelPickerReasoningLevels(provider.id, model.id);
|
|
4862
|
+
if (reasoningLevels.length > 0) {
|
|
4863
|
+
for (const level of reasoningLevels) {
|
|
4864
|
+
const isCurrent = props.agent.model === `${provider.id}:${model.id}` && props.agent.thinking === level;
|
|
4865
|
+
items.push({
|
|
4866
|
+
label: `${model.name} (${level})`,
|
|
4867
|
+
detail: isCurrent ? "(current)" : undefined,
|
|
4868
|
+
value: `${provider.id}:${model.id}`,
|
|
4869
|
+
command: `/model ${provider.id}:${model.id} --reasoning-effort ${level}`,
|
|
4870
|
+
category: provider.name,
|
|
4871
|
+
gutter: isCurrent ? "●" : undefined,
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
continue;
|
|
4875
|
+
}
|
|
4876
|
+
const isCurrent = props.agent.model === `${provider.id}:${model.id}`;
|
|
4877
|
+
items.push({
|
|
4878
|
+
label: model.name,
|
|
4879
|
+
detail: isCurrent ? "(current)" : undefined,
|
|
4880
|
+
value: `${provider.id}:${model.id}`,
|
|
4881
|
+
command: `/model ${provider.id}:${model.id}`,
|
|
4882
|
+
category: provider.name,
|
|
4883
|
+
gutter: isCurrent ? "●" : undefined,
|
|
4884
|
+
});
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
const currentModel = props.agent.model;
|
|
4888
|
+
if (!providerId && currentModel && !items.some((item) => item.value === currentModel)) {
|
|
4889
|
+
items.unshift({
|
|
4890
|
+
label: displayModel(currentModel),
|
|
4891
|
+
detail: "(current)",
|
|
4892
|
+
value: currentModel,
|
|
4893
|
+
command: `/model ${currentModel}`,
|
|
4894
|
+
category: "Recent",
|
|
4895
|
+
gutter: "●",
|
|
4896
|
+
});
|
|
4897
|
+
}
|
|
4898
|
+
return items;
|
|
4899
|
+
}
|
|
4521
4900
|
function buildPickerItems(kind, providerId) {
|
|
4522
4901
|
if (kind === "slash")
|
|
4523
4902
|
return [];
|
|
@@ -4526,60 +4905,7 @@ function OpenTuiApp(props) {
|
|
|
4526
4905
|
if (kind === "skill")
|
|
4527
4906
|
return buildSkillItems();
|
|
4528
4907
|
if (kind === "model") {
|
|
4529
|
-
|
|
4530
|
-
for (const provider of registry.getEnabled()) {
|
|
4531
|
-
if (providerId && provider.id !== providerId)
|
|
4532
|
-
continue;
|
|
4533
|
-
const customModels = registry.getModelConfig().getCustomModels(provider.id);
|
|
4534
|
-
const builtinProviderId = provider.id === "openai" && provider.authType === "oauth"
|
|
4535
|
-
? "openai-codex"
|
|
4536
|
-
: provider.id;
|
|
4537
|
-
const models = customModels.length > 0
|
|
4538
|
-
? customModels
|
|
4539
|
-
: listBuiltinModels(builtinProviderId).map((model) => ({
|
|
4540
|
-
id: model.id,
|
|
4541
|
-
name: model.name,
|
|
4542
|
-
providerId: provider.id,
|
|
4543
|
-
}));
|
|
4544
|
-
for (const model of models) {
|
|
4545
|
-
const reasoningLevels = getModelPickerReasoningLevels(provider.id, model.id);
|
|
4546
|
-
if (reasoningLevels.length > 0) {
|
|
4547
|
-
for (const level of reasoningLevels) {
|
|
4548
|
-
const isCurrent = props.agent.model === `${provider.id}:${model.id}` && props.agent.thinking === level;
|
|
4549
|
-
items.push({
|
|
4550
|
-
label: `${model.name} (${level})`,
|
|
4551
|
-
detail: isCurrent ? "(current)" : undefined,
|
|
4552
|
-
value: `${provider.id}:${model.id}`,
|
|
4553
|
-
command: `/model ${provider.id}:${model.id} --reasoning-effort ${level}`,
|
|
4554
|
-
category: provider.name,
|
|
4555
|
-
gutter: isCurrent ? "●" : undefined,
|
|
4556
|
-
});
|
|
4557
|
-
}
|
|
4558
|
-
continue;
|
|
4559
|
-
}
|
|
4560
|
-
const isCurrent = props.agent.model === `${provider.id}:${model.id}`;
|
|
4561
|
-
items.push({
|
|
4562
|
-
label: model.name,
|
|
4563
|
-
detail: isCurrent ? "(current)" : undefined,
|
|
4564
|
-
value: `${provider.id}:${model.id}`,
|
|
4565
|
-
command: `/model ${provider.id}:${model.id}`,
|
|
4566
|
-
category: provider.name,
|
|
4567
|
-
gutter: isCurrent ? "●" : undefined,
|
|
4568
|
-
});
|
|
4569
|
-
}
|
|
4570
|
-
}
|
|
4571
|
-
const currentModel = props.agent.model;
|
|
4572
|
-
if (!providerId && currentModel && !items.some((item) => item.value === currentModel)) {
|
|
4573
|
-
items.unshift({
|
|
4574
|
-
label: displayModel(currentModel),
|
|
4575
|
-
detail: "(current)",
|
|
4576
|
-
value: currentModel,
|
|
4577
|
-
command: `/model ${currentModel}`,
|
|
4578
|
-
category: "Recent",
|
|
4579
|
-
gutter: "●",
|
|
4580
|
-
});
|
|
4581
|
-
}
|
|
4582
|
-
return items;
|
|
4908
|
+
return buildLocalModelPickerItems(providerId);
|
|
4583
4909
|
}
|
|
4584
4910
|
if (kind === "provider") {
|
|
4585
4911
|
return buildProviderConnectItems();
|
|
@@ -4761,6 +5087,15 @@ function OpenTuiApp(props) {
|
|
|
4761
5087
|
redrawTranscript(undefined, nextMessages);
|
|
4762
5088
|
const taskStartedAt = Date.now();
|
|
4763
5089
|
const run = beginAgentRun();
|
|
5090
|
+
traceEvent("tui_agent_run_begin", {
|
|
5091
|
+
runId: run.id,
|
|
5092
|
+
input: summarizeTraceValue(actualInput),
|
|
5093
|
+
displayInput: summarizeTraceValue(displayInput),
|
|
5094
|
+
displayMessages: displayMessages.length,
|
|
5095
|
+
queuedInputs: queuedInputCount(),
|
|
5096
|
+
provider: activeProviderId,
|
|
5097
|
+
model: props.agent.apiModel,
|
|
5098
|
+
}, { surface: "tui" });
|
|
4764
5099
|
let assistantContent = "";
|
|
4765
5100
|
let assistantReasoning = "";
|
|
4766
5101
|
const toolCalls = [];
|
|
@@ -4805,6 +5140,14 @@ function OpenTuiApp(props) {
|
|
|
4805
5140
|
abortSignal: run.abortController.signal,
|
|
4806
5141
|
inputController: run.inputController,
|
|
4807
5142
|
})) {
|
|
5143
|
+
traceEvent("tui_agent_event", {
|
|
5144
|
+
runId: run.id,
|
|
5145
|
+
event: summarizeAgentEventForTrace(event),
|
|
5146
|
+
displayMessages: displayMessages.length,
|
|
5147
|
+
streamingChars: assistantContent.length,
|
|
5148
|
+
reasoningChars: assistantReasoning.length,
|
|
5149
|
+
toolCount: toolCalls.length,
|
|
5150
|
+
}, { surface: "tui" });
|
|
4808
5151
|
if (event.type === "turn_start") {
|
|
4809
5152
|
assistantContent = "";
|
|
4810
5153
|
assistantReasoning = "";
|
|
@@ -5011,6 +5354,11 @@ function OpenTuiApp(props) {
|
|
|
5011
5354
|
if (!runCancelled) {
|
|
5012
5355
|
runError = error?.message || String(error);
|
|
5013
5356
|
}
|
|
5357
|
+
traceEvent("tui_agent_run_error", {
|
|
5358
|
+
runId: run.id,
|
|
5359
|
+
cancelled: runCancelled,
|
|
5360
|
+
error: summarizeTraceError(error),
|
|
5361
|
+
}, { surface: "tui" });
|
|
5014
5362
|
}
|
|
5015
5363
|
finally {
|
|
5016
5364
|
if (pendingStreamingRedrawTimer !== undefined) {
|
|
@@ -5020,8 +5368,19 @@ function OpenTuiApp(props) {
|
|
|
5020
5368
|
pendingApprovalRef = undefined;
|
|
5021
5369
|
setPendingApproval(undefined);
|
|
5022
5370
|
setApprovalOptionIdx(0);
|
|
5371
|
+
traceEvent("tui_agent_run_end", {
|
|
5372
|
+
runId: run.id,
|
|
5373
|
+
cancelled: runCancelled,
|
|
5374
|
+
error: runError,
|
|
5375
|
+
displayMessages: displayMessages.length,
|
|
5376
|
+
queuedInputs: queuedInputCount(),
|
|
5377
|
+
}, { surface: "tui" });
|
|
5023
5378
|
for (const pendingInput of run.inputController.clear()) {
|
|
5024
5379
|
const pendingSteer = removePendingSteerInput(pendingInput.id);
|
|
5380
|
+
if (runCancelled) {
|
|
5381
|
+
removeQueuedUserDisplay(pendingSteer?.displayId);
|
|
5382
|
+
continue;
|
|
5383
|
+
}
|
|
5025
5384
|
requeueRejectedSteer(pendingInput.content, pendingSteer?.displayId);
|
|
5026
5385
|
}
|
|
5027
5386
|
finishAgentRun(run);
|
|
@@ -5035,6 +5394,7 @@ function OpenTuiApp(props) {
|
|
|
5035
5394
|
else if (runCancelled) {
|
|
5036
5395
|
if (!notice())
|
|
5037
5396
|
setNotice("Agent run cancelled");
|
|
5397
|
+
displayMessages = reconstructDisplayMessages(props.agent.messages);
|
|
5038
5398
|
redrawTranscript();
|
|
5039
5399
|
}
|
|
5040
5400
|
else {
|
|
@@ -5074,13 +5434,13 @@ function OpenTuiApp(props) {
|
|
|
5074
5434
|
return h("box", {
|
|
5075
5435
|
ref: (ref) => {
|
|
5076
5436
|
sessionComposerShell = ref;
|
|
5077
|
-
ref.visible = !isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !pendingFeedback() && !pendingFeishuSetup();
|
|
5437
|
+
ref.visible = !isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !pendingFeedback() && !statsPanel && !pendingFeishuSetup();
|
|
5078
5438
|
},
|
|
5079
5439
|
width: "100%",
|
|
5080
5440
|
paddingLeft: 2,
|
|
5081
5441
|
paddingRight: 2,
|
|
5082
5442
|
flexShrink: 0,
|
|
5083
|
-
visible: !isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !pendingFeishuSetup(),
|
|
5443
|
+
visible: !isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !statsPanel && !pendingFeishuSetup(),
|
|
5084
5444
|
}, renderPrompt({
|
|
5085
5445
|
ref: (ref) => { sessionPromptRef = ref; },
|
|
5086
5446
|
focused: !isHomeSurfaceActive(streamingDisplay),
|
|
@@ -5091,7 +5451,7 @@ function OpenTuiApp(props) {
|
|
|
5091
5451
|
onKeyDown: handlePickerKey,
|
|
5092
5452
|
onUiKeyDown: promptUiKeyDown,
|
|
5093
5453
|
getText: readPromptText,
|
|
5094
|
-
disabled: () => !!pendingFeedback(),
|
|
5454
|
+
disabled: () => !!pendingFeedback() || !!statsPanel,
|
|
5095
5455
|
mode,
|
|
5096
5456
|
registerModeLabel: registerPromptModeLabel,
|
|
5097
5457
|
registerModelLabel: registerPromptModelLabel,
|
|
@@ -5106,6 +5466,8 @@ function OpenTuiApp(props) {
|
|
|
5106
5466
|
return "Answer the question below";
|
|
5107
5467
|
if (pendingFeedback())
|
|
5108
5468
|
return "Describe feedback below";
|
|
5469
|
+
if (statsPanel)
|
|
5470
|
+
return "Stats panel is open";
|
|
5109
5471
|
const plan = pendingPlan();
|
|
5110
5472
|
if (plan)
|
|
5111
5473
|
return "Press Enter to approve plan or Esc to reject";
|
|
@@ -5136,14 +5498,14 @@ function OpenTuiApp(props) {
|
|
|
5136
5498
|
h("box", {
|
|
5137
5499
|
ref: (ref) => {
|
|
5138
5500
|
homeComposerShell = ref;
|
|
5139
|
-
ref.visible = isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !pendingFeedback() && !pendingFeishuSetup();
|
|
5501
|
+
ref.visible = isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !pendingFeedback() && !statsPanel && !pendingFeishuSetup();
|
|
5140
5502
|
},
|
|
5141
5503
|
width: "100%",
|
|
5142
5504
|
maxWidth: 75,
|
|
5143
5505
|
zIndex: 1000,
|
|
5144
5506
|
paddingTop: 1,
|
|
5145
5507
|
flexShrink: 0,
|
|
5146
|
-
visible: isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !pendingFeishuSetup(),
|
|
5508
|
+
visible: isHomeSurfaceActive(streamingDisplay) && !pendingQuestion() && !statsPanel && !pendingFeishuSetup(),
|
|
5147
5509
|
}, renderPrompt({
|
|
5148
5510
|
ref: (ref) => {
|
|
5149
5511
|
homePromptRef = ref;
|
|
@@ -5158,7 +5520,7 @@ function OpenTuiApp(props) {
|
|
|
5158
5520
|
onKeyDown: handlePickerKey,
|
|
5159
5521
|
onUiKeyDown: promptUiKeyDown,
|
|
5160
5522
|
getText: readPromptText,
|
|
5161
|
-
disabled: () => !!pendingFeedback(),
|
|
5523
|
+
disabled: () => !!pendingFeedback() || !!statsPanel,
|
|
5162
5524
|
mode,
|
|
5163
5525
|
registerModeLabel: registerPromptModeLabel,
|
|
5164
5526
|
registerModelLabel: registerPromptModelLabel,
|
|
@@ -5173,6 +5535,8 @@ function OpenTuiApp(props) {
|
|
|
5173
5535
|
return "Answer the question below";
|
|
5174
5536
|
if (pendingFeedback())
|
|
5175
5537
|
return "Describe feedback below";
|
|
5538
|
+
if (statsPanel)
|
|
5539
|
+
return "Stats panel is open";
|
|
5176
5540
|
const plan = pendingPlan();
|
|
5177
5541
|
if (plan)
|
|
5178
5542
|
return "Press Enter to approve plan or Esc to reject";
|
|
@@ -5419,6 +5783,131 @@ function OpenTuiApp(props) {
|
|
|
5419
5783
|
content: "ctrl+d submit · tab view payload · enter newline · esc cancel",
|
|
5420
5784
|
})));
|
|
5421
5785
|
}
|
|
5786
|
+
function renderStatsPanel() {
|
|
5787
|
+
return h("box", {
|
|
5788
|
+
ref: (ref) => {
|
|
5789
|
+
statsRoot = ref;
|
|
5790
|
+
redrawStatsPanel();
|
|
5791
|
+
},
|
|
5792
|
+
visible: false,
|
|
5793
|
+
focusable: true,
|
|
5794
|
+
position: "absolute",
|
|
5795
|
+
left: 0,
|
|
5796
|
+
top: 0,
|
|
5797
|
+
width: "100%",
|
|
5798
|
+
height: "100%",
|
|
5799
|
+
zIndex: 3050,
|
|
5800
|
+
backgroundColor: modalBackdropColor(),
|
|
5801
|
+
flexDirection: "column",
|
|
5802
|
+
onKeyDown: (event) => {
|
|
5803
|
+
if (handleStatsKey(event)) {
|
|
5804
|
+
event.preventDefault?.();
|
|
5805
|
+
event.stopPropagation?.();
|
|
5806
|
+
return true;
|
|
5807
|
+
}
|
|
5808
|
+
return false;
|
|
5809
|
+
},
|
|
5810
|
+
onMouseUp: () => closeStatsPanel(),
|
|
5811
|
+
}, h("box", {
|
|
5812
|
+
ref: (ref) => {
|
|
5813
|
+
statsPanelBox = ref;
|
|
5814
|
+
redrawStatsPanel();
|
|
5815
|
+
},
|
|
5816
|
+
visible: false,
|
|
5817
|
+
position: "absolute",
|
|
5818
|
+
width: 76,
|
|
5819
|
+
height: 24,
|
|
5820
|
+
backgroundColor: theme.backgroundPanel,
|
|
5821
|
+
flexDirection: "column",
|
|
5822
|
+
paddingTop: 1,
|
|
5823
|
+
onMouseUp: (event) => {
|
|
5824
|
+
event.stopPropagation?.();
|
|
5825
|
+
},
|
|
5826
|
+
}, [
|
|
5827
|
+
h("box", {
|
|
5828
|
+
flexDirection: "row",
|
|
5829
|
+
alignItems: "center",
|
|
5830
|
+
justifyContent: "space-between",
|
|
5831
|
+
paddingLeft: 4,
|
|
5832
|
+
paddingRight: 4,
|
|
5833
|
+
flexShrink: 0,
|
|
5834
|
+
}, h("text", {
|
|
5835
|
+
ref: (ref) => { statsTitle = ref; },
|
|
5836
|
+
fg: theme.text,
|
|
5837
|
+
content: "Stats",
|
|
5838
|
+
}), h("text", {
|
|
5839
|
+
ref: (ref) => { statsEsc = ref; },
|
|
5840
|
+
fg: theme.textMuted,
|
|
5841
|
+
content: "esc",
|
|
5842
|
+
onMouseUp: () => closeStatsPanel(),
|
|
5843
|
+
})),
|
|
5844
|
+
h("box", {
|
|
5845
|
+
flexDirection: "row",
|
|
5846
|
+
gap: 1,
|
|
5847
|
+
paddingLeft: 4,
|
|
5848
|
+
paddingRight: 4,
|
|
5849
|
+
paddingTop: 1,
|
|
5850
|
+
flexShrink: 0,
|
|
5851
|
+
}, h("box", {
|
|
5852
|
+
ref: (ref) => { statsTab7Box = ref; },
|
|
5853
|
+
paddingLeft: 1,
|
|
5854
|
+
paddingRight: 1,
|
|
5855
|
+
backgroundColor: theme.backgroundElement,
|
|
5856
|
+
onMouseUp: () => setStatsRange("7d"),
|
|
5857
|
+
}, h("text", {
|
|
5858
|
+
ref: (ref) => { statsTab7Text = ref; },
|
|
5859
|
+
fg: theme.textMuted,
|
|
5860
|
+
content: "7 days",
|
|
5861
|
+
})), h("box", {
|
|
5862
|
+
ref: (ref) => { statsTab30Box = ref; },
|
|
5863
|
+
paddingLeft: 1,
|
|
5864
|
+
paddingRight: 1,
|
|
5865
|
+
backgroundColor: theme.primary,
|
|
5866
|
+
onMouseUp: () => setStatsRange("30d"),
|
|
5867
|
+
}, h("text", {
|
|
5868
|
+
ref: (ref) => { statsTab30Text = ref; },
|
|
5869
|
+
fg: contrastText(theme.primary),
|
|
5870
|
+
content: "30 days",
|
|
5871
|
+
}))),
|
|
5872
|
+
h("box", {
|
|
5873
|
+
paddingLeft: 4,
|
|
5874
|
+
paddingRight: 4,
|
|
5875
|
+
paddingTop: 1,
|
|
5876
|
+
flexGrow: 1,
|
|
5877
|
+
minHeight: 0,
|
|
5878
|
+
}, h("scrollbox", {
|
|
5879
|
+
ref: (ref) => { statsBodyScroll = ref; },
|
|
5880
|
+
flexGrow: 1,
|
|
5881
|
+
minHeight: 0,
|
|
5882
|
+
height: 14,
|
|
5883
|
+
onMouseScroll: (event) => {
|
|
5884
|
+
event.stopPropagation?.();
|
|
5885
|
+
},
|
|
5886
|
+
}, h("text", {
|
|
5887
|
+
ref: (ref) => { statsBodyText = ref; },
|
|
5888
|
+
fg: theme.text,
|
|
5889
|
+
wrapMode: "none",
|
|
5890
|
+
content: "",
|
|
5891
|
+
}))),
|
|
5892
|
+
h("box", {
|
|
5893
|
+
flexDirection: "row",
|
|
5894
|
+
justifyContent: "space-between",
|
|
5895
|
+
paddingLeft: 4,
|
|
5896
|
+
paddingRight: 4,
|
|
5897
|
+
paddingTop: 1,
|
|
5898
|
+
paddingBottom: 1,
|
|
5899
|
+
flexShrink: 0,
|
|
5900
|
+
backgroundColor: theme.backgroundPanel,
|
|
5901
|
+
}, h("text", {
|
|
5902
|
+
ref: (ref) => { statsFooterText = ref; },
|
|
5903
|
+
fg: theme.textMuted,
|
|
5904
|
+
bg: theme.backgroundPanel,
|
|
5905
|
+
wrapMode: "none",
|
|
5906
|
+
truncate: true,
|
|
5907
|
+
content: "left/right:range|tab:toggle|esc:close|view:30d",
|
|
5908
|
+
})),
|
|
5909
|
+
]));
|
|
5910
|
+
}
|
|
5422
5911
|
function renderFeishuSetupPanel() {
|
|
5423
5912
|
return h("box", {
|
|
5424
5913
|
ref: (ref) => {
|
|
@@ -6293,6 +6782,7 @@ function OpenTuiApp(props) {
|
|
|
6293
6782
|
registerTraceBadge: registerFooterTraceBadge,
|
|
6294
6783
|
}),
|
|
6295
6784
|
renderProviderDialog(),
|
|
6785
|
+
renderStatsPanel(),
|
|
6296
6786
|
renderFeishuSetupPanel(),
|
|
6297
6787
|
renderNoticeOverlay(),
|
|
6298
6788
|
]);
|
|
@@ -6952,7 +7442,7 @@ function createTraceGroupRenderable(ctx, group, syntaxStyle, width = 80) {
|
|
|
6952
7442
|
}, children);
|
|
6953
7443
|
}
|
|
6954
7444
|
function shouldRenderTraceGroupAsRawTool(tool) {
|
|
6955
|
-
return tool.name === "question" || tool.name === "todo_write" || tool.name === "edit";
|
|
7445
|
+
return tool.name === "question" || tool.name === "todo_write" || tool.name === "edit" || tool.name === "apply_patch";
|
|
6956
7446
|
}
|
|
6957
7447
|
function traceGroupDetailLines(group) {
|
|
6958
7448
|
return group.previewLines.length > 0 ? group.previewLines : group.items;
|
|
@@ -7033,8 +7523,9 @@ function traceGroupRenderableSignature(group) {
|
|
|
7033
7523
|
return [
|
|
7034
7524
|
tool.id,
|
|
7035
7525
|
tool.name,
|
|
7036
|
-
tool.status ?? (tool.result === undefined ? "pending" : "completed"),
|
|
7526
|
+
tool.status ?? (tool.result === undefined && !tool.resultCollapsed ? "pending" : "completed"),
|
|
7037
7527
|
tool.isError ? "error" : "ok",
|
|
7528
|
+
tool.resultCollapsed ? "collapsed" : "expanded",
|
|
7038
7529
|
stableStringify(tool.args),
|
|
7039
7530
|
tool.result ?? "",
|
|
7040
7531
|
stableStringify(tool.metadata ?? null),
|
|
@@ -7046,8 +7537,9 @@ function toolRenderableSignature(tool, writeExpanded) {
|
|
|
7046
7537
|
return [
|
|
7047
7538
|
tool.id,
|
|
7048
7539
|
tool.name,
|
|
7049
|
-
tool.status ?? (tool.result === undefined ? "pending" : "completed"),
|
|
7540
|
+
tool.status ?? (tool.result === undefined && !tool.resultCollapsed ? "pending" : "completed"),
|
|
7050
7541
|
tool.isError ? "error" : "ok",
|
|
7542
|
+
tool.resultCollapsed ? "collapsed" : "expanded",
|
|
7051
7543
|
tool.streamingArgs ? "streaming-args" : "args-complete",
|
|
7052
7544
|
writeExpanded ? "expanded" : "collapsed",
|
|
7053
7545
|
hashString(stableStringify(tool.args)),
|
|
@@ -7174,16 +7666,41 @@ function createMarkdownList(ctx, token, palette, defaultFg) {
|
|
|
7174
7666
|
flexShrink: 0,
|
|
7175
7667
|
}, items.map((item, index) => {
|
|
7176
7668
|
const marker = ordered ? `${start + index}. ` : "• ";
|
|
7177
|
-
return
|
|
7178
|
-
fg(theme.textMuted)(marker),
|
|
7179
|
-
...markdownInlineToStyledText(markdownTokenInlineTokens(item), palette, item.text ?? "").chunks,
|
|
7180
|
-
]), {
|
|
7181
|
-
fg: defaultFg,
|
|
7182
|
-
wrapMode: "word",
|
|
7183
|
-
flexShrink: 0,
|
|
7184
|
-
});
|
|
7669
|
+
return createMarkdownListItem(ctx, item, marker, palette, defaultFg);
|
|
7185
7670
|
}));
|
|
7186
7671
|
}
|
|
7672
|
+
function createMarkdownListItem(ctx, item, marker, palette, defaultFg) {
|
|
7673
|
+
const tokens = Array.isArray(item?.tokens) ? item.tokens : [];
|
|
7674
|
+
const inlineTokens = tokens.filter((child) => !isMarkdownListToken(child));
|
|
7675
|
+
const nestedLists = tokens.filter(isMarkdownListToken);
|
|
7676
|
+
const fallback = tokens.length > 0 ? "" : (item?.text ?? "");
|
|
7677
|
+
const children = [];
|
|
7678
|
+
const line = markdownInlineToStyledText(inlineTokens, palette, fallback);
|
|
7679
|
+
children.push(createText(ctx, new StyledText([
|
|
7680
|
+
fg(theme.textMuted)(marker),
|
|
7681
|
+
...line.chunks,
|
|
7682
|
+
]), {
|
|
7683
|
+
fg: defaultFg,
|
|
7684
|
+
wrapMode: "word",
|
|
7685
|
+
flexShrink: 0,
|
|
7686
|
+
}));
|
|
7687
|
+
for (const nestedList of nestedLists) {
|
|
7688
|
+
const nested = createMarkdownList(ctx, nestedList, palette, defaultFg);
|
|
7689
|
+
if (!nested)
|
|
7690
|
+
continue;
|
|
7691
|
+
children.push(createBox(ctx, {
|
|
7692
|
+
flexDirection: "column",
|
|
7693
|
+
flexShrink: 0,
|
|
7694
|
+
paddingLeft: Math.max(2, marker.length),
|
|
7695
|
+
}, [nested]));
|
|
7696
|
+
}
|
|
7697
|
+
return children.length === 1
|
|
7698
|
+
? children[0]
|
|
7699
|
+
: createBox(ctx, { flexDirection: "column", flexShrink: 0 }, children);
|
|
7700
|
+
}
|
|
7701
|
+
function isMarkdownListToken(token) {
|
|
7702
|
+
return token?.type === "list";
|
|
7703
|
+
}
|
|
7187
7704
|
function markdownTokenInlineTokens(token) {
|
|
7188
7705
|
if (Array.isArray(token?.tokens))
|
|
7189
7706
|
return token.tokens;
|
|
@@ -7775,10 +8292,10 @@ function renderTool(tool, syntaxStyle, width = 80) {
|
|
|
7775
8292
|
const icon = toolStateIcon(tool);
|
|
7776
8293
|
const color = toolColor(tool);
|
|
7777
8294
|
const diff = extractToolDiff(tool);
|
|
7778
|
-
if (diff && !tool.isError && tool.name === "edit") {
|
|
8295
|
+
if (diff && !tool.resultCollapsed && !tool.isError && (tool.name === "edit" || tool.name === "apply_patch")) {
|
|
7779
8296
|
return h("box", { paddingLeft: 3, marginTop: 1, flexDirection: "column", flexShrink: 0 }, h("text", { fg: color }, `${icon} ${displayToolName(tool.name)}${toolHeader(tool) ? ` ${toolHeader(tool)}` : ""}`), h("box", { paddingLeft: 1, marginTop: 1, border: ["left"], borderColor: theme.borderSubtle, flexDirection: "column", flexShrink: 0 }, renderDiffContent(diff, toolPath(tool), syntaxStyle, width)));
|
|
7780
8297
|
}
|
|
7781
|
-
if (isWritePreviewTool(tool)) {
|
|
8298
|
+
if (!tool.resultCollapsed && isWritePreviewTool(tool)) {
|
|
7782
8299
|
const hasContent = typeof tool.args.content === "string";
|
|
7783
8300
|
const contentStr = hasContent ? String(tool.args.content) : "";
|
|
7784
8301
|
const preview = hasContent ? formatWritePreview(contentStr, false) : null;
|
|
@@ -8237,7 +8754,7 @@ function appendRawToolTranscript(chunks, tool) {
|
|
|
8237
8754
|
append(`${content}\n`, color);
|
|
8238
8755
|
};
|
|
8239
8756
|
appendLine("");
|
|
8240
|
-
const icon = tool.name === "bash" ? "$" : tool.name === "edit" || tool.name === "write" ? "✎" : "●";
|
|
8757
|
+
const icon = tool.name === "bash" ? "$" : tool.name === "edit" || tool.name === "write" || tool.name === "apply_patch" ? "✎" : "●";
|
|
8241
8758
|
const color = toolColor(tool);
|
|
8242
8759
|
append(` ${icon} `, color);
|
|
8243
8760
|
append(displayToolName(tool.name), color);
|
|
@@ -8405,6 +8922,18 @@ function getApprovalPanelMeta(request) {
|
|
|
8405
8922
|
path: request.path,
|
|
8406
8923
|
};
|
|
8407
8924
|
}
|
|
8925
|
+
if (request.type === "patch") {
|
|
8926
|
+
return {
|
|
8927
|
+
icon: "→",
|
|
8928
|
+
title: `Patch ${path}`,
|
|
8929
|
+
subtitle: `${request.paths.length} file${request.paths.length === 1 ? "" : "s"}`,
|
|
8930
|
+
preview: request.diff || "No diff provided",
|
|
8931
|
+
previewHeight: 10,
|
|
8932
|
+
previewColor: request.diff ? theme.toolText : theme.textMuted,
|
|
8933
|
+
diff: request.diff,
|
|
8934
|
+
path: request.paths[0] ?? request.path,
|
|
8935
|
+
};
|
|
8936
|
+
}
|
|
8408
8937
|
return {
|
|
8409
8938
|
icon: "→",
|
|
8410
8939
|
title: `Write ${path}`,
|
|
@@ -8483,7 +9012,7 @@ function toolColor(tool) {
|
|
|
8483
9012
|
return theme.toolShell;
|
|
8484
9013
|
if (tool.name === "read")
|
|
8485
9014
|
return theme.toolRead;
|
|
8486
|
-
if (tool.name === "write" || tool.name === "edit")
|
|
9015
|
+
if (tool.name === "write" || tool.name === "edit" || tool.name === "apply_patch")
|
|
8487
9016
|
return theme.toolWrite;
|
|
8488
9017
|
if (tool.name === "grep" || tool.name === "glob" || tool.name === "web_search" || tool.name === "web_fetch")
|
|
8489
9018
|
return theme.toolSearch;
|
|
@@ -8494,6 +9023,7 @@ function displayToolName(name) {
|
|
|
8494
9023
|
read: "Read",
|
|
8495
9024
|
write: "Write",
|
|
8496
9025
|
edit: "Edit",
|
|
9026
|
+
apply_patch: "Patch",
|
|
8497
9027
|
bash: "Shell",
|
|
8498
9028
|
grep: "Grep",
|
|
8499
9029
|
glob: "Glob",
|
|
@@ -8526,16 +9056,29 @@ function toolHeader(tool) {
|
|
|
8526
9056
|
const agentId = args.agent_id ?? (Array.isArray(args.agent_ids) ? `${args.agent_ids.length} agents` : undefined);
|
|
8527
9057
|
return agentId ? `(${truncate(String(agentId), 64)})` : "";
|
|
8528
9058
|
}
|
|
8529
|
-
const value = args.path ?? args.command ?? args.pattern ?? args.url ?? args.query;
|
|
9059
|
+
const value = args.path ?? args.command ?? args.pattern ?? args.url ?? args.query ?? toolPath(tool);
|
|
8530
9060
|
return value ? `(${truncate(String(value).replace(/\n/g, " "), 64)})` : "";
|
|
8531
9061
|
}
|
|
8532
9062
|
function toolPath(tool) {
|
|
8533
|
-
const value = tool.args?.path
|
|
9063
|
+
const value = tool.args?.path
|
|
9064
|
+
?? tool.args?.filePath
|
|
9065
|
+
?? tool.metadata?.path
|
|
9066
|
+
?? (Array.isArray(tool.metadata?.paths) ? tool.metadata.paths[0] : undefined);
|
|
8534
9067
|
return typeof value === "string" ? value : undefined;
|
|
8535
9068
|
}
|
|
8536
9069
|
function extractToolDiff(tool) {
|
|
9070
|
+
if (tool.resultCollapsed)
|
|
9071
|
+
return undefined;
|
|
9072
|
+
if (typeof tool.metadata?.diff === "string" && tool.metadata.diff.trim().length > 0) {
|
|
9073
|
+
return tool.metadata.diff.trim();
|
|
9074
|
+
}
|
|
8537
9075
|
if (!tool.result)
|
|
8538
9076
|
return undefined;
|
|
9077
|
+
if (tool.result.includes("✂") ||
|
|
9078
|
+
tool.result.includes("chars truncated") ||
|
|
9079
|
+
tool.result.includes("chars omitted for UI")) {
|
|
9080
|
+
return undefined;
|
|
9081
|
+
}
|
|
8539
9082
|
const marker = "\n\nDiff:\n";
|
|
8540
9083
|
const index = tool.result.indexOf(marker);
|
|
8541
9084
|
if (index === -1)
|
|
@@ -8589,7 +9132,7 @@ function summarizeToolResult(tool) {
|
|
|
8589
9132
|
const matches = typeof tool.metadata?.matches === "number" ? tool.metadata.matches : undefined;
|
|
8590
9133
|
if (tool.name === "read")
|
|
8591
9134
|
return "";
|
|
8592
|
-
if (tool.name === "edit")
|
|
9135
|
+
if (tool.name === "edit" || tool.name === "apply_patch")
|
|
8593
9136
|
return "patched file";
|
|
8594
9137
|
if (tool.name === "write")
|
|
8595
9138
|
return "wrote file";
|
|
@@ -8615,7 +9158,7 @@ function toolStateIcon(tool) {
|
|
|
8615
9158
|
}
|
|
8616
9159
|
if (tool.name === "bash")
|
|
8617
9160
|
return "$";
|
|
8618
|
-
if (tool.name === "edit")
|
|
9161
|
+
if (tool.name === "edit" || tool.name === "apply_patch")
|
|
8619
9162
|
return "✎";
|
|
8620
9163
|
if (tool.name === "write")
|
|
8621
9164
|
return "✎";
|
|
@@ -8692,7 +9235,7 @@ function formatQuestionAnswer(answer) {
|
|
|
8692
9235
|
return answer?.length ? answer.join(", ") : "(no answer)";
|
|
8693
9236
|
}
|
|
8694
9237
|
function isToolFinished(tool) {
|
|
8695
|
-
return tool.status === "completed" || tool.status === "error" || tool.result !== undefined;
|
|
9238
|
+
return tool.status === "completed" || tool.status === "error" || tool.resultCollapsed === true || tool.result !== undefined;
|
|
8696
9239
|
}
|
|
8697
9240
|
function assistantStatusLabel(message) {
|
|
8698
9241
|
if (message.status === "responding")
|