@blockrun/franklin 3.15.25 → 3.15.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proxy/server.js +12 -1
- package/dist/ui/app.js +50 -12
- package/package.json +1 -1
package/dist/proxy/server.js
CHANGED
|
@@ -452,7 +452,18 @@ export function createProxy(options) {
|
|
|
452
452
|
// Use the body with the correct fallback model for payment
|
|
453
453
|
body = result.bodyUsed;
|
|
454
454
|
usedFallback = result.fallbackUsed;
|
|
455
|
-
|
|
455
|
+
// Skip the success log when the request originated from a test
|
|
456
|
+
// fixture, even if the fallback ended on a real model. Verified
|
|
457
|
+
// on a real machine: 5 spurious "↺ Fallback successful: using
|
|
458
|
+
// deepseek/deepseek-chat" entries appeared in
|
|
459
|
+
// franklin-debug.log because the proxy timeout test uses
|
|
460
|
+
// `slow/model` (filtered) as the source but ends up on
|
|
461
|
+
// `deepseek/deepseek-chat` (not filtered). Check the
|
|
462
|
+
// failedModels array — any fixture in there means the call
|
|
463
|
+
// chain started in a test.
|
|
464
|
+
const fallbackTouchedFixture = result.failedModels.some(isTestFixtureModel) ||
|
|
465
|
+
isTestFixtureModel(finalModel);
|
|
466
|
+
if (usedFallback && !fallbackTouchedFixture) {
|
|
456
467
|
logger.info(`[franklin] ↺ Fallback successful: using ${finalModel}`);
|
|
457
468
|
}
|
|
458
469
|
}
|
package/dist/ui/app.js
CHANGED
|
@@ -56,18 +56,35 @@ function useTerminalSize() {
|
|
|
56
56
|
}, [stdout]);
|
|
57
57
|
return size;
|
|
58
58
|
}
|
|
59
|
-
function InputBox({ input, setInput, onSubmit, model, balance, chain, walletTail, sessionCost, queued, queuedCount, focused, busy, contextPct, vimMode, onVimModeChange }) {
|
|
59
|
+
function InputBox({ input, setInput, onSubmit, model, balance, chain, walletTail, sessionCost, queued, queuedCount, focused, busy, awaitingApproval, awaitingAnswer, contextPct, vimMode, onVimModeChange }) {
|
|
60
60
|
const { cols } = useTerminalSize();
|
|
61
61
|
// Avoid drawing right up to the terminal edge. Several terminals auto-wrap
|
|
62
62
|
// a full-width border glyph onto the next row, which leaves "ghost" top
|
|
63
63
|
// borders behind on re-render after errors / status changes.
|
|
64
64
|
const boxWidth = Math.max(20, cols - 2);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
// Awaiting-input states beat "Working..." — the agent isn't busy, it's
|
|
66
|
+
// blocked on the user. Saying "Working..." here while a permission dialog
|
|
67
|
+
// sits in the scrollback above is exactly how users miss it (verified
|
|
68
|
+
// 2026-05-04 from a real screenshot — "Working..." spinner kept turning
|
|
69
|
+
// while the agent waited on a Bash approval).
|
|
70
|
+
const placeholder = awaitingApproval
|
|
71
|
+
? '⚠ Approval needed — press [y]/[a]/[n] in the prompt above'
|
|
72
|
+
: awaitingAnswer
|
|
73
|
+
? '⚠ Question above — type your answer'
|
|
74
|
+
: busy
|
|
75
|
+
? (queued
|
|
76
|
+
? `⏎ ${queuedCount ?? 1} queued: ${queued.slice(0, 40)}`
|
|
77
|
+
: 'Working...')
|
|
78
|
+
: 'Type a message...';
|
|
79
|
+
// Color the input-box border to match the urgency. Awaiting-user states
|
|
80
|
+
// get a bright yellow border so the focal point physically moves down to
|
|
81
|
+
// the input field, even peripheral vision picks it up.
|
|
82
|
+
const borderColor = awaitingApproval || awaitingAnswer ? 'yellow' : undefined;
|
|
83
|
+
const showSpinner = busy && !input && !awaitingApproval && !awaitingAnswer;
|
|
84
|
+
const leadingGlyph = (awaitingApproval || awaitingAnswer)
|
|
85
|
+
? _jsx(Text, { color: "yellow", bold: true, children: "\u26A0 " })
|
|
86
|
+
: (showSpinner ? _jsxs(Text, { color: "yellow", children: [_jsx(Spinner, { type: "dots" }), " "] }) : null);
|
|
87
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { borderStyle: "round", borderColor: borderColor, borderDimColor: !borderColor, paddingX: 1, width: boxWidth, children: [leadingGlyph, _jsx(Box, { flexGrow: 1, children: vimMode ? (_jsx(VimInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: placeholder, focus: focused !== false, showMode: true, onModeChange: onVimModeChange })) : (_jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: placeholder, focus: focused !== false })) })] }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: [busy ? _jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }) : null, busy ? ' ' : '', shortModelName(model), " \u00B7 ", balance, chain ? _jsxs(Text, { children: [" \u00B7 ", _jsx(Text, { color: "magenta", children: chain }), walletTail ? _jsxs(Text, { dimColor: true, children: [":", walletTail] }) : ''] }) : '', sessionCost > 0.00001 ? _jsxs(Text, { color: "yellow", children: [" -$", sessionCost.toFixed(4)] }) : '', contextPct !== undefined && contextPct > 0 ? (() => {
|
|
71
88
|
// Visual context bar: ▓▓▓▓▓▓░░░░ 75%
|
|
72
89
|
const filled = Math.round(contextPct / 10);
|
|
73
90
|
const empty = 10 - filled;
|
|
@@ -148,6 +165,27 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
148
165
|
const [permissionRequest, setPermissionRequest] = useState(null);
|
|
149
166
|
const [askUserRequest, setAskUserRequest] = useState(null);
|
|
150
167
|
const [askUserInput, setAskUserInput] = useState('');
|
|
168
|
+
// Ring the terminal bell exactly once when a permission/askUser dialog
|
|
169
|
+
// first appears. Helpful when the user has Franklin in a background
|
|
170
|
+
// tab and the agent stops to ask for approval — verified 2026-05-04
|
|
171
|
+
// from a real screenshot where the user missed the dialog because the
|
|
172
|
+
// input box still read "Working...". Opt-out via FRANKLIN_NO_BELL=1.
|
|
173
|
+
const bellPlayedRef = useRef(false);
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
const dialogActive = !!permissionRequest || !!askUserRequest;
|
|
176
|
+
if (dialogActive && !bellPlayedRef.current) {
|
|
177
|
+
bellPlayedRef.current = true;
|
|
178
|
+
if (process.env.FRANKLIN_NO_BELL !== '1') {
|
|
179
|
+
try {
|
|
180
|
+
process.stderr.write('\x07');
|
|
181
|
+
}
|
|
182
|
+
catch { /* swallow — never break the UI on a TTY without bell */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (!dialogActive) {
|
|
186
|
+
bellPlayedRef.current = false;
|
|
187
|
+
}
|
|
188
|
+
}, [permissionRequest, askUserRequest]);
|
|
151
189
|
// Messages queued while agent is busy — auto-submitted FIFO when turns complete.
|
|
152
190
|
const [queuedInputs, setQueuedInputs] = useState([]);
|
|
153
191
|
const turnDoneCallbackRef = useRef(null);
|
|
@@ -771,7 +809,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
771
809
|
: `${formatTokens(r.tokens.input)} in / ${formatTokens(r.tokens.output)} out`, r.cost > 0 ? ` · $${r.cost.toFixed(4)}` : '', r.savings !== undefined && r.savings > 0 ? _jsxs(Text, { color: "green", children: [" saved ", Math.round(r.savings * 100), "%"] }) : '', r.ctxPct !== undefined && r.ctxPct >= 5
|
|
772
810
|
? _jsxs(Text, { color: r.ctxPct >= 80 ? 'red' : r.ctxPct >= 50 ? 'yellow' : undefined, dimColor: r.ctxPct < 50, children: [" \u00B7 ctx ", r.ctxPct, "%"] })
|
|
773
811
|
: ''] }) }))] }, r.key));
|
|
774
|
-
} }), permissionRequest && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { color: "yellow", children: "\u256D\u2500 Permission required \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Text, { color: "yellow", children: ["\u2502 ", _jsx(Text, { bold: true, children: permissionRequest.toolName })] }), permissionRequest.description.split('\n').map((line, i) => (_jsxs(Text, { dimColor: true, children: ["\u2502 ", line] }, i))), _jsx(Text, { color: "yellow", children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { children: [_jsx(Text, { bold: true, color: "green", children: "[y]" }), _jsx(Text, { dimColor: true, children: " yes " }), _jsx(Text, { bold: true, color: "cyan", children: "[a]" }), _jsx(Text, { dimColor: true, children: " always " }), _jsx(Text, { bold: true, color: "red", children: "[n]" }), _jsx(Text, { dimColor: true, children: " no" })] }) })] })), askUserRequest && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { color: "cyan", children: "\u256D\u2500 Question \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Text, { color: "cyan", children: ["\u2502 ", _jsx(Text, { bold: true, children: askUserRequest.question })] }), askUserRequest.options && askUserRequest.options.length > 0 && (askUserRequest.options.map((opt, i) => (_jsxs(Text, { dimColor: true, children: ["\u2502 ", i + 1, ". ", opt] }, i)))), _jsx(Text, { color: "cyan", children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { bold: true, children: "answer> " }), _jsx(TextInput, { value: askUserInput, onChange: setAskUserInput, onSubmit: (val) => {
|
|
812
|
+
} }), permissionRequest && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { color: "red", bold: true, children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u26A0 ACTION REQUIRED \u26A0 \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }), _jsx(Text, { color: "yellow", children: "\u256D\u2500 Permission required \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Text, { color: "yellow", children: ["\u2502 ", _jsx(Text, { bold: true, children: permissionRequest.toolName })] }), permissionRequest.description.split('\n').map((line, i) => (_jsxs(Text, { dimColor: true, children: ["\u2502 ", line] }, i))), _jsx(Text, { color: "yellow", children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { children: [_jsx(Text, { bold: true, color: "green", children: "[y]" }), _jsx(Text, { dimColor: true, children: " yes " }), _jsx(Text, { bold: true, color: "cyan", children: "[a]" }), _jsx(Text, { dimColor: true, children: " always " }), _jsx(Text, { bold: true, color: "red", children: "[n]" }), _jsx(Text, { dimColor: true, children: " no" })] }) })] })), askUserRequest && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { color: "magenta", bold: true, children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u26A0 ANSWER REQUIRED \u26A0 \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }), _jsx(Text, { color: "cyan", children: "\u256D\u2500 Question \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Text, { color: "cyan", children: ["\u2502 ", _jsx(Text, { bold: true, children: askUserRequest.question })] }), askUserRequest.options && askUserRequest.options.length > 0 && (askUserRequest.options.map((opt, i) => (_jsxs(Text, { dimColor: true, children: ["\u2502 ", i + 1, ". ", opt] }, i)))), _jsx(Text, { color: "cyan", children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { bold: true, children: "answer> " }), _jsx(TextInput, { value: askUserInput, onChange: setAskUserInput, onSubmit: (val) => {
|
|
775
813
|
const answer = val.trim() || '(no response)';
|
|
776
814
|
const r = askUserRequest.resolve;
|
|
777
815
|
setAskUserRequest(null);
|
|
@@ -784,14 +822,14 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
784
822
|
: `${tool.elapsed}ms`;
|
|
785
823
|
const hasExpandableContent = !!(tool.diff || (tool.fullOutput && tool.fullOutput.split('\n').length > 1));
|
|
786
824
|
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { children: [tool.error ? _jsx(Text, { color: "red", children: "\u2717" }) : _jsx(Text, { color: "green", children: "\u2713" }), ' ', _jsx(Text, { bold: true, children: tool.name }), tool.preview ? _jsxs(Text, { dimColor: true, children: ["(", tool.preview.slice(0, 80), ")"] }) : null, _jsxs(Text, { dimColor: true, children: [" ", elapsedFmt] }), hasExpandableContent && (_jsxs(Text, { dimColor: true, children: [" ", tool.expanded ? '(tab to collapse)' : '(tab to expand)'] }))] }), !tool.expanded && tool.diff && !tool.error && tool.diff.oldLines.length <= 8 && tool.diff.newLines.length <= 8 && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [tool.diff.oldLines.map((line, i) => (_jsxs(Text, { color: "red", wrap: "truncate-end", children: ['⎿ ', "- ", line.slice(0, 120)] }, `old-${i}`))), tool.diff.newLines.map((line, i) => (_jsxs(Text, { color: "green", wrap: "truncate-end", children: ['⎿ ', "+ ", line.slice(0, 120)] }, `new-${i}`)))] })), tool.expanded && tool.fullOutput && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [tool.fullOutput.split('\n').slice(0, 30).map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ['⎿ ', line.slice(0, 120)] }, i))), tool.fullOutput.split('\n').length > 30 && (_jsxs(Text, { dimColor: true, children: ['⎿ ', "... ", tool.fullOutput.split('\n').length - 30, " more lines"] }))] })), tool.error && !tool.expanded && tool.fullOutput && (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: tool.fullOutput.split('\n').filter(Boolean).slice(0, 3).map((line, i) => (_jsxs(Text, { color: "red", wrap: "truncate-end", children: ['⎿ ', line.slice(0, 120)] }, i))) }))] }));
|
|
787
|
-
})(), Array.from(tools.entries()).map(([id, tool]) => {
|
|
825
|
+
})(), !permissionRequest && !askUserRequest && Array.from(tools.entries()).map(([id, tool]) => {
|
|
788
826
|
const elapsed = Math.round((Date.now() - tool.startTime) / 1000);
|
|
789
827
|
const elapsedStr = elapsed > 0 ? ` ${elapsed}s` : '';
|
|
790
828
|
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), ' ', _jsx(Text, { bold: true, color: "cyan", children: tool.name }), tool.preview ? _jsxs(Text, { dimColor: true, children: ["(", tool.preview.slice(0, 70), ")"] }) : null, _jsx(Text, { dimColor: true, children: elapsedStr })] }), tool.liveLines.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: tool.liveLines.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ['⎿ ', line.slice(0, 120)] }, i))) }))] }, id));
|
|
791
|
-
}), thinking && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { color: "magenta", children: [_jsx(Spinner, { type: "dots" }), ' ', _jsx(Text, { bold: true, children: "thinking" }), completedTools.length > 0 ? _jsxs(Text, { dimColor: true, children: [' ', "\u00B7 step ", completedTools.length + 1] }) : null] }), process.env.FRANKLIN_SHOW_THINKING === '1' && thinkingText && (() => {
|
|
829
|
+
}), thinking && !permissionRequest && !askUserRequest && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { color: "magenta", children: [_jsx(Spinner, { type: "dots" }), ' ', _jsx(Text, { bold: true, children: "thinking" }), completedTools.length > 0 ? _jsxs(Text, { dimColor: true, children: [' ', "\u00B7 step ", completedTools.length + 1] }) : null] }), process.env.FRANKLIN_SHOW_THINKING === '1' && thinkingText && (() => {
|
|
792
830
|
const lines = thinkingText.split('\n').filter(Boolean).slice(-3);
|
|
793
831
|
return (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: lines.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ['⎿ ', line.slice(0, 120)] }, i))) }));
|
|
794
|
-
})()] })), waiting && !thinking && tools.size === 0 && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "yellow", children: [_jsx(Spinner, { type: "dots" }), ' ', _jsxs(Text, { dimColor: true, children: [shortModelName(currentModel), completedTools.length > 0 ? ` · step ${completedTools.length + 1}` : ''] })] }) })), streamText && (() => {
|
|
832
|
+
})()] })), waiting && !thinking && tools.size === 0 && !permissionRequest && !askUserRequest && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "yellow", children: [_jsx(Spinner, { type: "dots" }), ' ', _jsxs(Text, { dimColor: true, children: [shortModelName(currentModel), completedTools.length > 0 ? ` · step ${completedTools.length + 1}` : ''] })] }) })), streamText && !permissionRequest && !askUserRequest && (() => {
|
|
795
833
|
const maxLines = Math.max(8, termRows - 12);
|
|
796
834
|
const lines = streamText.split('\n');
|
|
797
835
|
const truncated = lines.length > maxLines;
|
|
@@ -831,7 +869,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
831
869
|
return (_jsxs(Box, { marginLeft: 2, children: [_jsxs(Text, { inverse: isSelected, color: isSelected ? 'cyan' : isHighlight ? 'yellow' : undefined, bold: isSelected || isHighlight, children: [' ', m.label.padEnd(26), ' '] }), _jsxs(Text, { dimColor: true, children: [" ", m.shortcut.padEnd(14)] }), _jsx(Text, { color: m.price === 'FREE' ? 'green' : isHighlight ? 'yellow' : undefined, dimColor: !isHighlight && m.price !== 'FREE', children: m.price }), isCurrent && _jsx(Text, { color: "green", children: " \u2190" })] }, m.id));
|
|
832
870
|
})] }, cat.category));
|
|
833
871
|
}), hiddenBelow > 0 && (_jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["\u2193 ", hiddenBelow, " more below"] }) })), _jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsx(Text, { dimColor: true, children: "Your conversation stays above \u2014 picking a model keeps all history intact." }) })] }));
|
|
834
|
-
})(), !inPicker && !permissionRequest && !askUserRequest && (_jsx(InputBox, { input: input, setInput: setInput, onSubmit: handleSubmit, model: currentModel, balance: liveBalance, chain: chain, walletTail: walletAddress && walletAddress.length >= 4 && !walletAddress.startsWith('not set') ? walletAddress.slice(-4) : undefined, sessionCost: totalCost, queued: queuedInputs[0] || undefined, queuedCount: queuedInputs.length, focused: !permissionRequest && !askUserRequest, busy: !askUserRequest && (waiting || thinking || tools.size > 0), contextPct: contextPct, vimMode: vimEnabled, onVimModeChange: setCurrentVimMode }))] }));
|
|
872
|
+
})(), !inPicker && !permissionRequest && !askUserRequest && (_jsx(InputBox, { input: input, setInput: setInput, onSubmit: handleSubmit, model: currentModel, balance: liveBalance, chain: chain, walletTail: walletAddress && walletAddress.length >= 4 && !walletAddress.startsWith('not set') ? walletAddress.slice(-4) : undefined, sessionCost: totalCost, queued: queuedInputs[0] || undefined, queuedCount: queuedInputs.length, focused: !permissionRequest && !askUserRequest, busy: !askUserRequest && (waiting || thinking || tools.size > 0), awaitingApproval: !!permissionRequest, awaitingAnswer: !!askUserRequest, contextPct: contextPct, vimMode: vimEnabled, onVimModeChange: setCurrentVimMode }))] }));
|
|
835
873
|
}
|
|
836
874
|
export function launchInkUI(opts) {
|
|
837
875
|
let resolveInput = null;
|
package/package.json
CHANGED