zidane 5.6.15 → 5.7.4
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 +3 -1
- package/dist/{agent-CkJp_ZOR.d.ts → agent-BNS2nx_T.d.ts} +535 -15
- package/dist/agent-BNS2nx_T.d.ts.map +1 -0
- package/dist/chat/pure.d.ts +4 -0
- package/dist/chat/pure.js +3 -0
- package/dist/chat.d.ts +31 -661
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +5 -3
- package/dist/chat.js.map +1 -1
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js.map +1 -1
- package/dist/{contexts-BOtMvzli.js → contexts-BD2U_xpi.js} +2 -2
- package/dist/{contexts-BOtMvzli.js.map → contexts-BD2U_xpi.js.map} +1 -1
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/edit-utils-DnfNoj16.js +574 -0
- package/dist/edit-utils-DnfNoj16.js.map +1 -0
- package/dist/{errors-DdZXnyXE.js → errors-CoQnKRf1.js} +32 -2
- package/dist/{errors-DdZXnyXE.js.map → errors-CoQnKRf1.js.map} +1 -1
- package/dist/fetch-url-CPxfiXDa.js +518 -0
- package/dist/fetch-url-CPxfiXDa.js.map +1 -0
- package/dist/{image-sniff-CmlNMPMr.js → image-sniff-B7uFSNO1.js} +1 -1
- package/dist/{image-sniff-CmlNMPMr.js.map → image-sniff-B7uFSNO1.js.map} +1 -1
- package/dist/{index-CtIS28mN.d.ts → index-CZOwAJIX.d.ts} +2 -2
- package/dist/index-CZOwAJIX.d.ts.map +1 -0
- package/dist/{index-CsWckg9p.d.ts → index-Ck_AWt8P.d.ts} +3 -4
- package/dist/index-Ck_AWt8P.d.ts.map +1 -0
- package/dist/{index-BXQC3I4d.d.ts → index-KiS7w0dC.d.ts} +3 -3
- package/dist/index-KiS7w0dC.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/{interpolate-C55ZIcKz.js → interpolate-TySiqKzc.js} +23 -23
- package/dist/{interpolate-C55ZIcKz.js.map → interpolate-TySiqKzc.js.map} +1 -1
- package/dist/{login-CY9uShjX.js → login-BDeqENSe.js} +7 -58
- package/dist/login-BDeqENSe.js.map +1 -0
- package/dist/{mcp-DDOc8hOM.js → mcp-Kqzz-Rs_.js} +5 -5
- package/dist/{mcp-DDOc8hOM.js.map → mcp-Kqzz-Rs_.js.map} +1 -1
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +1 -1
- package/dist/{messages-B-tuI2Ur.js → messages-CvRQTdbR.js} +93 -30
- package/dist/messages-CvRQTdbR.js.map +1 -0
- package/dist/{presets-CMkLtFFW.js → presets-JuOnSI-i.js} +2 -2
- package/dist/{presets-CMkLtFFW.js.map → presets-JuOnSI-i.js.map} +1 -1
- package/dist/presets.d.ts +3 -3
- package/dist/presets.js +1 -1
- package/dist/{providers-CRQQDuxx.js → providers-h4HJPbbv.js} +485 -31
- package/dist/providers-h4HJPbbv.js.map +1 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +3 -3
- package/dist/restate.d.ts +1 -1
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js.map +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +1 -1
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-BCT6eYxo.js → session-BzLou2_-.js} +2 -2
- package/dist/{session-BCT6eYxo.js.map → session-BzLou2_-.js.map} +1 -1
- package/dist/session.d.ts +2 -2
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +1 -1
- package/dist/skills.js.map +1 -1
- package/dist/{stats-CIv4j3Sz.js → stats-DAKBEKjc.js} +12 -2
- package/dist/stats-DAKBEKjc.js.map +1 -0
- package/dist/{stdio-loader-OOOXzUvm.js → stdio-loader-Ce68wUmM.js} +4 -4
- package/dist/stdio-loader-Ce68wUmM.js.map +1 -0
- package/dist/tool-formatters-CU-j3a3e.d.ts +1471 -0
- package/dist/tool-formatters-CU-j3a3e.d.ts.map +1 -0
- package/dist/tools/fetch-url.d.ts +70 -0
- package/dist/tools/fetch-url.d.ts.map +1 -0
- package/dist/tools/fetch-url.js +2 -0
- package/dist/tools/web-search.d.ts +7 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +190 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/{tools-0Kolu2bY.js → tools-BGtJK0vo.js} +1365 -420
- package/dist/tools-BGtJK0vo.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +1 -1
- package/dist/{turn-operations-DkLoiyF4.js → transcript-anchors-BTSZAPVc.js} +147 -2713
- package/dist/transcript-anchors-BTSZAPVc.js.map +1 -0
- package/dist/{transcript-anchors-C8IqWH4x.d.ts → transcript-anchors-DX90kXc4.d.ts} +13 -1299
- package/dist/transcript-anchors-DX90kXc4.d.ts.map +1 -0
- package/dist/tui.d.ts +58 -28
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +1348 -422
- package/dist/tui.js.map +1 -1
- package/dist/turn-operations-CCHfR9eC.js +1938 -0
- package/dist/turn-operations-CCHfR9eC.js.map +1 -0
- package/dist/turn-operations-DDIl4YVk.d.ts +658 -0
- package/dist/turn-operations-DDIl4YVk.d.ts.map +1 -0
- package/dist/{types-oKPBdCmL.js → types-BPw_i5vb.js} +1 -1
- package/dist/types-BPw_i5vb.js.map +1 -0
- package/dist/{types-2PMY5Rlc.d.ts → types-CEAMIUXw.d.ts} +1 -1
- package/dist/types-CEAMIUXw.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.js +3 -3
- package/docs/CHAT.md +53 -6
- package/docs/SKILL.md +3 -0
- package/docs/TUI.md +7 -0
- package/package.json +18 -2
- package/dist/agent-CkJp_ZOR.d.ts.map +0 -1
- package/dist/index-BXQC3I4d.d.ts.map +0 -1
- package/dist/index-CsWckg9p.d.ts.map +0 -1
- package/dist/index-CtIS28mN.d.ts.map +0 -1
- package/dist/login-CY9uShjX.js.map +0 -1
- package/dist/messages-B-tuI2Ur.js.map +0 -1
- package/dist/providers-CRQQDuxx.js.map +0 -1
- package/dist/stats-CIv4j3Sz.js.map +0 -1
- package/dist/stdio-loader-OOOXzUvm.js.map +0 -1
- package/dist/tools-0Kolu2bY.js.map +0 -1
- package/dist/transcript-anchors-C8IqWH4x.d.ts.map +0 -1
- package/dist/turn-operations-DkLoiyF4.js.map +0 -1
- package/dist/types-2PMY5Rlc.d.ts.map +0 -1
- package/dist/types-oKPBdCmL.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { A as replaceDynamicSection } from "./messages-
|
|
6
|
-
import { n as sniffImageMediaType } from "./image-sniff-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { $ as useMcpAuthDispatch, $t as deriveSessionTitle, A as runOAuthLogin, An as buildUpdateHint, At as clampFps, B as refreshMcpToolsCatalog, Bn as AUTO_COMPACT_MIN_GROWTH_FRACTION, Ct as listProjectFiles, D as formatPathForCwd, Dn as tryOpenBrowser, Dt as SETTINGS_CHOICES, En as useCompletion, Et as SETTINGS_CATEGORIES, Ft as resolveTheme, Gt as useDiscovery, H as subscribeMcpToolsCache, Hn as detectAuth, J as parentServerName, Jn as setProviderCredential, Jt as useConfig, K as buildVisibleMcpRows, Kt as useDiscoveryOptional, M as buildMcpServers, Mr as buildBuildSystem, Nr as buildPlanSystem, O as fetchOAuthRedirect, Ot as SETTINGS_TOGGLES, P as discoverProjectMcps, Pr as envSection, Pt as resolveChipColor, Q as McpAuthProvider, Qn as findGitRoot, R as loadMcpToolsCache, T as suggestSafelistEntry, Tt as DEFAULT_SETTINGS, Ut as createDiscoverySlot, Vn as shouldAutoCompact, W as useMcpToolToggleMap, Wt as DiscoveryProvider, Xn as discoverAgentsMd, Y as createFileMcpCredentialStore, Yt as resolveConfig, _ as useSafeModeQueue, _n as ensureKeybindingsFile, _t as hintsLength, a as useSurfaces, ar as accentColor, at as InteractionsProvider, b as getSafelist, bn as keybindingsPath, bt as generateSessionTitle, c as useStreamBuffer, cn as sumRunCosts, ct as createInteractionTools, d as discoverProjectSkills, dn as toolResultText, dr as TODO_STATUS_GLYPHS, dt as pendingInteractionsFromTurns, en as eventsFromTurns, et as useMcpAuthState, fn as updateToolEventOutcomes, g as useSafeModeActions, gn as KEYBINDING_KEY_COL_WIDTH, gt as clipHintsToWidth, h as SafeModeProvider, ht as EMPTY_HINTS, i as useSelectStyle, in as marginTopFor, j as supportsOAuth, jn as useUpdateCheck, jt as useSettings, k as oauthUsesManualCodePaste, kn as bootTick, kt as SettingsProvider, l as buildSkillsConfig, m as writeSessionExport, mn as KEYBINDING_DEFS, mt as useInteractionsQueue, n as ThemeProvider, nn as listSessionMeta, on as serverToolResultSummary, pt as useInteractionsActions, q as indexOfServerRow, qt as ConfigProvider, r as useColors, rt as splitMarkdownCodeBlocks, s as useTheme, sn as stripSpawnTokensLine, st as buildResumedToolResultsTurn, t as computeTurnAnchors, tn as lastContextSizeFromTurns, tt as getMcpAuthStatus, u as defaultSkillScanPaths, un as toolCallPreview, ut as makeRequestInteraction, vn as formatBindingForDisplay, vt as truncateTrailing, wt as useEnabledToggleSet, x as isOnSafelist, xn as matchesBinding, xr as useActiveTodos, xt as buildHints, y as addToSafelist, yn as groupBindings } from "./transcript-anchors-BTSZAPVc.js";
|
|
2
|
+
import { B as enabledModelOptions, E as cleanupPersistedSession, F as OUTPUT_RESERVE_TOKENS, G as modelSupportsReasoning, O as resolvePersistDir, V as getContextWindow, W as modelOptionsFor, X as restoreModelOptions, Y as piIdOf, d as createAgent, k as resolveTasksDir, z as effectiveContextWindow } from "./tools-BGtJK0vo.js";
|
|
3
|
+
import { a as compactPath, c as formatTaskStatus, d as shortId, i as ageString, l as formatTaskSummary, o as fmtTokens, s as formatDuration, u as previewLine } from "./edit-utils-DnfNoj16.js";
|
|
4
|
+
import { l as errorMessage } from "./errors-CoQnKRf1.js";
|
|
5
|
+
import { A as replaceDynamicSection } from "./messages-CvRQTdbR.js";
|
|
6
|
+
import { n as sniffImageMediaType } from "./image-sniff-B7uFSNO1.js";
|
|
7
|
+
import { n as createProcessContext } from "./contexts-BD2U_xpi.js";
|
|
8
|
+
import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-Kqzz-Rs_.js";
|
|
9
|
+
import { r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
|
|
10
|
+
import { a as selectFilesFromSession, b as summaryToTurn, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-BDeqENSe.js";
|
|
11
|
+
import { n as loadSession, t as createSession } from "./session-BzLou2_-.js";
|
|
11
12
|
import { createTuiStore } from "./session/sqlite.js";
|
|
13
|
+
import { B as summarizeOutcomes, C as buildContextualDiff, D as extractEditPayload, F as mergeApprovalAndBodyOutcomes, G as createFilesCompletionProvider, H as createSkillsCompletionProvider, I as parseEditOutcomesFromResult, L as resolveApprovalForPayload, N as buildEditOutcomesAnnotation, O as filetypeFromPath, Q as buildLinearRamp, R as rewriteMultiEditHeader, U as uniqueSkillNamesFromReferences, Z as blendHsl, _ as isEditErrorResult, a as TOOL_DISPLAY, b as selectableTurnIds, c as finalizeStreamingMarkdown, d as turnContextSize, f as splitPromptSegments, g as EDIT_TOOL_NAMES, h as indexOfEntry, i as turnAsText, j as summarizeEditPayload, k as previewEditPayload, l as finalizeStreamingMarkdownForOwner, m as filterModelCatalog, n as deleteTurnSafely, o as displayNameFor, p as buildModelCatalog, r as truncateTurnsAt, s as formatToolCall, v as isTurnHighlighted, w as buildUnifiedDiff, x as turnSelectionOwnership, y as isVisible, z as stripEditOutcomesAnnotation } from "./turn-operations-CCHfR9eC.js";
|
|
12
14
|
import { basename, join, relative } from "node:path";
|
|
13
|
-
import {
|
|
14
|
-
import { spawn } from "node:child_process";
|
|
15
|
+
import { Buffer } from "node:buffer";
|
|
15
16
|
import * as fs from "node:fs";
|
|
16
17
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
17
|
-
import {
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
19
|
+
import { homedir } from "node:os";
|
|
18
20
|
import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
21
|
+
import { Fzf, byLengthAsc } from "fzf";
|
|
19
22
|
import { BoxRenderable, CodeRenderable, RGBA, SyntaxStyle, TextRenderable, addDefaultParsers, createCliRenderer, decodePasteBytes, defaultTextareaKeyBindings, getTreeSitterClient, stripAnsiSequences } from "@opentui/core";
|
|
20
23
|
import { createRoot, useKeyboard, useRenderer, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
|
|
21
24
|
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
22
|
-
import { Fzf, byLengthAsc } from "fzf";
|
|
23
25
|
//#region src/tui/modal.tsx
|
|
24
26
|
const ModalContext = createContext(null);
|
|
25
27
|
function ModalRoot({ children }) {
|
|
@@ -2521,6 +2523,418 @@ function TodoInProgressList({ input, dim }) {
|
|
|
2521
2523
|
});
|
|
2522
2524
|
}
|
|
2523
2525
|
//#endregion
|
|
2526
|
+
//#region src/tui/context-panel.tsx
|
|
2527
|
+
/**
|
|
2528
|
+
* Distinct categorical palette for the bar + swatches — kept in lock-step with
|
|
2529
|
+
* the GUI popover (`CATEGORY_COLORS` in `ChatboxControls.tsx`) so both surfaces
|
|
2530
|
+
* read identically. Fixed hues (not theme tokens) so the same category is the
|
|
2531
|
+
* same color across every theme and across both surfaces. Deferred/free fall
|
|
2532
|
+
* through to muted theme tones via {@link colorFor}.
|
|
2533
|
+
*/
|
|
2534
|
+
const CATEGORY_HEX = {
|
|
2535
|
+
systemPrompt: "#4f9dff",
|
|
2536
|
+
rules: "#22d3ee",
|
|
2537
|
+
skills: "#a855f7",
|
|
2538
|
+
mcpInstructions: "#eab308",
|
|
2539
|
+
subagentDefs: "#ec4899",
|
|
2540
|
+
tools: "#22c55e",
|
|
2541
|
+
mcpTools: "#f59e0b",
|
|
2542
|
+
conversation: "#fb923c"
|
|
2543
|
+
};
|
|
2544
|
+
function colorFor(id, COLOR) {
|
|
2545
|
+
return CATEGORY_HEX[id] ?? COLOR.mute;
|
|
2546
|
+
}
|
|
2547
|
+
/** Percent label — `<1%` for tiny non-zero shares, empty at exactly 0. */
|
|
2548
|
+
function pctLabel(tokens, denom) {
|
|
2549
|
+
if (tokens <= 0 || denom <= 0) return "";
|
|
2550
|
+
const raw = tokens / denom * 100;
|
|
2551
|
+
return raw < 1 ? "<1%" : `${Math.round(raw)}%`;
|
|
2552
|
+
}
|
|
2553
|
+
function ContextPanelModal({ breakdown }) {
|
|
2554
|
+
const COLOR = useColors();
|
|
2555
|
+
const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
|
|
2556
|
+
const expandableIds = useMemo(() => breakdown.categories.filter((c) => c.items && c.items.length > 0).map((c) => c.id), [breakdown]);
|
|
2557
|
+
const [cursor, setCursor] = useState(0);
|
|
2558
|
+
const safeCursor = expandableIds.length === 0 ? 0 : Math.min(cursor, expandableIds.length - 1);
|
|
2559
|
+
useKeyboard((key) => {
|
|
2560
|
+
if (expandableIds.length === 0) return;
|
|
2561
|
+
if (key.name === "up") {
|
|
2562
|
+
setCursor((i) => ((i - 1) % expandableIds.length + expandableIds.length) % expandableIds.length);
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (key.name === "down") {
|
|
2566
|
+
setCursor((i) => (i + 1) % expandableIds.length);
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
if (key.name === "return" || key.name === "space") {
|
|
2570
|
+
const id = expandableIds[safeCursor];
|
|
2571
|
+
setExpanded((prev) => {
|
|
2572
|
+
const next = new Set(prev);
|
|
2573
|
+
if (next.has(id)) next.delete(id);
|
|
2574
|
+
else next.add(id);
|
|
2575
|
+
return next;
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
const denom = breakdown.effectiveWindow || breakdown.used || 1;
|
|
2580
|
+
const live = breakdown.categories.filter((c) => !c.deferred);
|
|
2581
|
+
const deferred = breakdown.categories.filter((c) => c.deferred);
|
|
2582
|
+
const pct = Math.round(breakdown.fraction * 100);
|
|
2583
|
+
const usedColor = pct >= 85 ? COLOR.error : pct >= 60 ? COLOR.warn : COLOR.brand;
|
|
2584
|
+
return /* @__PURE__ */ jsxs(Modal, {
|
|
2585
|
+
title: "context window",
|
|
2586
|
+
maxWidth: 76,
|
|
2587
|
+
children: [
|
|
2588
|
+
/* @__PURE__ */ jsxs("box", {
|
|
2589
|
+
style: {
|
|
2590
|
+
flexDirection: "column",
|
|
2591
|
+
flexShrink: 0
|
|
2592
|
+
},
|
|
2593
|
+
children: [
|
|
2594
|
+
/* @__PURE__ */ jsxs("box", {
|
|
2595
|
+
style: {
|
|
2596
|
+
flexDirection: "row",
|
|
2597
|
+
height: 1,
|
|
2598
|
+
flexShrink: 0
|
|
2599
|
+
},
|
|
2600
|
+
children: [
|
|
2601
|
+
/* @__PURE__ */ jsx("text", {
|
|
2602
|
+
wrapMode: "none",
|
|
2603
|
+
fg: COLOR.mute,
|
|
2604
|
+
children: breakdown.modelId
|
|
2605
|
+
}),
|
|
2606
|
+
/* @__PURE__ */ jsx("box", { style: { flexGrow: 1 } }),
|
|
2607
|
+
/* @__PURE__ */ jsxs("text", {
|
|
2608
|
+
wrapMode: "none",
|
|
2609
|
+
children: [
|
|
2610
|
+
/* @__PURE__ */ jsx("span", {
|
|
2611
|
+
fg: usedColor,
|
|
2612
|
+
children: fmtTokens(breakdown.used)
|
|
2613
|
+
}),
|
|
2614
|
+
/* @__PURE__ */ jsx("span", {
|
|
2615
|
+
fg: COLOR.mute,
|
|
2616
|
+
children: ` / ${fmtTokens(breakdown.effectiveWindow)} `
|
|
2617
|
+
}),
|
|
2618
|
+
/* @__PURE__ */ jsx("span", {
|
|
2619
|
+
fg: usedColor,
|
|
2620
|
+
children: `(${pct}%)`
|
|
2621
|
+
})
|
|
2622
|
+
]
|
|
2623
|
+
})
|
|
2624
|
+
]
|
|
2625
|
+
}),
|
|
2626
|
+
/* @__PURE__ */ jsx("box", {
|
|
2627
|
+
style: {
|
|
2628
|
+
height: 1,
|
|
2629
|
+
flexShrink: 0,
|
|
2630
|
+
marginTop: 1
|
|
2631
|
+
},
|
|
2632
|
+
children: /* @__PURE__ */ jsx(StackedBar, {
|
|
2633
|
+
breakdown,
|
|
2634
|
+
denom,
|
|
2635
|
+
COLOR
|
|
2636
|
+
})
|
|
2637
|
+
}),
|
|
2638
|
+
/* @__PURE__ */ jsx("box", { style: {
|
|
2639
|
+
height: 1,
|
|
2640
|
+
flexShrink: 0
|
|
2641
|
+
} }),
|
|
2642
|
+
live.map((cat) => /* @__PURE__ */ jsx(CategoryRows, {
|
|
2643
|
+
cat,
|
|
2644
|
+
denom,
|
|
2645
|
+
COLOR,
|
|
2646
|
+
isCursor: expandableIds[safeCursor] === cat.id,
|
|
2647
|
+
isExpanded: expanded.has(cat.id)
|
|
2648
|
+
}, cat.id)),
|
|
2649
|
+
deferred.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("box", { style: {
|
|
2650
|
+
height: 1,
|
|
2651
|
+
flexShrink: 0
|
|
2652
|
+
} }), deferred.map((cat) => /* @__PURE__ */ jsx(CategoryRows, {
|
|
2653
|
+
cat,
|
|
2654
|
+
denom,
|
|
2655
|
+
COLOR,
|
|
2656
|
+
isCursor: expandableIds[safeCursor] === cat.id,
|
|
2657
|
+
isExpanded: expanded.has(cat.id)
|
|
2658
|
+
}, cat.id))] }),
|
|
2659
|
+
breakdown.activeSkills && breakdown.activeSkills.length > 0 && /* @__PURE__ */ jsx(ActiveSkillsRow, {
|
|
2660
|
+
skills: breakdown.activeSkills,
|
|
2661
|
+
COLOR
|
|
2662
|
+
}),
|
|
2663
|
+
breakdown.usage && /* @__PURE__ */ jsx(UsageRows, {
|
|
2664
|
+
usage: breakdown.usage,
|
|
2665
|
+
COLOR
|
|
2666
|
+
})
|
|
2667
|
+
]
|
|
2668
|
+
}),
|
|
2669
|
+
breakdown.hasEstimates && /* @__PURE__ */ jsxs("text", {
|
|
2670
|
+
fg: COLOR.dim,
|
|
2671
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2672
|
+
fg: COLOR.mute,
|
|
2673
|
+
children: "~"
|
|
2674
|
+
}), " estimated — exact counts unavailable for this provider"]
|
|
2675
|
+
}),
|
|
2676
|
+
/* @__PURE__ */ jsx("text", {
|
|
2677
|
+
fg: COLOR.dim,
|
|
2678
|
+
children: expandableIds.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2679
|
+
/* @__PURE__ */ jsx("span", {
|
|
2680
|
+
fg: COLOR.warn,
|
|
2681
|
+
children: "↑↓"
|
|
2682
|
+
}),
|
|
2683
|
+
" navigate · ",
|
|
2684
|
+
/* @__PURE__ */ jsx("span", {
|
|
2685
|
+
fg: COLOR.warn,
|
|
2686
|
+
children: "↵/space"
|
|
2687
|
+
}),
|
|
2688
|
+
" expand · ",
|
|
2689
|
+
/* @__PURE__ */ jsx("span", {
|
|
2690
|
+
fg: COLOR.warn,
|
|
2691
|
+
children: "esc"
|
|
2692
|
+
}),
|
|
2693
|
+
" close"
|
|
2694
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
2695
|
+
fg: COLOR.warn,
|
|
2696
|
+
children: "esc"
|
|
2697
|
+
}), " close"] })
|
|
2698
|
+
})
|
|
2699
|
+
]
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Single-row stacked bar that fills the panel width. Layout (left → right):
|
|
2704
|
+
* [ colored category segments ][ free space ][ autocompact buffer ]
|
|
2705
|
+
*
|
|
2706
|
+
* Sizing uses `flexGrow` weights (×1000 of the window fraction) so the bar
|
|
2707
|
+
* stretches to the full container width. Each non-zero colored segment also
|
|
2708
|
+
* carries `minWidth: 1` so tiny shares (system/tools at <1% of a 1M window)
|
|
2709
|
+
* stay visible instead of collapsing into one color. The autocompact buffer is
|
|
2710
|
+
* pinned at the far right — reserved tail headroom, consumed last.
|
|
2711
|
+
*/
|
|
2712
|
+
function StackedBar({ breakdown, denom, COLOR }) {
|
|
2713
|
+
const live = breakdown.categories.filter((c) => !c.deferred && c.tokens > 0);
|
|
2714
|
+
const buffer = breakdown.categories.find((c) => c.id === "autocompactBuffer" && c.tokens > 0);
|
|
2715
|
+
const MIN_WEIGHT = 14;
|
|
2716
|
+
const w = (tokens) => Math.max(MIN_WEIGHT, Math.round(tokens / denom * 1e3));
|
|
2717
|
+
const colored = live.map((c) => ({
|
|
2718
|
+
id: c.id,
|
|
2719
|
+
weight: w(c.tokens)
|
|
2720
|
+
}));
|
|
2721
|
+
const coloredWeight = colored.reduce((a, s) => a + s.weight, 0);
|
|
2722
|
+
const bufferWeight = buffer ? Math.round(buffer.tokens / denom * 1e3) : 0;
|
|
2723
|
+
const freeWeight = Math.max(0, 1e3 - coloredWeight - bufferWeight);
|
|
2724
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
2725
|
+
style: {
|
|
2726
|
+
flexDirection: "row",
|
|
2727
|
+
flexGrow: 1,
|
|
2728
|
+
height: 1
|
|
2729
|
+
},
|
|
2730
|
+
children: [
|
|
2731
|
+
colored.map((s, i) => /* @__PURE__ */ jsx("box", { style: {
|
|
2732
|
+
flexGrow: s.weight,
|
|
2733
|
+
flexBasis: 0,
|
|
2734
|
+
height: 1,
|
|
2735
|
+
backgroundColor: colorFor(s.id, COLOR)
|
|
2736
|
+
} }, `${s.id}-${i}`)),
|
|
2737
|
+
freeWeight > 0 && /* @__PURE__ */ jsx("box", { style: {
|
|
2738
|
+
flexGrow: freeWeight,
|
|
2739
|
+
flexBasis: 0,
|
|
2740
|
+
height: 1,
|
|
2741
|
+
backgroundColor: COLOR.mute
|
|
2742
|
+
} }),
|
|
2743
|
+
bufferWeight > 0 && /* @__PURE__ */ jsx("box", { style: {
|
|
2744
|
+
flexGrow: bufferWeight,
|
|
2745
|
+
flexBasis: 0,
|
|
2746
|
+
height: 1,
|
|
2747
|
+
backgroundColor: COLOR.dim
|
|
2748
|
+
} })
|
|
2749
|
+
]
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Currently-active skills. Informational — an activated skill's body lives in
|
|
2754
|
+
* the conversation (already counted there); this surfaces which are loaded.
|
|
2755
|
+
*/
|
|
2756
|
+
function ActiveSkillsRow({ skills, COLOR }) {
|
|
2757
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2758
|
+
/* @__PURE__ */ jsx("box", { style: {
|
|
2759
|
+
height: 1,
|
|
2760
|
+
flexShrink: 0
|
|
2761
|
+
} }),
|
|
2762
|
+
/* @__PURE__ */ jsx("box", {
|
|
2763
|
+
style: {
|
|
2764
|
+
height: 1,
|
|
2765
|
+
flexShrink: 0
|
|
2766
|
+
},
|
|
2767
|
+
children: /* @__PURE__ */ jsx("text", {
|
|
2768
|
+
wrapMode: "none",
|
|
2769
|
+
fg: COLOR.mute,
|
|
2770
|
+
children: "active skills"
|
|
2771
|
+
})
|
|
2772
|
+
}),
|
|
2773
|
+
skills.map((name) => /* @__PURE__ */ jsx("box", {
|
|
2774
|
+
style: {
|
|
2775
|
+
flexDirection: "row",
|
|
2776
|
+
height: 1,
|
|
2777
|
+
flexShrink: 0
|
|
2778
|
+
},
|
|
2779
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
2780
|
+
wrapMode: "none",
|
|
2781
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2782
|
+
fg: CATEGORY_HEX.skills,
|
|
2783
|
+
children: "✦ "
|
|
2784
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2785
|
+
fg: COLOR.dim,
|
|
2786
|
+
children: name
|
|
2787
|
+
})]
|
|
2788
|
+
})
|
|
2789
|
+
}, name))
|
|
2790
|
+
] });
|
|
2791
|
+
}
|
|
2792
|
+
/**
|
|
2793
|
+
* Exact last-turn token split (cache read / write / fresh input / output).
|
|
2794
|
+
* Informational — sits below the categories, all values exact (no `~`).
|
|
2795
|
+
*/
|
|
2796
|
+
function UsageRows({ usage, COLOR }) {
|
|
2797
|
+
const rows = [
|
|
2798
|
+
{
|
|
2799
|
+
label: "Cached (read)",
|
|
2800
|
+
tokens: usage.cacheRead
|
|
2801
|
+
},
|
|
2802
|
+
{
|
|
2803
|
+
label: "Cache write",
|
|
2804
|
+
tokens: usage.cacheCreation
|
|
2805
|
+
},
|
|
2806
|
+
{
|
|
2807
|
+
label: "Fresh input",
|
|
2808
|
+
tokens: usage.input
|
|
2809
|
+
},
|
|
2810
|
+
{
|
|
2811
|
+
label: "Output",
|
|
2812
|
+
tokens: usage.output
|
|
2813
|
+
}
|
|
2814
|
+
].filter((r) => r.tokens > 0);
|
|
2815
|
+
if (rows.length === 0) return null;
|
|
2816
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2817
|
+
/* @__PURE__ */ jsx("box", { style: {
|
|
2818
|
+
height: 1,
|
|
2819
|
+
flexShrink: 0
|
|
2820
|
+
} }),
|
|
2821
|
+
/* @__PURE__ */ jsx("box", {
|
|
2822
|
+
style: {
|
|
2823
|
+
height: 1,
|
|
2824
|
+
flexShrink: 0
|
|
2825
|
+
},
|
|
2826
|
+
children: /* @__PURE__ */ jsx("text", {
|
|
2827
|
+
wrapMode: "none",
|
|
2828
|
+
fg: COLOR.mute,
|
|
2829
|
+
children: "last turn"
|
|
2830
|
+
})
|
|
2831
|
+
}),
|
|
2832
|
+
rows.map((r) => /* @__PURE__ */ jsxs("box", {
|
|
2833
|
+
style: {
|
|
2834
|
+
flexDirection: "row",
|
|
2835
|
+
height: 1,
|
|
2836
|
+
flexShrink: 0
|
|
2837
|
+
},
|
|
2838
|
+
children: [
|
|
2839
|
+
/* @__PURE__ */ jsx("text", {
|
|
2840
|
+
wrapMode: "none",
|
|
2841
|
+
fg: COLOR.mute,
|
|
2842
|
+
children: r.label
|
|
2843
|
+
}),
|
|
2844
|
+
/* @__PURE__ */ jsx("box", { style: { flexGrow: 1 } }),
|
|
2845
|
+
/* @__PURE__ */ jsx("text", {
|
|
2846
|
+
wrapMode: "none",
|
|
2847
|
+
fg: COLOR.dim,
|
|
2848
|
+
children: fmtTokens(r.tokens)
|
|
2849
|
+
})
|
|
2850
|
+
]
|
|
2851
|
+
}, r.label))
|
|
2852
|
+
] });
|
|
2853
|
+
}
|
|
2854
|
+
function CategoryRows({ cat, denom, COLOR, isCursor, isExpanded }) {
|
|
2855
|
+
const SURFACE = useSurfaces();
|
|
2856
|
+
const pct = pctLabel(cat.tokens, denom);
|
|
2857
|
+
const hasItems = !!cat.items && cat.items.length > 0;
|
|
2858
|
+
const labelColor = isCursor ? COLOR.brand : cat.deferred ? COLOR.mute : COLOR.dim;
|
|
2859
|
+
const marker = hasItems ? isExpanded ? " ▾" : " ▸" : "";
|
|
2860
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
2861
|
+
style: {
|
|
2862
|
+
flexDirection: "column",
|
|
2863
|
+
flexShrink: 0
|
|
2864
|
+
},
|
|
2865
|
+
children: [/* @__PURE__ */ jsxs("box", {
|
|
2866
|
+
style: {
|
|
2867
|
+
flexDirection: "row",
|
|
2868
|
+
height: 1,
|
|
2869
|
+
flexShrink: 0,
|
|
2870
|
+
backgroundColor: isCursor && hasItems ? SURFACE.selection : void 0
|
|
2871
|
+
},
|
|
2872
|
+
children: [
|
|
2873
|
+
/* @__PURE__ */ jsxs("text", {
|
|
2874
|
+
wrapMode: "none",
|
|
2875
|
+
children: [
|
|
2876
|
+
/* @__PURE__ */ jsx("span", {
|
|
2877
|
+
fg: colorFor(cat.id, COLOR),
|
|
2878
|
+
children: "■ "
|
|
2879
|
+
}),
|
|
2880
|
+
/* @__PURE__ */ jsx("span", {
|
|
2881
|
+
fg: labelColor,
|
|
2882
|
+
children: cat.label
|
|
2883
|
+
}),
|
|
2884
|
+
marker ? /* @__PURE__ */ jsx("span", {
|
|
2885
|
+
fg: hasItems && isCursor ? COLOR.brand : COLOR.mute,
|
|
2886
|
+
children: marker
|
|
2887
|
+
}) : null
|
|
2888
|
+
]
|
|
2889
|
+
}),
|
|
2890
|
+
/* @__PURE__ */ jsx("box", { style: { flexGrow: 1 } }),
|
|
2891
|
+
/* @__PURE__ */ jsxs("text", {
|
|
2892
|
+
wrapMode: "none",
|
|
2893
|
+
children: [
|
|
2894
|
+
cat.estimated && /* @__PURE__ */ jsx("span", {
|
|
2895
|
+
fg: COLOR.mute,
|
|
2896
|
+
children: "~"
|
|
2897
|
+
}),
|
|
2898
|
+
/* @__PURE__ */ jsx("span", {
|
|
2899
|
+
fg: cat.deferred ? COLOR.mute : COLOR.dim,
|
|
2900
|
+
children: fmtTokens(cat.tokens)
|
|
2901
|
+
}),
|
|
2902
|
+
pct ? /* @__PURE__ */ jsx("span", {
|
|
2903
|
+
fg: COLOR.mute,
|
|
2904
|
+
children: ` ${pct}`
|
|
2905
|
+
}) : null
|
|
2906
|
+
]
|
|
2907
|
+
})
|
|
2908
|
+
]
|
|
2909
|
+
}), isExpanded && hasItems && cat.items.map((item) => /* @__PURE__ */ jsxs("box", {
|
|
2910
|
+
style: {
|
|
2911
|
+
flexDirection: "row",
|
|
2912
|
+
height: 1,
|
|
2913
|
+
paddingLeft: 2,
|
|
2914
|
+
flexShrink: 0
|
|
2915
|
+
},
|
|
2916
|
+
children: [
|
|
2917
|
+
/* @__PURE__ */ jsx("text", {
|
|
2918
|
+
wrapMode: "none",
|
|
2919
|
+
fg: COLOR.mute,
|
|
2920
|
+
children: item.label
|
|
2921
|
+
}),
|
|
2922
|
+
/* @__PURE__ */ jsx("box", { style: { flexGrow: 1 } }),
|
|
2923
|
+
/* @__PURE__ */ jsxs("text", {
|
|
2924
|
+
wrapMode: "none",
|
|
2925
|
+
children: [item.estimated && /* @__PURE__ */ jsx("span", {
|
|
2926
|
+
fg: COLOR.mute,
|
|
2927
|
+
children: "~"
|
|
2928
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2929
|
+
fg: COLOR.mute,
|
|
2930
|
+
children: fmtTokens(item.tokens)
|
|
2931
|
+
})]
|
|
2932
|
+
})
|
|
2933
|
+
]
|
|
2934
|
+
}, item.id))]
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
//#endregion
|
|
2524
2938
|
//#region src/tui/cwd-picker.tsx
|
|
2525
2939
|
const VISIBLE_ROWS$1 = 12;
|
|
2526
2940
|
const HOME = homedir();
|
|
@@ -2668,8 +3082,7 @@ function CwdPickerModal({ currentCwd, onPick }) {
|
|
|
2668
3082
|
start: 0,
|
|
2669
3083
|
slice: results
|
|
2670
3084
|
};
|
|
2671
|
-
|
|
2672
|
-
let start = Math.max(0, safeIndex - half);
|
|
3085
|
+
let start = Math.max(0, safeIndex - Math.floor(VISIBLE_ROWS$1 / 2));
|
|
2673
3086
|
if (start + VISIBLE_ROWS$1 > results.length) start = results.length - VISIBLE_ROWS$1;
|
|
2674
3087
|
return {
|
|
2675
3088
|
start,
|
|
@@ -3008,200 +3421,8 @@ function readToolsByServer(dataDir) {
|
|
|
3008
3421
|
return out;
|
|
3009
3422
|
}
|
|
3010
3423
|
//#endregion
|
|
3011
|
-
//#region src/tui/
|
|
3012
|
-
|
|
3013
|
-
{
|
|
3014
|
-
id: "off",
|
|
3015
|
-
description: "no reasoning — fastest, smallest output"
|
|
3016
|
-
},
|
|
3017
|
-
{
|
|
3018
|
-
id: "minimal",
|
|
3019
|
-
description: "tiny reasoning budget (gpt-5 family)"
|
|
3020
|
-
},
|
|
3021
|
-
{
|
|
3022
|
-
id: "low",
|
|
3023
|
-
description: "short reasoning pass"
|
|
3024
|
-
},
|
|
3025
|
-
{
|
|
3026
|
-
id: "medium",
|
|
3027
|
-
description: "balanced — sensible default"
|
|
3028
|
-
},
|
|
3029
|
-
{
|
|
3030
|
-
id: "high",
|
|
3031
|
-
description: "deep reasoning — slowest, longest"
|
|
3032
|
-
}
|
|
3033
|
-
];
|
|
3034
|
-
const ADAPTIVE_LEVEL = {
|
|
3035
|
-
id: "adaptive",
|
|
3036
|
-
description: "model decides per-turn (Anthropic)"
|
|
3037
|
-
};
|
|
3038
|
-
function EffortPickerModal({ current, supportsAdaptive, onPick }) {
|
|
3039
|
-
const COLOR = useColors();
|
|
3040
|
-
const SURFACE = useSurfaces();
|
|
3041
|
-
const inputRef = useRef(null);
|
|
3042
|
-
const [query, setQuery] = useState("");
|
|
3043
|
-
const levels = useMemo(() => {
|
|
3044
|
-
return (supportsAdaptive ? [...BASE_LEVELS, ADAPTIVE_LEVEL] : BASE_LEVELS).map((l) => ({
|
|
3045
|
-
...l,
|
|
3046
|
-
searchCorpus: `${l.id} ${l.description}`.toLowerCase()
|
|
3047
|
-
}));
|
|
3048
|
-
}, [supportsAdaptive]);
|
|
3049
|
-
const filtered = useMemo(() => {
|
|
3050
|
-
const trimmed = query.trim().toLowerCase();
|
|
3051
|
-
if (!trimmed) return levels;
|
|
3052
|
-
const terms = trimmed.split(/\s+/);
|
|
3053
|
-
return levels.filter((l) => terms.every((t) => l.searchCorpus.includes(t)));
|
|
3054
|
-
}, [levels, query]);
|
|
3055
|
-
const [selectedIdx, setSelectedIdx] = useState(() => {
|
|
3056
|
-
const idx = levels.findIndex((l) => l.id === current);
|
|
3057
|
-
if (idx >= 0) return idx;
|
|
3058
|
-
const fallback = levels.findIndex((l) => l.id === "medium");
|
|
3059
|
-
return fallback < 0 ? 0 : fallback;
|
|
3060
|
-
});
|
|
3061
|
-
const handleQueryChange = useCallback((next) => {
|
|
3062
|
-
setQuery(next);
|
|
3063
|
-
setSelectedIdx(0);
|
|
3064
|
-
}, []);
|
|
3065
|
-
const safeIndex = filtered.length === 0 ? 0 : Math.min(selectedIdx, filtered.length - 1);
|
|
3066
|
-
const commit = () => {
|
|
3067
|
-
const row = filtered[safeIndex];
|
|
3068
|
-
if (row) onPick(row.id);
|
|
3069
|
-
};
|
|
3070
|
-
useEffect(() => {
|
|
3071
|
-
inputRef.current?.focus();
|
|
3072
|
-
}, []);
|
|
3073
|
-
useKeyboard((key) => {
|
|
3074
|
-
if (key.name === "up") {
|
|
3075
|
-
setSelectedIdx((i) => {
|
|
3076
|
-
if (filtered.length === 0) return i;
|
|
3077
|
-
return ((i - 1) % filtered.length + filtered.length) % filtered.length;
|
|
3078
|
-
});
|
|
3079
|
-
return;
|
|
3080
|
-
}
|
|
3081
|
-
if (key.name === "down") {
|
|
3082
|
-
setSelectedIdx((i) => {
|
|
3083
|
-
if (filtered.length === 0) return i;
|
|
3084
|
-
return (i + 1) % filtered.length;
|
|
3085
|
-
});
|
|
3086
|
-
return;
|
|
3087
|
-
}
|
|
3088
|
-
if (key.name === "return") commit();
|
|
3089
|
-
});
|
|
3090
|
-
return /* @__PURE__ */ jsxs(Modal, {
|
|
3091
|
-
title: "select reasoning effort",
|
|
3092
|
-
maxWidth: 80,
|
|
3093
|
-
children: [
|
|
3094
|
-
/* @__PURE__ */ jsx("box", {
|
|
3095
|
-
style: {
|
|
3096
|
-
border: true,
|
|
3097
|
-
borderColor: COLOR.borderActive,
|
|
3098
|
-
paddingLeft: 1,
|
|
3099
|
-
paddingRight: 1,
|
|
3100
|
-
height: 3
|
|
3101
|
-
},
|
|
3102
|
-
children: /* @__PURE__ */ jsx("input", {
|
|
3103
|
-
ref: inputRef,
|
|
3104
|
-
focused: true,
|
|
3105
|
-
placeholder: "search effort levels…",
|
|
3106
|
-
onInput: handleQueryChange,
|
|
3107
|
-
onSubmit: () => {},
|
|
3108
|
-
style: { flexGrow: 1 }
|
|
3109
|
-
})
|
|
3110
|
-
}),
|
|
3111
|
-
/* @__PURE__ */ jsx("box", {
|
|
3112
|
-
style: {
|
|
3113
|
-
flexDirection: "column",
|
|
3114
|
-
flexShrink: 0
|
|
3115
|
-
},
|
|
3116
|
-
children: filtered.length === 0 ? /* @__PURE__ */ jsxs("text", {
|
|
3117
|
-
fg: COLOR.dim,
|
|
3118
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
3119
|
-
fg: COLOR.mute,
|
|
3120
|
-
children: "no levels match "
|
|
3121
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
3122
|
-
fg: COLOR.warn,
|
|
3123
|
-
children: query.trim()
|
|
3124
|
-
})]
|
|
3125
|
-
}) : filtered.map((level, i) => /* @__PURE__ */ jsx(EffortRow, {
|
|
3126
|
-
level,
|
|
3127
|
-
isCurrent: level.id === current,
|
|
3128
|
-
isFocused: i === safeIndex,
|
|
3129
|
-
highlightBg: SURFACE.selection
|
|
3130
|
-
}, level.id))
|
|
3131
|
-
}),
|
|
3132
|
-
/* @__PURE__ */ jsxs("text", {
|
|
3133
|
-
fg: COLOR.dim,
|
|
3134
|
-
children: [
|
|
3135
|
-
/* @__PURE__ */ jsx("span", {
|
|
3136
|
-
fg: COLOR.warn,
|
|
3137
|
-
children: "↑↓"
|
|
3138
|
-
}),
|
|
3139
|
-
" navigate · ",
|
|
3140
|
-
/* @__PURE__ */ jsx("span", {
|
|
3141
|
-
fg: COLOR.warn,
|
|
3142
|
-
children: "↵"
|
|
3143
|
-
}),
|
|
3144
|
-
" select · ",
|
|
3145
|
-
/* @__PURE__ */ jsx("span", {
|
|
3146
|
-
fg: COLOR.warn,
|
|
3147
|
-
children: "esc"
|
|
3148
|
-
}),
|
|
3149
|
-
" close · ",
|
|
3150
|
-
/* @__PURE__ */ jsx("span", {
|
|
3151
|
-
fg: COLOR.mute,
|
|
3152
|
-
children: `${filtered.length} / ${levels.length} level${levels.length === 1 ? "" : "s"}`
|
|
3153
|
-
})
|
|
3154
|
-
]
|
|
3155
|
-
})
|
|
3156
|
-
]
|
|
3157
|
-
});
|
|
3158
|
-
}
|
|
3159
|
-
/**
|
|
3160
|
-
* Single row in the picker. Mirrors `ModelRow` in `model-picker.tsx`:
|
|
3161
|
-
* `●` marker for the current pick, single-space middle-dot separators,
|
|
3162
|
-
* focused row gets the `surfaces.selection` background lift.
|
|
3163
|
-
*/
|
|
3164
|
-
function EffortRow({ level, isCurrent, isFocused, highlightBg }) {
|
|
3165
|
-
const COLOR = useColors();
|
|
3166
|
-
const marker = isCurrent ? "●" : " ";
|
|
3167
|
-
return /* @__PURE__ */ jsx("box", {
|
|
3168
|
-
style: {
|
|
3169
|
-
height: 1,
|
|
3170
|
-
paddingLeft: 1,
|
|
3171
|
-
paddingRight: 1,
|
|
3172
|
-
flexShrink: 0,
|
|
3173
|
-
backgroundColor: isFocused ? highlightBg : void 0
|
|
3174
|
-
},
|
|
3175
|
-
children: /* @__PURE__ */ jsxs("text", {
|
|
3176
|
-
wrapMode: "none",
|
|
3177
|
-
children: [
|
|
3178
|
-
/* @__PURE__ */ jsx("span", {
|
|
3179
|
-
fg: isCurrent ? COLOR.brand : COLOR.mute,
|
|
3180
|
-
children: marker
|
|
3181
|
-
}),
|
|
3182
|
-
/* @__PURE__ */ jsx("span", {
|
|
3183
|
-
fg: COLOR.mute,
|
|
3184
|
-
children: " "
|
|
3185
|
-
}),
|
|
3186
|
-
/* @__PURE__ */ jsx("span", {
|
|
3187
|
-
fg: isFocused ? COLOR.brand : COLOR.dim,
|
|
3188
|
-
children: level.id
|
|
3189
|
-
}),
|
|
3190
|
-
/* @__PURE__ */ jsx("span", {
|
|
3191
|
-
fg: COLOR.mute,
|
|
3192
|
-
children: " · "
|
|
3193
|
-
}),
|
|
3194
|
-
/* @__PURE__ */ jsx("span", {
|
|
3195
|
-
fg: COLOR.mute,
|
|
3196
|
-
children: level.description
|
|
3197
|
-
})
|
|
3198
|
-
]
|
|
3199
|
-
})
|
|
3200
|
-
});
|
|
3201
|
-
}
|
|
3202
|
-
//#endregion
|
|
3203
|
-
//#region src/tui/keybindings-modal.tsx
|
|
3204
|
-
function KeybindingsModal({ bindings, filePath, onEditFile, onClose }) {
|
|
3424
|
+
//#region src/tui/keybindings-modal.tsx
|
|
3425
|
+
function KeybindingsModal({ bindings, filePath, onEditFile, onClose }) {
|
|
3205
3426
|
const SURFACE = useSurfaces();
|
|
3206
3427
|
const { height: termHeight } = useTerminalDimensions();
|
|
3207
3428
|
const scrollRef = useRef(null);
|
|
@@ -3252,7 +3473,7 @@ function KeybindingsCatalog({ sections }) {
|
|
|
3252
3473
|
flexShrink: 0,
|
|
3253
3474
|
marginTop: sectionIdx === 0 ? 0 : 1
|
|
3254
3475
|
},
|
|
3255
|
-
children: [/* @__PURE__ */ jsx(SectionHeader, { label: section.group }), section.rows.map((row) => /* @__PURE__ */ jsx(BindingRow, {
|
|
3476
|
+
children: [/* @__PURE__ */ jsx(SectionHeader$1, { label: section.group }), section.rows.map((row) => /* @__PURE__ */ jsx(BindingRow, {
|
|
3256
3477
|
def: row.def,
|
|
3257
3478
|
spec: row.spec
|
|
3258
3479
|
}, row.def.action))]
|
|
@@ -3309,7 +3530,7 @@ function KeybindingsEditFileButton({ filePath, highlightBg }) {
|
|
|
3309
3530
|
})]
|
|
3310
3531
|
});
|
|
3311
3532
|
}
|
|
3312
|
-
function SectionHeader({ label }) {
|
|
3533
|
+
function SectionHeader$1({ label }) {
|
|
3313
3534
|
return /* @__PURE__ */ jsx("box", {
|
|
3314
3535
|
style: {
|
|
3315
3536
|
flexShrink: 0,
|
|
@@ -3325,57 +3546,285 @@ function SectionHeader({ label }) {
|
|
|
3325
3546
|
})
|
|
3326
3547
|
});
|
|
3327
3548
|
}
|
|
3328
|
-
function BindingRow({ def, spec }) {
|
|
3549
|
+
function BindingRow({ def, spec }) {
|
|
3550
|
+
const COLOR = useColors();
|
|
3551
|
+
const display = formatBindingForDisplay(spec);
|
|
3552
|
+
const keyText = (display || "—").padEnd(KEYBINDING_KEY_COL_WIDTH, " ");
|
|
3553
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
3554
|
+
style: {
|
|
3555
|
+
flexDirection: "column",
|
|
3556
|
+
flexShrink: 0,
|
|
3557
|
+
paddingLeft: 3,
|
|
3558
|
+
paddingRight: 1
|
|
3559
|
+
},
|
|
3560
|
+
children: [/* @__PURE__ */ jsxs("text", {
|
|
3561
|
+
wrapMode: "none",
|
|
3562
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
3563
|
+
fg: display ? COLOR.warn : COLOR.mute,
|
|
3564
|
+
children: keyText
|
|
3565
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
3566
|
+
fg: COLOR.dim,
|
|
3567
|
+
children: def.label
|
|
3568
|
+
})]
|
|
3569
|
+
}), /* @__PURE__ */ jsx("box", {
|
|
3570
|
+
style: {
|
|
3571
|
+
flexShrink: 0,
|
|
3572
|
+
paddingLeft: KEYBINDING_KEY_COL_WIDTH
|
|
3573
|
+
},
|
|
3574
|
+
children: /* @__PURE__ */ jsx("text", {
|
|
3575
|
+
wrapMode: "word",
|
|
3576
|
+
fg: COLOR.mute,
|
|
3577
|
+
children: def.description
|
|
3578
|
+
})
|
|
3579
|
+
})]
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
function CountsBadge$1({ count }) {
|
|
3583
|
+
const COLOR = useColors();
|
|
3584
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
3585
|
+
wrapMode: "none",
|
|
3586
|
+
children: [
|
|
3587
|
+
/* @__PURE__ */ jsx("span", {
|
|
3588
|
+
fg: COLOR.mute,
|
|
3589
|
+
children: " "
|
|
3590
|
+
}),
|
|
3591
|
+
/* @__PURE__ */ jsx("span", {
|
|
3592
|
+
fg: COLOR.accent,
|
|
3593
|
+
children: String(count)
|
|
3594
|
+
}),
|
|
3595
|
+
/* @__PURE__ */ jsx("span", {
|
|
3596
|
+
fg: COLOR.mute,
|
|
3597
|
+
children: ` action${count === 1 ? "" : "s"} `
|
|
3598
|
+
})
|
|
3599
|
+
]
|
|
3600
|
+
});
|
|
3601
|
+
}
|
|
3602
|
+
//#endregion
|
|
3603
|
+
//#region src/tui/model-options-picker.tsx
|
|
3604
|
+
const BASE_LEVELS$1 = [
|
|
3605
|
+
{
|
|
3606
|
+
id: "off",
|
|
3607
|
+
description: "no reasoning — fastest, smallest output"
|
|
3608
|
+
},
|
|
3609
|
+
{
|
|
3610
|
+
id: "minimal",
|
|
3611
|
+
description: "tiny reasoning budget (gpt-5 family)"
|
|
3612
|
+
},
|
|
3613
|
+
{
|
|
3614
|
+
id: "low",
|
|
3615
|
+
description: "short reasoning pass"
|
|
3616
|
+
},
|
|
3617
|
+
{
|
|
3618
|
+
id: "medium",
|
|
3619
|
+
description: "balanced — sensible default"
|
|
3620
|
+
},
|
|
3621
|
+
{
|
|
3622
|
+
id: "high",
|
|
3623
|
+
description: "deep reasoning — slowest, longest"
|
|
3624
|
+
}
|
|
3625
|
+
];
|
|
3626
|
+
const ADAPTIVE_LEVEL$1 = {
|
|
3627
|
+
id: "adaptive",
|
|
3628
|
+
description: "model decides per-turn (Anthropic)"
|
|
3629
|
+
};
|
|
3630
|
+
function ModelOptionsPickerModal({ supportsReasoning, supportsAdaptive, currentEffort, options, enabled, onPickEffort, onToggleOption }) {
|
|
3631
|
+
const COLOR = useColors();
|
|
3632
|
+
const SURFACE = useSurfaces();
|
|
3633
|
+
const [selectedIdx, setSelectedIdx] = useState(0);
|
|
3634
|
+
const [localEnabled, setLocalEnabled] = useState(() => ({ ...enabled }));
|
|
3635
|
+
const handleToggle = (optionId) => {
|
|
3636
|
+
setLocalEnabled((prev) => ({
|
|
3637
|
+
...prev,
|
|
3638
|
+
[optionId]: !prev[optionId]
|
|
3639
|
+
}));
|
|
3640
|
+
onToggleOption(optionId);
|
|
3641
|
+
};
|
|
3642
|
+
const rows = useMemo(() => {
|
|
3643
|
+
const out = [];
|
|
3644
|
+
if (supportsReasoning) {
|
|
3645
|
+
const levels = supportsAdaptive ? [...BASE_LEVELS$1, ADAPTIVE_LEVEL$1] : BASE_LEVELS$1;
|
|
3646
|
+
for (const level of levels) out.push({
|
|
3647
|
+
kind: "effort",
|
|
3648
|
+
level
|
|
3649
|
+
});
|
|
3650
|
+
}
|
|
3651
|
+
for (const option of options) out.push({
|
|
3652
|
+
kind: "option",
|
|
3653
|
+
option
|
|
3654
|
+
});
|
|
3655
|
+
return out;
|
|
3656
|
+
}, [
|
|
3657
|
+
supportsReasoning,
|
|
3658
|
+
supportsAdaptive,
|
|
3659
|
+
options
|
|
3660
|
+
]);
|
|
3661
|
+
const safeIndex = rows.length === 0 ? 0 : Math.min(selectedIdx, rows.length - 1);
|
|
3662
|
+
const firstOptionIdx = rows.findIndex((r) => r.kind === "option");
|
|
3663
|
+
useKeyboard((key) => {
|
|
3664
|
+
if (key.name === "up") {
|
|
3665
|
+
setSelectedIdx((i) => rows.length === 0 ? i : ((i - 1) % rows.length + rows.length) % rows.length);
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
if (key.name === "down") {
|
|
3669
|
+
setSelectedIdx((i) => rows.length === 0 ? i : (i + 1) % rows.length);
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
if (key.name === "return" || key.name === "space") {
|
|
3673
|
+
const row = rows[safeIndex];
|
|
3674
|
+
if (!row) return;
|
|
3675
|
+
if (row.kind === "effort") onPickEffort(row.level.id);
|
|
3676
|
+
else handleToggle(row.option.id);
|
|
3677
|
+
}
|
|
3678
|
+
});
|
|
3679
|
+
return /* @__PURE__ */ jsxs(Modal, {
|
|
3680
|
+
title: "model options",
|
|
3681
|
+
maxWidth: 80,
|
|
3682
|
+
children: [/* @__PURE__ */ jsxs("box", {
|
|
3683
|
+
style: {
|
|
3684
|
+
flexDirection: "column",
|
|
3685
|
+
flexShrink: 0
|
|
3686
|
+
},
|
|
3687
|
+
children: [supportsReasoning && /* @__PURE__ */ jsx(SectionHeader, {
|
|
3688
|
+
label: "reasoning effort",
|
|
3689
|
+
color: COLOR.mute
|
|
3690
|
+
}), rows.map((row, i) => {
|
|
3691
|
+
const isFocused = i === safeIndex;
|
|
3692
|
+
if (row.kind === "effort") return /* @__PURE__ */ jsx(EffortRow$1, {
|
|
3693
|
+
level: row.level,
|
|
3694
|
+
isCurrent: row.level.id === currentEffort,
|
|
3695
|
+
isFocused,
|
|
3696
|
+
highlightBg: SURFACE.selection
|
|
3697
|
+
}, `effort:${row.level.id}`);
|
|
3698
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
3699
|
+
style: {
|
|
3700
|
+
flexDirection: "column",
|
|
3701
|
+
flexShrink: 0
|
|
3702
|
+
},
|
|
3703
|
+
children: [i === firstOptionIdx && /* @__PURE__ */ jsxs(Fragment, { children: [supportsReasoning && /* @__PURE__ */ jsx("box", { style: {
|
|
3704
|
+
height: 1,
|
|
3705
|
+
flexShrink: 0
|
|
3706
|
+
} }), /* @__PURE__ */ jsx(SectionHeader, {
|
|
3707
|
+
label: "features",
|
|
3708
|
+
color: COLOR.mute
|
|
3709
|
+
})] }), /* @__PURE__ */ jsx(OptionRow, {
|
|
3710
|
+
option: row.option,
|
|
3711
|
+
isEnabled: localEnabled[row.option.id] === true,
|
|
3712
|
+
isFocused,
|
|
3713
|
+
highlightBg: SURFACE.selection
|
|
3714
|
+
})]
|
|
3715
|
+
}, `option:${row.option.id}`);
|
|
3716
|
+
})]
|
|
3717
|
+
}), /* @__PURE__ */ jsxs("text", {
|
|
3718
|
+
fg: COLOR.dim,
|
|
3719
|
+
children: [
|
|
3720
|
+
/* @__PURE__ */ jsx("span", {
|
|
3721
|
+
fg: COLOR.warn,
|
|
3722
|
+
children: "↑↓"
|
|
3723
|
+
}),
|
|
3724
|
+
" navigate · ",
|
|
3725
|
+
/* @__PURE__ */ jsx("span", {
|
|
3726
|
+
fg: COLOR.warn,
|
|
3727
|
+
children: "↵/space"
|
|
3728
|
+
}),
|
|
3729
|
+
" select/toggle · ",
|
|
3730
|
+
/* @__PURE__ */ jsx("span", {
|
|
3731
|
+
fg: COLOR.warn,
|
|
3732
|
+
children: "esc"
|
|
3733
|
+
}),
|
|
3734
|
+
" close"
|
|
3735
|
+
]
|
|
3736
|
+
})]
|
|
3737
|
+
});
|
|
3738
|
+
}
|
|
3739
|
+
function SectionHeader({ label, color }) {
|
|
3740
|
+
return /* @__PURE__ */ jsx("box", {
|
|
3741
|
+
style: {
|
|
3742
|
+
height: 1,
|
|
3743
|
+
paddingLeft: 1,
|
|
3744
|
+
flexShrink: 0
|
|
3745
|
+
},
|
|
3746
|
+
children: /* @__PURE__ */ jsx("text", {
|
|
3747
|
+
wrapMode: "none",
|
|
3748
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3749
|
+
fg: color,
|
|
3750
|
+
children: label
|
|
3751
|
+
})
|
|
3752
|
+
})
|
|
3753
|
+
});
|
|
3754
|
+
}
|
|
3755
|
+
function EffortRow$1({ level, isCurrent, isFocused, highlightBg }) {
|
|
3756
|
+
const COLOR = useColors();
|
|
3757
|
+
const marker = isCurrent ? "●" : " ";
|
|
3758
|
+
return /* @__PURE__ */ jsx("box", {
|
|
3759
|
+
style: {
|
|
3760
|
+
height: 1,
|
|
3761
|
+
paddingLeft: 1,
|
|
3762
|
+
paddingRight: 1,
|
|
3763
|
+
flexShrink: 0,
|
|
3764
|
+
backgroundColor: isFocused ? highlightBg : void 0
|
|
3765
|
+
},
|
|
3766
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
3767
|
+
wrapMode: "none",
|
|
3768
|
+
children: [
|
|
3769
|
+
/* @__PURE__ */ jsx("span", {
|
|
3770
|
+
fg: isCurrent ? COLOR.brand : COLOR.mute,
|
|
3771
|
+
children: marker
|
|
3772
|
+
}),
|
|
3773
|
+
/* @__PURE__ */ jsx("span", {
|
|
3774
|
+
fg: COLOR.mute,
|
|
3775
|
+
children: " "
|
|
3776
|
+
}),
|
|
3777
|
+
/* @__PURE__ */ jsx("span", {
|
|
3778
|
+
fg: isFocused ? COLOR.brand : COLOR.dim,
|
|
3779
|
+
children: level.id
|
|
3780
|
+
}),
|
|
3781
|
+
/* @__PURE__ */ jsx("span", {
|
|
3782
|
+
fg: COLOR.mute,
|
|
3783
|
+
children: " · "
|
|
3784
|
+
}),
|
|
3785
|
+
/* @__PURE__ */ jsx("span", {
|
|
3786
|
+
fg: COLOR.mute,
|
|
3787
|
+
children: level.description
|
|
3788
|
+
})
|
|
3789
|
+
]
|
|
3790
|
+
})
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
function OptionRow({ option, isEnabled, isFocused, highlightBg }) {
|
|
3329
3794
|
const COLOR = useColors();
|
|
3330
|
-
const
|
|
3331
|
-
|
|
3332
|
-
return /* @__PURE__ */ jsxs("box", {
|
|
3795
|
+
const marker = isEnabled ? "[x]" : "[ ]";
|
|
3796
|
+
return /* @__PURE__ */ jsx("box", {
|
|
3333
3797
|
style: {
|
|
3334
|
-
|
|
3798
|
+
height: 1,
|
|
3799
|
+
paddingLeft: 1,
|
|
3800
|
+
paddingRight: 1,
|
|
3335
3801
|
flexShrink: 0,
|
|
3336
|
-
|
|
3337
|
-
paddingRight: 1
|
|
3802
|
+
backgroundColor: isFocused ? highlightBg : void 0
|
|
3338
3803
|
},
|
|
3339
|
-
children:
|
|
3804
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
3340
3805
|
wrapMode: "none",
|
|
3341
|
-
children: [
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
}
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
return /* @__PURE__ */ jsxs("text", {
|
|
3364
|
-
wrapMode: "none",
|
|
3365
|
-
children: [
|
|
3366
|
-
/* @__PURE__ */ jsx("span", {
|
|
3367
|
-
fg: COLOR.mute,
|
|
3368
|
-
children: " "
|
|
3369
|
-
}),
|
|
3370
|
-
/* @__PURE__ */ jsx("span", {
|
|
3371
|
-
fg: COLOR.accent,
|
|
3372
|
-
children: String(count)
|
|
3373
|
-
}),
|
|
3374
|
-
/* @__PURE__ */ jsx("span", {
|
|
3375
|
-
fg: COLOR.mute,
|
|
3376
|
-
children: ` action${count === 1 ? "" : "s"} `
|
|
3377
|
-
})
|
|
3378
|
-
]
|
|
3806
|
+
children: [
|
|
3807
|
+
/* @__PURE__ */ jsx("span", {
|
|
3808
|
+
fg: isEnabled ? COLOR.brand : COLOR.mute,
|
|
3809
|
+
children: marker
|
|
3810
|
+
}),
|
|
3811
|
+
/* @__PURE__ */ jsx("span", {
|
|
3812
|
+
fg: COLOR.mute,
|
|
3813
|
+
children: " "
|
|
3814
|
+
}),
|
|
3815
|
+
/* @__PURE__ */ jsx("span", {
|
|
3816
|
+
fg: isFocused ? COLOR.brand : COLOR.dim,
|
|
3817
|
+
children: option.label
|
|
3818
|
+
}),
|
|
3819
|
+
option.description ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
3820
|
+
fg: COLOR.mute,
|
|
3821
|
+
children: " · "
|
|
3822
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
3823
|
+
fg: COLOR.mute,
|
|
3824
|
+
children: option.description
|
|
3825
|
+
})] }) : null
|
|
3826
|
+
]
|
|
3827
|
+
})
|
|
3379
3828
|
});
|
|
3380
3829
|
}
|
|
3381
3830
|
//#endregion
|
|
@@ -3443,8 +3892,7 @@ function ModelPickerModal({ providers, modelsFor, current, onPick }) {
|
|
|
3443
3892
|
start: 0,
|
|
3444
3893
|
slice: filtered
|
|
3445
3894
|
};
|
|
3446
|
-
|
|
3447
|
-
let start = Math.max(0, safeIndex - half);
|
|
3895
|
+
let start = Math.max(0, safeIndex - Math.floor(VISIBLE_ROWS / 2));
|
|
3448
3896
|
if (start + VISIBLE_ROWS > filtered.length) start = filtered.length - VISIBLE_ROWS;
|
|
3449
3897
|
return {
|
|
3450
3898
|
start,
|
|
@@ -4526,23 +4974,25 @@ function snippet(s) {
|
|
|
4526
4974
|
}
|
|
4527
4975
|
//#endregion
|
|
4528
4976
|
//#region src/tui/interaction-block.tsx
|
|
4529
|
-
const COMMENT_TEXTAREA_BINDINGS =
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4977
|
+
const COMMENT_TEXTAREA_BINDINGS = (() => {
|
|
4978
|
+
return [
|
|
4979
|
+
...defaultTextareaKeyBindings.filter((b) => b.name !== "return" && !(b.name === "a" && b.ctrl && !b.shift && !b.meta)),
|
|
4980
|
+
{
|
|
4981
|
+
name: "a",
|
|
4982
|
+
ctrl: true,
|
|
4983
|
+
action: "select-all"
|
|
4984
|
+
},
|
|
4985
|
+
{
|
|
4986
|
+
name: "return",
|
|
4987
|
+
action: "submit"
|
|
4988
|
+
},
|
|
4989
|
+
{
|
|
4990
|
+
name: "return",
|
|
4991
|
+
shift: true,
|
|
4992
|
+
action: "newline"
|
|
4993
|
+
}
|
|
4994
|
+
];
|
|
4995
|
+
})();
|
|
4546
4996
|
/**
|
|
4547
4997
|
* InteractionBlock — picker UI for `present_plan` and `ask_user` tool calls.
|
|
4548
4998
|
*
|
|
@@ -5616,13 +6066,7 @@ const API_KEY_INPUT_BINDINGS = makeSubmitBindings(false);
|
|
|
5616
6066
|
function findByKey(items, value) {
|
|
5617
6067
|
return typeof value === "string" ? items.find((i) => i.key === value) : void 0;
|
|
5618
6068
|
}
|
|
5619
|
-
|
|
5620
|
-
* Sentinel value used by the picker's "+ add / re-configure" option. Lives
|
|
5621
|
-
* outside the provider key namespace (key strings are at least one char, no
|
|
5622
|
-
* leading `__`) so we can't collide with a real registry entry.
|
|
5623
|
-
*/
|
|
5624
|
-
const WIZARD_OPTION_VALUE = "__wizard__";
|
|
5625
|
-
function AuthScreen({ onPick }) {
|
|
6069
|
+
function AuthScreen({ onPick, initialConfigureKey }) {
|
|
5626
6070
|
const config = useConfig();
|
|
5627
6071
|
const { providers: registry } = config;
|
|
5628
6072
|
const focused = useModalAwareFocus();
|
|
@@ -5633,30 +6077,48 @@ function AuthScreen({ onPick }) {
|
|
|
5633
6077
|
useEffect(() => {
|
|
5634
6078
|
refresh();
|
|
5635
6079
|
}, [refresh]);
|
|
5636
|
-
const [
|
|
6080
|
+
const [configuring, setConfiguring] = useState(() => initialConfigureKey ? registry[initialConfigureKey] ?? null : null);
|
|
5637
6081
|
const available = useMemo(() => providers.filter((p) => p.available), [providers]);
|
|
5638
|
-
const onWizardDone = useCallback(() => {
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
6082
|
+
const onWizardDone = useCallback((descriptorKey) => {
|
|
6083
|
+
setConfiguring(null);
|
|
6084
|
+
const detected = detectAuth(config.paths.userDir, registry);
|
|
6085
|
+
setProviders(detected);
|
|
6086
|
+
const configured = detected.find((p) => p.key === descriptorKey);
|
|
6087
|
+
if (configured?.available) onPick(configured);
|
|
6088
|
+
}, [
|
|
6089
|
+
config.paths.userDir,
|
|
6090
|
+
registry,
|
|
6091
|
+
onPick
|
|
6092
|
+
]);
|
|
6093
|
+
const onSelectProvider = useCallback((p) => {
|
|
6094
|
+
if (p.available) {
|
|
6095
|
+
onPick(p);
|
|
6096
|
+
return;
|
|
6097
|
+
}
|
|
6098
|
+
const descriptor = registry[p.key];
|
|
6099
|
+
if (descriptor) setConfiguring(descriptor);
|
|
6100
|
+
}, [registry, onPick]);
|
|
6101
|
+
if (configuring) return /* @__PURE__ */ jsx(SetupWizard, {
|
|
6102
|
+
registry,
|
|
6103
|
+
dataDir: config.paths.userDir,
|
|
6104
|
+
initialDescriptor: configuring,
|
|
6105
|
+
onConfigured: onWizardDone,
|
|
6106
|
+
onCancel: available.length > 0 ? () => setConfiguring(null) : void 0
|
|
6107
|
+
});
|
|
6108
|
+
if (available.length === 0) return /* @__PURE__ */ jsx(SetupWizard, {
|
|
6109
|
+
registry,
|
|
6110
|
+
dataDir: config.paths.userDir,
|
|
6111
|
+
onConfigured: onWizardDone
|
|
6112
|
+
});
|
|
5651
6113
|
const options = [...available.map((p) => ({
|
|
5652
6114
|
name: p.label,
|
|
5653
6115
|
description: p.methods.map((m) => m.detail).join(" · "),
|
|
5654
6116
|
value: p.key
|
|
5655
|
-
})), {
|
|
5656
|
-
name:
|
|
5657
|
-
description: "
|
|
5658
|
-
value:
|
|
5659
|
-
}];
|
|
6117
|
+
})), ...providers.filter((p) => !p.available).map((p) => ({
|
|
6118
|
+
name: p.label,
|
|
6119
|
+
description: "not configured — press ↵ to set up",
|
|
6120
|
+
value: p.key
|
|
6121
|
+
}))];
|
|
5660
6122
|
return /* @__PURE__ */ jsxs("box", {
|
|
5661
6123
|
style: {
|
|
5662
6124
|
flexDirection: "column",
|
|
@@ -5677,57 +6139,114 @@ function AuthScreen({ onPick }) {
|
|
|
5677
6139
|
wrapSelection: true,
|
|
5678
6140
|
onSelect: (_idx, option) => {
|
|
5679
6141
|
if (!option) return;
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
return;
|
|
5683
|
-
}
|
|
5684
|
-
const provider = findByKey(available, option.value);
|
|
5685
|
-
if (provider) onPick(provider);
|
|
6142
|
+
const provider = findByKey(providers, option.value);
|
|
6143
|
+
if (provider) onSelectProvider(provider);
|
|
5686
6144
|
},
|
|
5687
6145
|
style: { flexGrow: 1 }
|
|
5688
6146
|
})
|
|
5689
6147
|
}), /* @__PURE__ */ jsx(TitleOverlay, { title: "pick a provider" })]
|
|
5690
6148
|
});
|
|
5691
6149
|
}
|
|
5692
|
-
|
|
5693
|
-
|
|
6150
|
+
/**
|
|
6151
|
+
* First credential step for a chosen provider. The method picker only earns
|
|
6152
|
+
* its place when there's a genuine choice — i.e. the provider supports OAuth
|
|
6153
|
+
* in addition to an API key. For API-key-only providers (e.g. `local`,
|
|
6154
|
+
* Cerebras), it's a single-option screen, so we skip it and go straight to the
|
|
6155
|
+
* customFields form / API-key input.
|
|
6156
|
+
*/
|
|
6157
|
+
function firstCredentialStep(descriptor) {
|
|
6158
|
+
if (supportsOAuth(descriptor)) return {
|
|
6159
|
+
kind: "pick-method",
|
|
6160
|
+
descriptor
|
|
6161
|
+
};
|
|
6162
|
+
if (descriptor.customFields && descriptor.customFields.length > 0) return {
|
|
6163
|
+
kind: "enter-custom-fields",
|
|
6164
|
+
descriptor
|
|
6165
|
+
};
|
|
6166
|
+
return {
|
|
6167
|
+
kind: "enter-apikey",
|
|
6168
|
+
descriptor,
|
|
6169
|
+
customFields: {}
|
|
6170
|
+
};
|
|
6171
|
+
}
|
|
6172
|
+
function SetupWizard({ registry, dataDir, initialDescriptor, onConfigured, onCancel }) {
|
|
6173
|
+
const [step, setStep] = useState(initialDescriptor ? firstCredentialStep(initialDescriptor) : { kind: "pick-provider" });
|
|
5694
6174
|
const [error, setError] = useState(null);
|
|
5695
6175
|
const descriptors = useMemo(() => Object.values(registry), [registry]);
|
|
5696
6176
|
const onPickProvider = useCallback((descriptor) => {
|
|
5697
6177
|
setError(null);
|
|
5698
|
-
setStep(
|
|
5699
|
-
kind: "pick-method",
|
|
5700
|
-
descriptor
|
|
5701
|
-
});
|
|
6178
|
+
setStep(firstCredentialStep(descriptor));
|
|
5702
6179
|
}, []);
|
|
5703
6180
|
const onPickMethod = useCallback((descriptor, method) => {
|
|
5704
6181
|
setError(null);
|
|
5705
|
-
if (method === "apikey") setStep({
|
|
5706
|
-
kind: "enter-
|
|
6182
|
+
if (method === "apikey") if (descriptor.customFields && descriptor.customFields.length > 0) setStep({
|
|
6183
|
+
kind: "enter-custom-fields",
|
|
5707
6184
|
descriptor
|
|
5708
6185
|
});
|
|
6186
|
+
else setStep({
|
|
6187
|
+
kind: "enter-apikey",
|
|
6188
|
+
descriptor,
|
|
6189
|
+
customFields: {}
|
|
6190
|
+
});
|
|
5709
6191
|
else setStep({
|
|
5710
6192
|
kind: "oauth-running",
|
|
5711
6193
|
descriptor
|
|
5712
6194
|
});
|
|
5713
6195
|
}, []);
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
6196
|
+
/**
|
|
6197
|
+
* Single write path for any wizard outcome that produces an apikey credential.
|
|
6198
|
+
* Centralizes credential persistence + env-var mirroring so the customFields,
|
|
6199
|
+
* API-key, and customFields-only flows can't drift out of sync.
|
|
6200
|
+
*
|
|
6201
|
+
* Env mirroring stays in lockstep with `applyApiKeyEnv`: anything we store
|
|
6202
|
+
* here is what that function would put back on the next launch, so the
|
|
6203
|
+
* in-process env reflects the on-disk truth.
|
|
6204
|
+
*/
|
|
6205
|
+
const saveApiKeyCredential = useCallback((descriptor, apiKey, customFields) => {
|
|
5720
6206
|
try {
|
|
5721
6207
|
setProviderCredential(dataDir, descriptor, {
|
|
5722
6208
|
kind: "apikey",
|
|
5723
|
-
value:
|
|
6209
|
+
value: apiKey,
|
|
6210
|
+
...Object.keys(customFields).length > 0 ? { customFields } : {}
|
|
5724
6211
|
});
|
|
5725
|
-
if (descriptor.envKey) process.env[descriptor.envKey] =
|
|
5726
|
-
|
|
6212
|
+
if (descriptor.envKey && apiKey) process.env[descriptor.envKey] = apiKey;
|
|
6213
|
+
for (const field of descriptor.customFields ?? []) {
|
|
6214
|
+
const v = customFields[field.key];
|
|
6215
|
+
if (typeof v === "string" && v.length > 0) process.env[field.envVar] = v;
|
|
6216
|
+
}
|
|
6217
|
+
onConfigured(descriptor.key);
|
|
5727
6218
|
} catch (err) {
|
|
5728
6219
|
setError(errorMessage(err));
|
|
5729
6220
|
}
|
|
5730
6221
|
}, [dataDir, onConfigured]);
|
|
6222
|
+
const onCustomFieldsSubmit = useCallback((descriptor, values) => {
|
|
6223
|
+
const fields = descriptor.customFields ?? [];
|
|
6224
|
+
const missing = fields.find((f) => f.required && !values[f.key]?.trim());
|
|
6225
|
+
if (missing) {
|
|
6226
|
+
setError(`${missing.label} is required.`);
|
|
6227
|
+
return;
|
|
6228
|
+
}
|
|
6229
|
+
setError(null);
|
|
6230
|
+
const collected = {};
|
|
6231
|
+
for (const field of fields) {
|
|
6232
|
+
const v = values[field.key]?.trim();
|
|
6233
|
+
if (v) collected[field.key] = v;
|
|
6234
|
+
}
|
|
6235
|
+
if (descriptor.envKey) setStep({
|
|
6236
|
+
kind: "enter-apikey",
|
|
6237
|
+
descriptor,
|
|
6238
|
+
customFields: collected
|
|
6239
|
+
});
|
|
6240
|
+
else saveApiKeyCredential(descriptor, "", collected);
|
|
6241
|
+
}, [saveApiKeyCredential]);
|
|
6242
|
+
const onApiKeySubmit = useCallback((descriptor, customFields, value) => {
|
|
6243
|
+
const trimmed = value.trim();
|
|
6244
|
+
if (!trimmed) {
|
|
6245
|
+
setError("API key cannot be empty.");
|
|
6246
|
+
return;
|
|
6247
|
+
}
|
|
6248
|
+
saveApiKeyCredential(descriptor, trimmed, customFields);
|
|
6249
|
+
}, [saveApiKeyCredential]);
|
|
5731
6250
|
const onOAuthError = useCallback((msg) => {
|
|
5732
6251
|
setError(msg);
|
|
5733
6252
|
setStep((prev) => prev.kind === "oauth-running" ? {
|
|
@@ -5735,6 +6254,12 @@ function SetupWizard({ registry, dataDir, onConfigured, onCancel }) {
|
|
|
5735
6254
|
descriptor: prev.descriptor
|
|
5736
6255
|
} : prev);
|
|
5737
6256
|
}, []);
|
|
6257
|
+
const oauthDescriptorRef = useRef(null);
|
|
6258
|
+
oauthDescriptorRef.current = step.kind === "oauth-running" ? step.descriptor : oauthDescriptorRef.current;
|
|
6259
|
+
const onOAuthSuccess = useCallback(() => {
|
|
6260
|
+
const key = oauthDescriptorRef.current?.key;
|
|
6261
|
+
if (key) onConfigured(key);
|
|
6262
|
+
}, [onConfigured]);
|
|
5738
6263
|
if (descriptors.length === 0) return /* @__PURE__ */ jsx(EmptyRegistryNotice, {});
|
|
5739
6264
|
if (step.kind === "pick-provider") return /* @__PURE__ */ jsx(PickProviderStep, {
|
|
5740
6265
|
descriptors,
|
|
@@ -5747,15 +6272,21 @@ function SetupWizard({ registry, dataDir, onConfigured, onCancel }) {
|
|
|
5747
6272
|
error,
|
|
5748
6273
|
onPick: onPickMethod
|
|
5749
6274
|
});
|
|
6275
|
+
if (step.kind === "enter-custom-fields") return /* @__PURE__ */ jsx(EnterCustomFieldsStep, {
|
|
6276
|
+
descriptor: step.descriptor,
|
|
6277
|
+
error,
|
|
6278
|
+
onSubmit: onCustomFieldsSubmit
|
|
6279
|
+
});
|
|
5750
6280
|
if (step.kind === "enter-apikey") return /* @__PURE__ */ jsx(EnterApiKeyStep, {
|
|
5751
6281
|
descriptor: step.descriptor,
|
|
6282
|
+
customFields: step.customFields,
|
|
5752
6283
|
error,
|
|
5753
6284
|
onSubmit: onApiKeySubmit
|
|
5754
6285
|
});
|
|
5755
6286
|
return /* @__PURE__ */ jsx(OAuthRunningStep, {
|
|
5756
6287
|
descriptor: step.descriptor,
|
|
5757
6288
|
dataDir,
|
|
5758
|
-
onSuccess:
|
|
6289
|
+
onSuccess: onOAuthSuccess,
|
|
5759
6290
|
onError: onOAuthError
|
|
5760
6291
|
});
|
|
5761
6292
|
}
|
|
@@ -5803,6 +6334,15 @@ function WizardEscHint() {
|
|
|
5803
6334
|
children: "esc to exit"
|
|
5804
6335
|
});
|
|
5805
6336
|
}
|
|
6337
|
+
/**
|
|
6338
|
+
* Defensive: older descriptor hints ended with a dangling "… Press" (the
|
|
6339
|
+
* one-field-at-a-time wizard appended " enter to continue"). The multi-field
|
|
6340
|
+
* form renders the hint standalone, so trim a trailing "Press" / "press"
|
|
6341
|
+
* (with optional period) to avoid a hanging word. No-op for clean hints.
|
|
6342
|
+
*/
|
|
6343
|
+
function stripTrailingPressHint(hint) {
|
|
6344
|
+
return hint.replace(/\s*press\.?\s*$/i, "");
|
|
6345
|
+
}
|
|
5806
6346
|
function EmptyRegistryNotice() {
|
|
5807
6347
|
const COLOR = useColors();
|
|
5808
6348
|
return /* @__PURE__ */ jsxs(WizardPanel, {
|
|
@@ -5882,9 +6422,23 @@ function PickMethodStep({ descriptor, error, onPick }) {
|
|
|
5882
6422
|
const focused = useModalAwareFocus();
|
|
5883
6423
|
const SELECT_THEME = useSelectStyle();
|
|
5884
6424
|
const options = useMemo(() => {
|
|
6425
|
+
const hasCustomFields = (descriptor.customFields?.length ?? 0) > 0;
|
|
6426
|
+
const hasEnvKey = !!descriptor.envKey;
|
|
6427
|
+
let apikeyName;
|
|
6428
|
+
let apikeyDescription;
|
|
6429
|
+
if (!hasEnvKey && hasCustomFields) {
|
|
6430
|
+
apikeyName = "Configure";
|
|
6431
|
+
apikeyDescription = `configure ${descriptor.label} (no API key required)`;
|
|
6432
|
+
} else if (hasEnvKey && hasCustomFields) {
|
|
6433
|
+
apikeyName = "API key";
|
|
6434
|
+
apikeyDescription = `configure & paste your ${descriptor.label} API key`;
|
|
6435
|
+
} else {
|
|
6436
|
+
apikeyName = "API key";
|
|
6437
|
+
apikeyDescription = `paste your ${descriptor.label} API key`;
|
|
6438
|
+
}
|
|
5885
6439
|
const items = [{
|
|
5886
|
-
name:
|
|
5887
|
-
description:
|
|
6440
|
+
name: apikeyName,
|
|
6441
|
+
description: apikeyDescription,
|
|
5888
6442
|
value: "apikey"
|
|
5889
6443
|
}];
|
|
5890
6444
|
if (supportsOAuth(descriptor)) {
|
|
@@ -5912,13 +6466,139 @@ function PickMethodStep({ descriptor, error, onPick }) {
|
|
|
5912
6466
|
})]
|
|
5913
6467
|
});
|
|
5914
6468
|
}
|
|
5915
|
-
|
|
6469
|
+
/**
|
|
6470
|
+
* Multi-field form for {@link ProviderDescriptor.customFields}. All fields
|
|
6471
|
+
* render on a single page as a stack of inputs; the user moves between them
|
|
6472
|
+
* with ↑/↓ (or tab / shift-tab) and edits any value freely before submitting.
|
|
6473
|
+
* A trailing "save" row submits the whole form.
|
|
6474
|
+
*
|
|
6475
|
+
* Each `<input>` owns its own buffer (none are unmounted while navigating), so
|
|
6476
|
+
* returning to an earlier field shows what was already typed. Required fields
|
|
6477
|
+
* are validated on submit; the first missing one focuses + reports inline.
|
|
6478
|
+
*/
|
|
6479
|
+
function EnterCustomFieldsStep({ descriptor, error, onSubmit }) {
|
|
6480
|
+
const screenFocused = useModalAwareFocus();
|
|
6481
|
+
const COLOR = useColors();
|
|
6482
|
+
const fields = useMemo(() => descriptor.customFields ?? [], [descriptor]);
|
|
6483
|
+
const inputRefs = useRef([]);
|
|
6484
|
+
const SAVE_ROW = fields.length;
|
|
6485
|
+
const [focusedRow, setFocusedRow] = useState(0);
|
|
6486
|
+
const collectValues = useCallback(() => {
|
|
6487
|
+
const values = {};
|
|
6488
|
+
fields.forEach((field, i) => {
|
|
6489
|
+
values[field.key] = inputRefs.current[i]?.value ?? "";
|
|
6490
|
+
});
|
|
6491
|
+
return values;
|
|
6492
|
+
}, [fields]);
|
|
6493
|
+
const submit = useCallback(() => {
|
|
6494
|
+
const values = collectValues();
|
|
6495
|
+
const missingIdx = fields.findIndex((f) => f.required && !values[f.key]?.trim());
|
|
6496
|
+
if (missingIdx >= 0) {
|
|
6497
|
+
setFocusedRow(missingIdx);
|
|
6498
|
+
onSubmit(descriptor, values);
|
|
6499
|
+
return;
|
|
6500
|
+
}
|
|
6501
|
+
onSubmit(descriptor, values);
|
|
6502
|
+
}, [
|
|
6503
|
+
descriptor,
|
|
6504
|
+
fields,
|
|
6505
|
+
collectValues,
|
|
6506
|
+
onSubmit
|
|
6507
|
+
]);
|
|
6508
|
+
const move = useCallback((delta) => {
|
|
6509
|
+
setFocusedRow((prev) => {
|
|
6510
|
+
const next = prev + delta;
|
|
6511
|
+
if (next < 0) return 0;
|
|
6512
|
+
if (next > SAVE_ROW) return SAVE_ROW;
|
|
6513
|
+
return next;
|
|
6514
|
+
});
|
|
6515
|
+
}, [SAVE_ROW]);
|
|
6516
|
+
useKeyboard((key) => {
|
|
6517
|
+
if (!screenFocused) return;
|
|
6518
|
+
if (key.name === "up" || key.name === "tab" && key.shift) {
|
|
6519
|
+
move(-1);
|
|
6520
|
+
return;
|
|
6521
|
+
}
|
|
6522
|
+
if (key.name === "down" || key.name === "tab" && !key.shift) {
|
|
6523
|
+
move(1);
|
|
6524
|
+
return;
|
|
6525
|
+
}
|
|
6526
|
+
if (key.name === "return") if (focusedRow >= SAVE_ROW) submit();
|
|
6527
|
+
else move(1);
|
|
6528
|
+
});
|
|
6529
|
+
return /* @__PURE__ */ jsxs(WizardPanel, {
|
|
6530
|
+
title: `configure ${descriptor.label}`,
|
|
6531
|
+
error,
|
|
6532
|
+
children: [
|
|
6533
|
+
/* @__PURE__ */ jsxs("text", {
|
|
6534
|
+
fg: COLOR.dim,
|
|
6535
|
+
children: [
|
|
6536
|
+
/* @__PURE__ */ jsx("span", {
|
|
6537
|
+
fg: COLOR.model,
|
|
6538
|
+
children: "↑/↓"
|
|
6539
|
+
}),
|
|
6540
|
+
" to move between fields · ",
|
|
6541
|
+
/* @__PURE__ */ jsx("span", {
|
|
6542
|
+
fg: COLOR.model,
|
|
6543
|
+
children: "enter"
|
|
6544
|
+
}),
|
|
6545
|
+
" to advance / save · esc to exit"
|
|
6546
|
+
]
|
|
6547
|
+
}),
|
|
6548
|
+
fields.map((field, i) => {
|
|
6549
|
+
const requiredTag = field.required ? " (required)" : " (optional)";
|
|
6550
|
+
const active = focusedRow === i;
|
|
6551
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
6552
|
+
style: { flexDirection: "column" },
|
|
6553
|
+
children: [
|
|
6554
|
+
/* @__PURE__ */ jsx("text", {
|
|
6555
|
+
fg: active ? COLOR.model : COLOR.dim,
|
|
6556
|
+
children: `${field.label}${requiredTag}`
|
|
6557
|
+
}),
|
|
6558
|
+
field.hint && /* @__PURE__ */ jsx("text", {
|
|
6559
|
+
fg: COLOR.dim,
|
|
6560
|
+
children: stripTrailingPressHint(field.hint)
|
|
6561
|
+
}),
|
|
6562
|
+
/* @__PURE__ */ jsx("box", {
|
|
6563
|
+
style: {
|
|
6564
|
+
border: true,
|
|
6565
|
+
borderColor: active ? COLOR.borderActive : COLOR.border,
|
|
6566
|
+
paddingLeft: 1,
|
|
6567
|
+
paddingRight: 1,
|
|
6568
|
+
height: 3
|
|
6569
|
+
},
|
|
6570
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
6571
|
+
ref: (r) => {
|
|
6572
|
+
inputRefs.current[i] = r;
|
|
6573
|
+
},
|
|
6574
|
+
focused: screenFocused && active,
|
|
6575
|
+
keyBindings: API_KEY_INPUT_BINDINGS,
|
|
6576
|
+
placeholder: field.placeholder ?? field.label,
|
|
6577
|
+
onSubmit: () => {},
|
|
6578
|
+
style: { flexGrow: 1 }
|
|
6579
|
+
})
|
|
6580
|
+
})
|
|
6581
|
+
]
|
|
6582
|
+
}, field.key);
|
|
6583
|
+
}),
|
|
6584
|
+
/* @__PURE__ */ jsx("text", {
|
|
6585
|
+
fg: focusedRow === SAVE_ROW ? COLOR.brand : COLOR.dim,
|
|
6586
|
+
children: focusedRow === SAVE_ROW ? "▶ save" : " save"
|
|
6587
|
+
})
|
|
6588
|
+
]
|
|
6589
|
+
});
|
|
6590
|
+
}
|
|
6591
|
+
function EnterApiKeyStep({ descriptor, customFields, error, onSubmit }) {
|
|
5916
6592
|
const focused = useModalAwareFocus();
|
|
5917
6593
|
const inputRef = useRef(null);
|
|
5918
6594
|
const COLOR = useColors();
|
|
5919
6595
|
const submit = useCallback(() => {
|
|
5920
|
-
onSubmit(descriptor, inputRef.current?.value ?? "");
|
|
5921
|
-
}, [
|
|
6596
|
+
onSubmit(descriptor, customFields, inputRef.current?.value ?? "");
|
|
6597
|
+
}, [
|
|
6598
|
+
descriptor,
|
|
6599
|
+
customFields,
|
|
6600
|
+
onSubmit
|
|
6601
|
+
]);
|
|
5922
6602
|
return /* @__PURE__ */ jsxs(WizardPanel, {
|
|
5923
6603
|
title: `configure ${descriptor.label} — paste API key`,
|
|
5924
6604
|
error,
|
|
@@ -6201,7 +6881,7 @@ function SessionsScreen({ sessions, currentId, focusedSessionId, onPick, onCreat
|
|
|
6201
6881
|
const OVERLAY_RESERVED = 6;
|
|
6202
6882
|
const TITLE_LEN = 8;
|
|
6203
6883
|
const SEP_LEN = 3;
|
|
6204
|
-
const allProjectsLen = showAllProjects ?
|
|
6884
|
+
const allProjectsLen = showAllProjects ? 15 : 0;
|
|
6205
6885
|
const countLen = countSegs.reduce((sum, s) => sum + s.text.length, 0);
|
|
6206
6886
|
const cwdBudget = Math.max(0, termWidth - 4 - TITLE_LEN - OVERLAY_RESERVED) - countLen - SEP_LEN - allProjectsLen;
|
|
6207
6887
|
if (cwdBudget < 6) return countSegs;
|
|
@@ -8174,7 +8854,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8174
8854
|
mcpToolsByServer
|
|
8175
8855
|
]);
|
|
8176
8856
|
const filteredProviders = useMemo(() => (authentication?.providers ?? []).filter((p) => matchesQuery(providerCorpus(p), query)), [authentication?.providers, query]);
|
|
8177
|
-
const authRowCount = filteredProviders.length
|
|
8857
|
+
const authRowCount = filteredProviders.length;
|
|
8178
8858
|
const filteredSize = {
|
|
8179
8859
|
...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, filteredByCategory[c.id].length])),
|
|
8180
8860
|
skills: filteredSkills.length,
|
|
@@ -8368,16 +9048,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8368
9048
|
return;
|
|
8369
9049
|
}
|
|
8370
9050
|
if (activeTab === "authentication") {
|
|
8371
|
-
if (cursor === filteredProviders.length || !filteredProviders[cursor]) {
|
|
8372
|
-
actions?.onReauth?.();
|
|
8373
|
-
return;
|
|
8374
|
-
}
|
|
8375
9051
|
const provider = filteredProviders[cursor];
|
|
8376
|
-
if (provider
|
|
8377
|
-
actions.onPickProvider(provider);
|
|
8378
|
-
return;
|
|
8379
|
-
}
|
|
8380
|
-
actions?.onReauth?.();
|
|
9052
|
+
if (provider) actions?.onConfigureProvider?.(provider);
|
|
8381
9053
|
return;
|
|
8382
9054
|
}
|
|
8383
9055
|
return;
|
|
@@ -8894,7 +9566,6 @@ function AuthenticationList({ view, providers, cursor, highlightBg, query }) {
|
|
|
8894
9566
|
const COLOR = useColors();
|
|
8895
9567
|
const home = homedir();
|
|
8896
9568
|
const totalProviders = view.providers?.length ?? 0;
|
|
8897
|
-
const addRowIndex = providers.length;
|
|
8898
9569
|
return /* @__PURE__ */ jsxs("box", {
|
|
8899
9570
|
style: { flexDirection: "column" },
|
|
8900
9571
|
children: [
|
|
@@ -9013,44 +9684,10 @@ function AuthenticationList({ view, providers, cursor, highlightBg, query }) {
|
|
|
9013
9684
|
children: ` ${p.methods.map((m) => `${m.source}: ${displayPath$1(m.detail, home)}`).join(" · ")}`
|
|
9014
9685
|
})]
|
|
9015
9686
|
}, p.key);
|
|
9016
|
-
}),
|
|
9017
|
-
/* @__PURE__ */ jsx(AuthAddRow, {
|
|
9018
|
-
anchorIndex: addRowIndex,
|
|
9019
|
-
focused: cursor === addRowIndex,
|
|
9020
|
-
highlightBg
|
|
9021
9687
|
})
|
|
9022
9688
|
]
|
|
9023
9689
|
});
|
|
9024
9690
|
}
|
|
9025
|
-
function AuthAddRow({ anchorIndex, focused, highlightBg }) {
|
|
9026
|
-
const COLOR = useColors();
|
|
9027
|
-
const bg = focused ? highlightBg : void 0;
|
|
9028
|
-
return /* @__PURE__ */ jsxs("box", {
|
|
9029
|
-
id: anchorIdFor(anchorIndex),
|
|
9030
|
-
style: {
|
|
9031
|
-
flexDirection: "column",
|
|
9032
|
-
flexShrink: 0,
|
|
9033
|
-
marginTop: 1,
|
|
9034
|
-
paddingLeft: 1,
|
|
9035
|
-
paddingRight: 1,
|
|
9036
|
-
backgroundColor: bg
|
|
9037
|
-
},
|
|
9038
|
-
children: [/* @__PURE__ */ jsxs("text", {
|
|
9039
|
-
wrapMode: "none",
|
|
9040
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
9041
|
-
fg: focused ? COLOR.brand : COLOR.mute,
|
|
9042
|
-
children: focused ? "▶ " : " "
|
|
9043
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
9044
|
-
fg: focused ? COLOR.brand : COLOR.dim,
|
|
9045
|
-
children: "+ add or re-configure a provider"
|
|
9046
|
-
})]
|
|
9047
|
-
}), /* @__PURE__ */ jsx("text", {
|
|
9048
|
-
wrapMode: "none",
|
|
9049
|
-
fg: COLOR.mute,
|
|
9050
|
-
children: " launch the setup wizard"
|
|
9051
|
-
})]
|
|
9052
|
-
});
|
|
9053
|
-
}
|
|
9054
9691
|
function ToggleRow({ id, label, description, enabled, focused, bg }) {
|
|
9055
9692
|
const COLOR = useColors();
|
|
9056
9693
|
return /* @__PURE__ */ jsxs("box", {
|
|
@@ -9601,23 +10238,25 @@ const PREVIEW_CHAR_MAX = 8e3;
|
|
|
9601
10238
|
* keeps a comfortable shape rather than stretching to the full height.
|
|
9602
10239
|
*/
|
|
9603
10240
|
const MAX_MODAL_HEIGHT = 28;
|
|
9604
|
-
const EDIT_TEXTAREA_BINDINGS =
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
10241
|
+
const EDIT_TEXTAREA_BINDINGS = (() => {
|
|
10242
|
+
return [
|
|
10243
|
+
...defaultTextareaKeyBindings.filter((b) => b.name !== "return" && !(b.name === "a" && b.ctrl && !b.shift && !b.meta)),
|
|
10244
|
+
{
|
|
10245
|
+
name: "a",
|
|
10246
|
+
ctrl: true,
|
|
10247
|
+
action: "select-all"
|
|
10248
|
+
},
|
|
10249
|
+
{
|
|
10250
|
+
name: "return",
|
|
10251
|
+
action: "submit"
|
|
10252
|
+
},
|
|
10253
|
+
{
|
|
10254
|
+
name: "return",
|
|
10255
|
+
shift: true,
|
|
10256
|
+
action: "newline"
|
|
10257
|
+
}
|
|
10258
|
+
];
|
|
10259
|
+
})();
|
|
9621
10260
|
/**
|
|
9622
10261
|
* Extract the editable text from a turn — joins all `text` blocks.
|
|
9623
10262
|
* Non-text blocks (tool_call, tool_result, thinking, etc.) are structural
|
|
@@ -10305,6 +10944,7 @@ function AppShell() {
|
|
|
10305
10944
|
if (!resumeProvider) return "auth";
|
|
10306
10945
|
return lastResumedSessionId ? "chat" : "sessions";
|
|
10307
10946
|
});
|
|
10947
|
+
const [authConfigureKey, setAuthConfigureKey] = useState(void 0);
|
|
10308
10948
|
const [picked, setPicked] = useState(() => initialPicked);
|
|
10309
10949
|
const pickedRef = useRef(picked);
|
|
10310
10950
|
pickedRef.current = picked;
|
|
@@ -10524,13 +11164,12 @@ function AppShell() {
|
|
|
10524
11164
|
const remembered = initialState.lastModelByProvider?.[provider.key];
|
|
10525
11165
|
const model = modelId ?? remembered ?? descriptor.defaultModel ?? descriptor.factory().meta.defaultModel;
|
|
10526
11166
|
const effort = effortForModel(descriptor, model, initialState.lastEffortByModel);
|
|
10527
|
-
|
|
11167
|
+
const opts = restoreModelOptions(descriptor, model, initialState.lastModelOptionsByModel);
|
|
11168
|
+
return {
|
|
10528
11169
|
provider,
|
|
10529
11170
|
model,
|
|
10530
|
-
effort
|
|
10531
|
-
|
|
10532
|
-
provider,
|
|
10533
|
-
model
|
|
11171
|
+
...effort ? { effort } : {},
|
|
11172
|
+
...opts ? { modelOptions: opts } : {}
|
|
10534
11173
|
};
|
|
10535
11174
|
}, [providerRegistry, initialState]);
|
|
10536
11175
|
const cancelRunOnDenial = useCallback((reason) => {
|
|
@@ -10761,6 +11400,25 @@ function AppShell() {
|
|
|
10761
11400
|
});
|
|
10762
11401
|
agent.hooks.hook("stream:thinking", ({ delta, turnId }) => stream.queueStreamDelta("thinking", delta, { turnId }));
|
|
10763
11402
|
agent.hooks.hook("stream:text", ({ delta, turnId }) => stream.queueStreamDelta("markdown", delta, { turnId }));
|
|
11403
|
+
agent.hooks.hook("stream:server_tool_use", ({ id, name, input, turnId }) => {
|
|
11404
|
+
stream.appendImmediate({
|
|
11405
|
+
kind: "tool",
|
|
11406
|
+
text: toolCallPreview(name, input),
|
|
11407
|
+
tool: name,
|
|
11408
|
+
input,
|
|
11409
|
+
callId: id,
|
|
11410
|
+
turnId
|
|
11411
|
+
});
|
|
11412
|
+
});
|
|
11413
|
+
agent.hooks.hook("stream:server_tool_result", ({ toolUseId, toolName, content, turnId }) => {
|
|
11414
|
+
stream.appendImmediate({
|
|
11415
|
+
kind: "tool-result",
|
|
11416
|
+
text: serverToolResultSummary(content),
|
|
11417
|
+
tool: toolName,
|
|
11418
|
+
callId: toolUseId,
|
|
11419
|
+
turnId
|
|
11420
|
+
});
|
|
11421
|
+
});
|
|
10764
11422
|
agent.hooks.hook("tool:before", async ({ callId, name, input, turnId }) => {
|
|
10765
11423
|
registerInFlightTool({
|
|
10766
11424
|
callId,
|
|
@@ -10922,6 +11580,29 @@ function AppShell() {
|
|
|
10922
11580
|
turnId
|
|
10923
11581
|
});
|
|
10924
11582
|
});
|
|
11583
|
+
agent.hooks.hook("child:stream:server_tool_use", ({ id, name, input, childId, depth, turnId }) => {
|
|
11584
|
+
stream.appendImmediate({
|
|
11585
|
+
kind: "tool",
|
|
11586
|
+
text: toolCallPreview(name, input),
|
|
11587
|
+
tool: name,
|
|
11588
|
+
input,
|
|
11589
|
+
callId: id,
|
|
11590
|
+
childId,
|
|
11591
|
+
depth,
|
|
11592
|
+
turnId
|
|
11593
|
+
});
|
|
11594
|
+
});
|
|
11595
|
+
agent.hooks.hook("child:stream:server_tool_result", ({ toolUseId, toolName, content, childId, depth, turnId }) => {
|
|
11596
|
+
stream.appendImmediate({
|
|
11597
|
+
kind: "tool-result",
|
|
11598
|
+
text: serverToolResultSummary(content),
|
|
11599
|
+
tool: toolName,
|
|
11600
|
+
callId: toolUseId,
|
|
11601
|
+
childId,
|
|
11602
|
+
depth,
|
|
11603
|
+
turnId
|
|
11604
|
+
});
|
|
11605
|
+
});
|
|
10925
11606
|
agent.hooks.hook("child:tool:before", ({ callId, name, input, childId, depth, turnId, priorContent }) => {
|
|
10926
11607
|
registerInFlightTool({
|
|
10927
11608
|
callId,
|
|
@@ -11069,9 +11750,11 @@ function AppShell() {
|
|
|
11069
11750
|
setEvents(eventsFromTurns(session.turns, session.runs));
|
|
11070
11751
|
setBusy(true);
|
|
11071
11752
|
try {
|
|
11753
|
+
const runModelOptions = enabledModelOptions(currentPicked.modelOptions);
|
|
11072
11754
|
await agent.run({
|
|
11073
11755
|
model: currentPicked.model,
|
|
11074
|
-
...currentPicked.effort ? { thinking: currentPicked.effort } : {}
|
|
11756
|
+
...currentPicked.effort ? { thinking: currentPicked.effort } : {},
|
|
11757
|
+
...Object.keys(runModelOptions).length > 0 ? { modelOptions: runModelOptions } : {}
|
|
11075
11758
|
});
|
|
11076
11759
|
await session.save().catch((err) => debugLog("resume-interaction: session.save failed", err));
|
|
11077
11760
|
setCurrentSession((prev) => prev ? {
|
|
@@ -11194,6 +11877,7 @@ function AppShell() {
|
|
|
11194
11877
|
const onPickProvider = useCallback(async (p) => {
|
|
11195
11878
|
const next = makePicked(p);
|
|
11196
11879
|
if (!next) return;
|
|
11880
|
+
setAuthConfigureKey(void 0);
|
|
11197
11881
|
setPicked(next);
|
|
11198
11882
|
stateStore.save({
|
|
11199
11883
|
...stateStore.load(),
|
|
@@ -11356,13 +12040,12 @@ function AppShell() {
|
|
|
11356
12040
|
}
|
|
11357
12041
|
});
|
|
11358
12042
|
const nextEffort = descriptor ? effortForModel(descriptor, next.modelId, prior.lastEffortByModel) : void 0;
|
|
11359
|
-
|
|
12043
|
+
const nextOptions = descriptor ? restoreModelOptions(descriptor, next.modelId, prior.lastModelOptionsByModel) : void 0;
|
|
12044
|
+
setPicked({
|
|
11360
12045
|
provider: nextProvider,
|
|
11361
12046
|
model: next.modelId,
|
|
11362
|
-
effort: nextEffort
|
|
11363
|
-
|
|
11364
|
-
provider: nextProvider,
|
|
11365
|
-
model: next.modelId
|
|
12047
|
+
...nextEffort ? { effort: nextEffort } : {},
|
|
12048
|
+
...nextOptions ? { modelOptions: nextOptions } : {}
|
|
11366
12049
|
});
|
|
11367
12050
|
modal.close();
|
|
11368
12051
|
if (providerChanged && currentSession && !busy && !pendingApproval) await activateSession(currentSession.id, nextProvider.key);
|
|
@@ -11395,6 +12078,32 @@ function AppShell() {
|
|
|
11395
12078
|
});
|
|
11396
12079
|
modal.close();
|
|
11397
12080
|
}, [modal, stateStore]);
|
|
12081
|
+
const onToggleModelOption = useCallback((optionId) => {
|
|
12082
|
+
setPicked((prev) => {
|
|
12083
|
+
if (!prev) return prev;
|
|
12084
|
+
const next = {
|
|
12085
|
+
...prev.modelOptions ?? {},
|
|
12086
|
+
[optionId]: !prev.modelOptions?.[optionId]
|
|
12087
|
+
};
|
|
12088
|
+
const prior = stateStore.load();
|
|
12089
|
+
stateStore.save({
|
|
12090
|
+
...prior,
|
|
12091
|
+
lastModelOptionsByModel: {
|
|
12092
|
+
...prior.lastModelOptionsByModel,
|
|
12093
|
+
[prev.model]: next
|
|
12094
|
+
}
|
|
12095
|
+
});
|
|
12096
|
+
return {
|
|
12097
|
+
...prev,
|
|
12098
|
+
modelOptions: next
|
|
12099
|
+
};
|
|
12100
|
+
});
|
|
12101
|
+
}, [stateStore]);
|
|
12102
|
+
const modelOptions = useMemo(() => {
|
|
12103
|
+
if (!picked) return [];
|
|
12104
|
+
const descriptor = providerRegistry[picked.provider.key];
|
|
12105
|
+
return descriptor ? modelOptionsFor(descriptor, picked.model) : [];
|
|
12106
|
+
}, [picked, providerRegistry]);
|
|
11398
12107
|
const modelHasReasoning = useMemo(() => {
|
|
11399
12108
|
if (!picked) return false;
|
|
11400
12109
|
const descriptor = providerRegistry[picked.provider.key];
|
|
@@ -11498,10 +12207,12 @@ function AppShell() {
|
|
|
11498
12207
|
name: att.name
|
|
11499
12208
|
};
|
|
11500
12209
|
})];
|
|
12210
|
+
const runModelOptions = enabledModelOptions(picked.modelOptions);
|
|
11501
12211
|
await agent.run({
|
|
11502
12212
|
model: picked.model,
|
|
11503
12213
|
prompt: runPrompt,
|
|
11504
|
-
...picked.effort ? { thinking: picked.effort } : {}
|
|
12214
|
+
...picked.effort ? { thinking: picked.effort } : {},
|
|
12215
|
+
...Object.keys(runModelOptions).length > 0 ? { modelOptions: runModelOptions } : {}
|
|
11505
12216
|
});
|
|
11506
12217
|
await session.save().catch((err) => debugLog("session.save failed", err));
|
|
11507
12218
|
setCurrentSession((prev) => prev ? {
|
|
@@ -11559,10 +12270,11 @@ function AppShell() {
|
|
|
11559
12270
|
}
|
|
11560
12271
|
})();
|
|
11561
12272
|
}, [picked, runSingleMessage]);
|
|
11562
|
-
const
|
|
12273
|
+
const onConfigureProvider = useMemo(() => {
|
|
11563
12274
|
if (busy || pendingApproval) return void 0;
|
|
11564
|
-
return () => {
|
|
12275
|
+
return (provider) => {
|
|
11565
12276
|
modal.close();
|
|
12277
|
+
setAuthConfigureKey(provider.key);
|
|
11566
12278
|
setScreen("auth");
|
|
11567
12279
|
};
|
|
11568
12280
|
}, [
|
|
@@ -11955,7 +12667,7 @@ function AppShell() {
|
|
|
11955
12667
|
restoredFiles: restoration.restoredFiles,
|
|
11956
12668
|
restoredSkills: restoration.restoredSkills,
|
|
11957
12669
|
model: result.model,
|
|
11958
|
-
inputTokens: (result.usage
|
|
12670
|
+
inputTokens: effectiveInputFromTurn(result.usage),
|
|
11959
12671
|
outputTokens: result.usage.output ?? 0,
|
|
11960
12672
|
effectiveTokens
|
|
11961
12673
|
};
|
|
@@ -12148,10 +12860,6 @@ function AppShell() {
|
|
|
12148
12860
|
}
|
|
12149
12861
|
if (matchesBinding(key, keybindings.openSettings) && screen !== "auth") {
|
|
12150
12862
|
const allProviders = detectAuth(config.paths.userDir, providerRegistry);
|
|
12151
|
-
const onPickProviderFromSettings = (provider) => {
|
|
12152
|
-
modal.close();
|
|
12153
|
-
onPickProvider(provider);
|
|
12154
|
-
};
|
|
12155
12863
|
modal.open(/* @__PURE__ */ jsx(SettingsModal, {
|
|
12156
12864
|
keybindings,
|
|
12157
12865
|
keybindingsPath: keybindingsPath(config.paths.userDir),
|
|
@@ -12162,8 +12870,7 @@ function AppShell() {
|
|
|
12162
12870
|
providers: allProviders
|
|
12163
12871
|
},
|
|
12164
12872
|
actions: {
|
|
12165
|
-
|
|
12166
|
-
onPickProvider: onPickProviderFromSettings,
|
|
12873
|
+
onConfigureProvider,
|
|
12167
12874
|
onOpenKeybindings: onOpenKeybindingsFile,
|
|
12168
12875
|
onLoginMcp,
|
|
12169
12876
|
onLogoutMcp,
|
|
@@ -12197,12 +12904,16 @@ function AppShell() {
|
|
|
12197
12904
|
}));
|
|
12198
12905
|
return;
|
|
12199
12906
|
}
|
|
12200
|
-
if (matchesBinding(key, keybindings.openEffortPicker) && screen === "chat" && picked && !busy && modelHasReasoning) {
|
|
12907
|
+
if (matchesBinding(key, keybindings.openEffortPicker) && screen === "chat" && picked && !busy && (modelHasReasoning || modelOptions.length > 0)) {
|
|
12201
12908
|
const descriptor = providerRegistry[picked.provider.key];
|
|
12202
|
-
modal.open(/* @__PURE__ */ jsx(
|
|
12203
|
-
|
|
12909
|
+
modal.open(/* @__PURE__ */ jsx(ModelOptionsPickerModal, {
|
|
12910
|
+
supportsReasoning: modelHasReasoning,
|
|
12204
12911
|
supportsAdaptive: !!descriptor && piIdOf(descriptor) === "anthropic",
|
|
12205
|
-
|
|
12912
|
+
currentEffort: picked.effort,
|
|
12913
|
+
options: modelOptions,
|
|
12914
|
+
enabled: picked.modelOptions,
|
|
12915
|
+
onPickEffort,
|
|
12916
|
+
onToggleOption: onToggleModelOption
|
|
12206
12917
|
}));
|
|
12207
12918
|
return;
|
|
12208
12919
|
}
|
|
@@ -12213,6 +12924,21 @@ function AppShell() {
|
|
|
12213
12924
|
}));
|
|
12214
12925
|
return;
|
|
12215
12926
|
}
|
|
12927
|
+
if (matchesBinding(key, keybindings.openContextPanel) && screen === "chat" && picked) {
|
|
12928
|
+
const agent = agentRef.current;
|
|
12929
|
+
if (!agent) return;
|
|
12930
|
+
const descriptor = providerRegistry[picked.provider.key];
|
|
12931
|
+
const effWindow = effectiveContextWindow(descriptor ? getContextWindow(descriptor, picked.model) : null) ?? void 0;
|
|
12932
|
+
agent.getContextBreakdown({
|
|
12933
|
+
model: picked.model,
|
|
12934
|
+
...effWindow !== void 0 ? { effectiveWindow: effWindow } : {},
|
|
12935
|
+
autocompactBuffer: OUTPUT_RESERVE_TOKENS,
|
|
12936
|
+
...settings.autoCompact && settings.autoCompactThreshold ? { compactThreshold: settings.autoCompactThreshold } : {}
|
|
12937
|
+
}).then((breakdown) => {
|
|
12938
|
+
if (breakdown && agentRef.current === agent) modal.open(/* @__PURE__ */ jsx(ContextPanelModal, { breakdown }));
|
|
12939
|
+
}).catch((err) => debugLog("getContextBreakdown failed", err));
|
|
12940
|
+
return;
|
|
12941
|
+
}
|
|
12216
12942
|
if (matchesBinding(key, keybindings.openKeybindings) && screen !== "auth") {
|
|
12217
12943
|
modal.open(/* @__PURE__ */ jsx(KeybindingsModal, {
|
|
12218
12944
|
bindings: keybindings,
|
|
@@ -12354,6 +13080,10 @@ function AppShell() {
|
|
|
12354
13080
|
modelColor: COLOR.model,
|
|
12355
13081
|
effortLabel: modelHasReasoning ? picked?.effort ?? "medium" : null,
|
|
12356
13082
|
effortColor: COLOR.warn,
|
|
13083
|
+
modelOptionsLabel: (() => {
|
|
13084
|
+
const on = modelOptions.filter((o) => picked?.modelOptions?.[o.id]);
|
|
13085
|
+
return on.length > 0 ? on.map((o) => o.id).join(", ") : null;
|
|
13086
|
+
})(),
|
|
12357
13087
|
effortKeyColor: COLOR.warn,
|
|
12358
13088
|
agentLabel: pickedAgent.label,
|
|
12359
13089
|
agentColor: accentColor(pickedAgent.accent, COLOR),
|
|
@@ -12375,6 +13105,7 @@ function AppShell() {
|
|
|
12375
13105
|
pickedAgent,
|
|
12376
13106
|
COLOR,
|
|
12377
13107
|
modelHasReasoning,
|
|
13108
|
+
modelOptions,
|
|
12378
13109
|
keybindings,
|
|
12379
13110
|
inFlightTools,
|
|
12380
13111
|
backgroundTasks,
|
|
@@ -12439,7 +13170,10 @@ function AppShell() {
|
|
|
12439
13170
|
paddingRight: 1
|
|
12440
13171
|
},
|
|
12441
13172
|
children: [
|
|
12442
|
-
screen === "auth" && /* @__PURE__ */ jsx(AuthScreen, {
|
|
13173
|
+
screen === "auth" && /* @__PURE__ */ jsx(AuthScreen, {
|
|
13174
|
+
onPick: onPickProvider,
|
|
13175
|
+
initialConfigureKey: authConfigureKey
|
|
13176
|
+
}),
|
|
12443
13177
|
screen === "sessions" && /* @__PURE__ */ jsx(SessionsScreen, {
|
|
12444
13178
|
sessions,
|
|
12445
13179
|
currentId: currentSession?.id ?? null,
|
|
@@ -12726,6 +13460,198 @@ function initTreeSitterWorker() {
|
|
|
12726
13460
|
return getTreeSitterClient().initialize();
|
|
12727
13461
|
}
|
|
12728
13462
|
//#endregion
|
|
13463
|
+
//#region src/tui/effort-picker.tsx
|
|
13464
|
+
const BASE_LEVELS = [
|
|
13465
|
+
{
|
|
13466
|
+
id: "off",
|
|
13467
|
+
description: "no reasoning — fastest, smallest output"
|
|
13468
|
+
},
|
|
13469
|
+
{
|
|
13470
|
+
id: "minimal",
|
|
13471
|
+
description: "tiny reasoning budget (gpt-5 family)"
|
|
13472
|
+
},
|
|
13473
|
+
{
|
|
13474
|
+
id: "low",
|
|
13475
|
+
description: "short reasoning pass"
|
|
13476
|
+
},
|
|
13477
|
+
{
|
|
13478
|
+
id: "medium",
|
|
13479
|
+
description: "balanced — sensible default"
|
|
13480
|
+
},
|
|
13481
|
+
{
|
|
13482
|
+
id: "high",
|
|
13483
|
+
description: "deep reasoning — slowest, longest"
|
|
13484
|
+
}
|
|
13485
|
+
];
|
|
13486
|
+
const ADAPTIVE_LEVEL = {
|
|
13487
|
+
id: "adaptive",
|
|
13488
|
+
description: "model decides per-turn (Anthropic)"
|
|
13489
|
+
};
|
|
13490
|
+
function EffortPickerModal({ current, supportsAdaptive, onPick }) {
|
|
13491
|
+
const COLOR = useColors();
|
|
13492
|
+
const SURFACE = useSurfaces();
|
|
13493
|
+
const inputRef = useRef(null);
|
|
13494
|
+
const [query, setQuery] = useState("");
|
|
13495
|
+
const levels = useMemo(() => {
|
|
13496
|
+
return (supportsAdaptive ? [...BASE_LEVELS, ADAPTIVE_LEVEL] : BASE_LEVELS).map((l) => ({
|
|
13497
|
+
...l,
|
|
13498
|
+
searchCorpus: `${l.id} ${l.description}`.toLowerCase()
|
|
13499
|
+
}));
|
|
13500
|
+
}, [supportsAdaptive]);
|
|
13501
|
+
const filtered = useMemo(() => {
|
|
13502
|
+
const trimmed = query.trim().toLowerCase();
|
|
13503
|
+
if (!trimmed) return levels;
|
|
13504
|
+
const terms = trimmed.split(/\s+/);
|
|
13505
|
+
return levels.filter((l) => terms.every((t) => l.searchCorpus.includes(t)));
|
|
13506
|
+
}, [levels, query]);
|
|
13507
|
+
const [selectedIdx, setSelectedIdx] = useState(() => {
|
|
13508
|
+
const idx = levels.findIndex((l) => l.id === current);
|
|
13509
|
+
if (idx >= 0) return idx;
|
|
13510
|
+
const fallback = levels.findIndex((l) => l.id === "medium");
|
|
13511
|
+
return fallback < 0 ? 0 : fallback;
|
|
13512
|
+
});
|
|
13513
|
+
const handleQueryChange = useCallback((next) => {
|
|
13514
|
+
setQuery(next);
|
|
13515
|
+
setSelectedIdx(0);
|
|
13516
|
+
}, []);
|
|
13517
|
+
const safeIndex = filtered.length === 0 ? 0 : Math.min(selectedIdx, filtered.length - 1);
|
|
13518
|
+
const commit = () => {
|
|
13519
|
+
const row = filtered[safeIndex];
|
|
13520
|
+
if (row) onPick(row.id);
|
|
13521
|
+
};
|
|
13522
|
+
useEffect(() => {
|
|
13523
|
+
inputRef.current?.focus();
|
|
13524
|
+
}, []);
|
|
13525
|
+
useKeyboard((key) => {
|
|
13526
|
+
if (key.name === "up") {
|
|
13527
|
+
setSelectedIdx((i) => {
|
|
13528
|
+
if (filtered.length === 0) return i;
|
|
13529
|
+
return ((i - 1) % filtered.length + filtered.length) % filtered.length;
|
|
13530
|
+
});
|
|
13531
|
+
return;
|
|
13532
|
+
}
|
|
13533
|
+
if (key.name === "down") {
|
|
13534
|
+
setSelectedIdx((i) => {
|
|
13535
|
+
if (filtered.length === 0) return i;
|
|
13536
|
+
return (i + 1) % filtered.length;
|
|
13537
|
+
});
|
|
13538
|
+
return;
|
|
13539
|
+
}
|
|
13540
|
+
if (key.name === "return") commit();
|
|
13541
|
+
});
|
|
13542
|
+
return /* @__PURE__ */ jsxs(Modal, {
|
|
13543
|
+
title: "select reasoning effort",
|
|
13544
|
+
maxWidth: 80,
|
|
13545
|
+
children: [
|
|
13546
|
+
/* @__PURE__ */ jsx("box", {
|
|
13547
|
+
style: {
|
|
13548
|
+
border: true,
|
|
13549
|
+
borderColor: COLOR.borderActive,
|
|
13550
|
+
paddingLeft: 1,
|
|
13551
|
+
paddingRight: 1,
|
|
13552
|
+
height: 3
|
|
13553
|
+
},
|
|
13554
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
13555
|
+
ref: inputRef,
|
|
13556
|
+
focused: true,
|
|
13557
|
+
placeholder: "search effort levels…",
|
|
13558
|
+
onInput: handleQueryChange,
|
|
13559
|
+
onSubmit: () => {},
|
|
13560
|
+
style: { flexGrow: 1 }
|
|
13561
|
+
})
|
|
13562
|
+
}),
|
|
13563
|
+
/* @__PURE__ */ jsx("box", {
|
|
13564
|
+
style: {
|
|
13565
|
+
flexDirection: "column",
|
|
13566
|
+
flexShrink: 0
|
|
13567
|
+
},
|
|
13568
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsxs("text", {
|
|
13569
|
+
fg: COLOR.dim,
|
|
13570
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
13571
|
+
fg: COLOR.mute,
|
|
13572
|
+
children: "no levels match "
|
|
13573
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
13574
|
+
fg: COLOR.warn,
|
|
13575
|
+
children: query.trim()
|
|
13576
|
+
})]
|
|
13577
|
+
}) : filtered.map((level, i) => /* @__PURE__ */ jsx(EffortRow, {
|
|
13578
|
+
level,
|
|
13579
|
+
isCurrent: level.id === current,
|
|
13580
|
+
isFocused: i === safeIndex,
|
|
13581
|
+
highlightBg: SURFACE.selection
|
|
13582
|
+
}, level.id))
|
|
13583
|
+
}),
|
|
13584
|
+
/* @__PURE__ */ jsxs("text", {
|
|
13585
|
+
fg: COLOR.dim,
|
|
13586
|
+
children: [
|
|
13587
|
+
/* @__PURE__ */ jsx("span", {
|
|
13588
|
+
fg: COLOR.warn,
|
|
13589
|
+
children: "↑↓"
|
|
13590
|
+
}),
|
|
13591
|
+
" navigate · ",
|
|
13592
|
+
/* @__PURE__ */ jsx("span", {
|
|
13593
|
+
fg: COLOR.warn,
|
|
13594
|
+
children: "↵"
|
|
13595
|
+
}),
|
|
13596
|
+
" select · ",
|
|
13597
|
+
/* @__PURE__ */ jsx("span", {
|
|
13598
|
+
fg: COLOR.warn,
|
|
13599
|
+
children: "esc"
|
|
13600
|
+
}),
|
|
13601
|
+
" close · ",
|
|
13602
|
+
/* @__PURE__ */ jsx("span", {
|
|
13603
|
+
fg: COLOR.mute,
|
|
13604
|
+
children: `${filtered.length} / ${levels.length} level${levels.length === 1 ? "" : "s"}`
|
|
13605
|
+
})
|
|
13606
|
+
]
|
|
13607
|
+
})
|
|
13608
|
+
]
|
|
13609
|
+
});
|
|
13610
|
+
}
|
|
13611
|
+
/**
|
|
13612
|
+
* Single row in the picker. Mirrors `ModelRow` in `model-picker.tsx`:
|
|
13613
|
+
* `●` marker for the current pick, single-space middle-dot separators,
|
|
13614
|
+
* focused row gets the `surfaces.selection` background lift.
|
|
13615
|
+
*/
|
|
13616
|
+
function EffortRow({ level, isCurrent, isFocused, highlightBg }) {
|
|
13617
|
+
const COLOR = useColors();
|
|
13618
|
+
const marker = isCurrent ? "●" : " ";
|
|
13619
|
+
return /* @__PURE__ */ jsx("box", {
|
|
13620
|
+
style: {
|
|
13621
|
+
height: 1,
|
|
13622
|
+
paddingLeft: 1,
|
|
13623
|
+
paddingRight: 1,
|
|
13624
|
+
flexShrink: 0,
|
|
13625
|
+
backgroundColor: isFocused ? highlightBg : void 0
|
|
13626
|
+
},
|
|
13627
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
13628
|
+
wrapMode: "none",
|
|
13629
|
+
children: [
|
|
13630
|
+
/* @__PURE__ */ jsx("span", {
|
|
13631
|
+
fg: isCurrent ? COLOR.brand : COLOR.mute,
|
|
13632
|
+
children: marker
|
|
13633
|
+
}),
|
|
13634
|
+
/* @__PURE__ */ jsx("span", {
|
|
13635
|
+
fg: COLOR.mute,
|
|
13636
|
+
children: " "
|
|
13637
|
+
}),
|
|
13638
|
+
/* @__PURE__ */ jsx("span", {
|
|
13639
|
+
fg: isFocused ? COLOR.brand : COLOR.dim,
|
|
13640
|
+
children: level.id
|
|
13641
|
+
}),
|
|
13642
|
+
/* @__PURE__ */ jsx("span", {
|
|
13643
|
+
fg: COLOR.mute,
|
|
13644
|
+
children: " · "
|
|
13645
|
+
}),
|
|
13646
|
+
/* @__PURE__ */ jsx("span", {
|
|
13647
|
+
fg: COLOR.mute,
|
|
13648
|
+
children: level.description
|
|
13649
|
+
})
|
|
13650
|
+
]
|
|
13651
|
+
})
|
|
13652
|
+
});
|
|
13653
|
+
}
|
|
13654
|
+
//#endregion
|
|
12729
13655
|
//#region src/tui/mcps-settings.tsx
|
|
12730
13656
|
/**
|
|
12731
13657
|
* MCP server picker. Hierarchical: each server is one row, and an
|
|
@@ -13591,6 +14517,6 @@ async function runTui(options = {}) {
|
|
|
13591
14517
|
process.exit(0);
|
|
13592
14518
|
}
|
|
13593
14519
|
//#endregion
|
|
13594
|
-
export { AgentPickerModal, App, AuthScreen, ChatScreen, CompletionPopup, EffortPickerModal, Footer, InteractionBlock, McpsSettingsModal, Modal, ModalRoot, ModelPickerModal, SessionDetailsModal, SessionsScreen, SettingsModal, SkillsSettingsModal, Spinner, StatusSpinner, TOOL_DISPLAY, TitleOverlay, ToggleListModal, Transcript, TurnDetailsModal, accentColor, buildMdStyle, clipHintsToWidth, computeTurnAnchors, displayNameFor, formatToolCall, hintsLength, isEditErrorResult, isTurnHighlighted, isVisible, marginTopFor, onInputSubmit, renderHintSpans, runTui, selectableTurnIds, splitMarkdownCodeBlocks, splitPromptSegments, truncateTrailing, turnSelectionOwnership, useMdStyle, useModal, useModalAwareFocus };
|
|
14520
|
+
export { AgentPickerModal, App, AuthScreen, ChatScreen, CompletionPopup, EffortPickerModal, Footer, InteractionBlock, McpsSettingsModal, Modal, ModalRoot, ModelOptionsPickerModal, ModelPickerModal, SessionDetailsModal, SessionsScreen, SettingsModal, SkillsSettingsModal, Spinner, StatusSpinner, TOOL_DISPLAY, TitleOverlay, ToggleListModal, Transcript, TurnDetailsModal, accentColor, buildMdStyle, clipHintsToWidth, computeTurnAnchors, displayNameFor, formatToolCall, hintsLength, isEditErrorResult, isTurnHighlighted, isVisible, marginTopFor, onInputSubmit, renderHintSpans, runTui, selectableTurnIds, splitMarkdownCodeBlocks, splitPromptSegments, truncateTrailing, turnSelectionOwnership, useMdStyle, useModal, useModalAwareFocus };
|
|
13595
14521
|
|
|
13596
14522
|
//# sourceMappingURL=tui.js.map
|