koneck 2.128.27 → 2.128.29
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/auto-update.d.ts +79 -0
- package/dist/auto-update.d.ts.map +1 -0
- package/dist/auto-update.js +120 -0
- package/dist/auto-update.js.map +1 -0
- package/dist/config-store.d.ts +20 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +29 -2
- package/dist/config-store.js.map +1 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +117 -24
- package/dist/ink-chat.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/ink-chat.js
CHANGED
|
@@ -8,7 +8,7 @@ import { pacedInterval, startingInterval, floorFor, shouldDrawFrame } from './fr
|
|
|
8
8
|
import { estimateCost, formatCost, rateText } from './pricing.js';
|
|
9
9
|
import { generateSessionId, saveSession, listSessions, loadSession, relativeAge, renameSession, forkSession, archiveSession, findSession, } from './session.js';
|
|
10
10
|
import { loadMemory } from './memory.js';
|
|
11
|
-
import { loadKoneckConfig, saveKoneckConfig, setConfigKey, configKeyDescriptions, CONFIG_FILE, CONFIG_SCHEMA, stepValue, displayValue } from './config-store.js';
|
|
11
|
+
import { loadKoneckConfig, saveKoneckConfig, setConfigKey, configKeyDescriptions, CONFIG_FILE, CONFIG_SCHEMA, stepValue, displayValue, settingsWindow } from './config-store.js';
|
|
12
12
|
import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, removeUserProvider, userProvidersPath, keyEnvFor, BUILT_INS as BUILT_IN_PROVIDERS, servesLocally, servesOwnWeights, endpointForProvider } from './providers.js';
|
|
13
13
|
import { normaliseEndpoint, withEndpoint, savedEndpoints, shortSource, resolveModelChoice, withModel } from './endpoints.js';
|
|
14
14
|
import { loadProjectConfig, loadGlobalConfig, saveGlobalConfig, withoutStoredApiKey } from './config.js';
|
|
@@ -715,6 +715,14 @@ const PANEL_DIFF_LINES = 400;
|
|
|
715
715
|
const USAGE_TABS = ['settings', 'session', 'context', 'models', 'limits'];
|
|
716
716
|
const MIN_REPLY_WIDTH = 24; // below this a table cannot render legibly anyway
|
|
717
717
|
const PICKER_MAX_ROWS = 16;
|
|
718
|
+
/**
|
|
719
|
+
* Rows kept for everything around a picker in ordinary scrollback.
|
|
720
|
+
*
|
|
721
|
+
* The composer with its border, the status bar, the footer, the picker's own border and hint
|
|
722
|
+
* row, and the blanks between them. Found by measurement rather than by adding those up: at
|
|
723
|
+
* 80x24, nine and twelve both still wiped the terminal and fourteen did not.
|
|
724
|
+
*/
|
|
725
|
+
const DEFAULT_PICKER_RESERVE = 14;
|
|
718
726
|
/**
|
|
719
727
|
* Number of choices to keep visible in a palette.
|
|
720
728
|
*
|
|
@@ -723,13 +731,24 @@ const PICKER_MAX_ROWS = 16;
|
|
|
723
731
|
* the composer and the rest remains available through search and paging.
|
|
724
732
|
*/
|
|
725
733
|
export function pickerRowLimit(kind, itemCount, terminalRows, reservedRows) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
734
|
+
/*
|
|
735
|
+
* The window bounds this on every screen, and it used not to.
|
|
736
|
+
*
|
|
737
|
+
* The reasoning here was that ordinary scrollback is not a fixed frame, so a picker may run as
|
|
738
|
+
* long as it likes and let the terminal scroll — and that only the alternate screen needed a
|
|
739
|
+
* reservation, because overflow there makes Ink clear and repaint everything.
|
|
740
|
+
*
|
|
741
|
+
* Ink does not make that distinction. It compares the height of what it is about to draw
|
|
742
|
+
* against `stdout.rows` and wipes the terminal when it does not fit, whichever screen is in
|
|
743
|
+
* use, because it cannot erase lines that have scrolled away. The picker is part of the
|
|
744
|
+
* repainting region in both. Measured at 80x24: typing "/config" wiped the screen three times
|
|
745
|
+
* before the command had even been chosen, so every slash command flashed the whole terminal.
|
|
746
|
+
*
|
|
747
|
+
* The reservation differs — the alternate screen has a transcript above the picker to leave
|
|
748
|
+
* room for — but there is always one.
|
|
749
|
+
*/
|
|
750
|
+
const reserved = reservedRows ?? DEFAULT_PICKER_RESERVE;
|
|
751
|
+
const terminalBudget = Math.max(1, (terminalRows && terminalRows > 0 ? terminalRows : 24) - reserved);
|
|
733
752
|
// Keep kind in the signature: it documents that this is the common policy for *every* picker
|
|
734
753
|
// and lets a future picker opt into a different policy without duplicating this calculation.
|
|
735
754
|
void kind;
|
|
@@ -2389,6 +2408,14 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
2389
2408
|
}, [effort]);
|
|
2390
2409
|
const [showAnalytics, setShowAnalytics] = useState(false);
|
|
2391
2410
|
const [updatePane, setUpdatePane] = useState(null);
|
|
2411
|
+
/**
|
|
2412
|
+
* The version installed in the background, if one was; see auto-update.ts.
|
|
2413
|
+
*
|
|
2414
|
+
* Only ever says "installed", never "updated": the code this session is running was read into
|
|
2415
|
+
* memory when it started, and replacing the files on disk does not change it. The restart is
|
|
2416
|
+
* the part that matters and so it is the part the line ends on.
|
|
2417
|
+
*/
|
|
2418
|
+
const [autoUpdated, setAutoUpdated] = useState(null);
|
|
2392
2419
|
const [updating, setUpdating] = useState('idle');
|
|
2393
2420
|
const [updateLog, setUpdateLog] = useState('');
|
|
2394
2421
|
// Settable, because resuming has to adopt the restored session's id. Without that, every save
|
|
@@ -2804,6 +2831,35 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
2804
2831
|
timer = setTimeout(tick, interval);
|
|
2805
2832
|
return () => clearTimeout(timer);
|
|
2806
2833
|
}, [busy, animate, ask]);
|
|
2834
|
+
/*
|
|
2835
|
+
* Check for a new version and install it, quietly, in the background.
|
|
2836
|
+
*
|
|
2837
|
+
* Deliberately late and deliberately silent. It is network and a subprocess, neither of which
|
|
2838
|
+
* should compete with the first turn, and there is nothing to report unless it succeeds — a
|
|
2839
|
+
* registry that cannot be reached or an npm that cannot write to its global prefix is not the
|
|
2840
|
+
* user's problem to hear about mid-task. See auto-update.ts for what it refuses to do.
|
|
2841
|
+
*/
|
|
2842
|
+
useEffect(() => {
|
|
2843
|
+
if (cfg.autoUpdate === false)
|
|
2844
|
+
return;
|
|
2845
|
+
let live = true;
|
|
2846
|
+
const timer = setTimeout(() => {
|
|
2847
|
+
void (async () => {
|
|
2848
|
+
try {
|
|
2849
|
+
const { autoUpdate } = await import('./auto-update.js');
|
|
2850
|
+
const outcome = await autoUpdate({
|
|
2851
|
+
currentVersion: VERSION,
|
|
2852
|
+
modulePath: fileURLToPath(import.meta.url),
|
|
2853
|
+
enabled: cfg.autoUpdate !== false,
|
|
2854
|
+
});
|
|
2855
|
+
if (live && outcome.state === 'installed')
|
|
2856
|
+
setAutoUpdated(outcome.version);
|
|
2857
|
+
}
|
|
2858
|
+
catch { /* an update that cannot happen is not worth interrupting anyone over */ }
|
|
2859
|
+
})();
|
|
2860
|
+
}, 4_000);
|
|
2861
|
+
return () => { live = false; clearTimeout(timer); };
|
|
2862
|
+
}, [cfg.autoUpdate]);
|
|
2807
2863
|
// A queued prompt runs the moment the agent is free again. Keyed on `busy` rather than done
|
|
2808
2864
|
// inside the turn's own exit paths, so every way a turn can end — success, failure, cancel —
|
|
2809
2865
|
// drains the queue the same way.
|
|
@@ -4784,6 +4840,12 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
4784
4840
|
setCfg(c => ({ ...c, autoCopy: ['true', '1', 'yes'].includes(val.toLowerCase()) }));
|
|
4785
4841
|
if (key === 'animate')
|
|
4786
4842
|
setAnimate(['true', '1', 'yes'].includes(val.toLowerCase()));
|
|
4843
|
+
// Applied to the live session, not only written to the file. Every other switch here
|
|
4844
|
+
// takes effect at once, and one that quietly waited for a restart was a setting you
|
|
4845
|
+
// could not tell you had changed.
|
|
4846
|
+
if (key === 'autoUpdate') {
|
|
4847
|
+
setCfg(c => ({ ...c, autoUpdate: ['true', '1', 'yes'].includes(val.toLowerCase()) }));
|
|
4848
|
+
}
|
|
4787
4849
|
if (key === 'mouse') {
|
|
4788
4850
|
setMouse(['true', '1', 'yes'].includes(val.toLowerCase()));
|
|
4789
4851
|
}
|
|
@@ -5397,12 +5459,12 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
5397
5459
|
return;
|
|
5398
5460
|
}
|
|
5399
5461
|
if (key.pageDown) {
|
|
5400
|
-
const jump = pickerRowLimit(picker.kind, list.length,
|
|
5462
|
+
const jump = pickerRowLimit(picker.kind, list.length, termRows, altScreen ? 13 : undefined);
|
|
5401
5463
|
setPickIndex(i => Math.min(i + jump, Math.max(0, list.length - 1)));
|
|
5402
5464
|
return;
|
|
5403
5465
|
}
|
|
5404
5466
|
if (key.pageUp) {
|
|
5405
|
-
const jump = pickerRowLimit(picker.kind, list.length,
|
|
5467
|
+
const jump = pickerRowLimit(picker.kind, list.length, termRows, altScreen ? 13 : undefined);
|
|
5406
5468
|
setPickIndex(i => Math.max(0, i - jump));
|
|
5407
5469
|
return;
|
|
5408
5470
|
}
|
|
@@ -6360,13 +6422,40 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
6360
6422
|
const meter = (fraction, label) => [`${bar(fraction, BAR_W)} ${Math.round(fraction * 100)}%`, `${label}`, ''];
|
|
6361
6423
|
const lines = [tabs, '---'];
|
|
6362
6424
|
if (usagePane === 'settings') {
|
|
6363
|
-
|
|
6425
|
+
const compact = termRows < 30;
|
|
6426
|
+
const chrome = compact ? 21 : 21;
|
|
6427
|
+
if (!compact)
|
|
6428
|
+
lines.push('**Settings** — written to disk and applied now', '');
|
|
6364
6429
|
// What is actually in force, not just what this file happens to hold. provider and
|
|
6365
6430
|
// baseURL commonly come from ~/.koneckrc, and showing those as "(not set)" made a
|
|
6366
6431
|
// configured session look unconfigured.
|
|
6367
6432
|
const endpoint = endpointFor(cfg.provider);
|
|
6368
6433
|
const labelW = Math.max(...CONFIG_SCHEMA.map(f => f.label.length));
|
|
6434
|
+
/*
|
|
6435
|
+
* Only as many rows as there is room for.
|
|
6436
|
+
*
|
|
6437
|
+
* Everything else in this pane costs rows too — the tabs, the heading, the two footer
|
|
6438
|
+
* lines, the panel's own border — and below it sit the composer and the status bar. Drawn
|
|
6439
|
+
* in full on a twenty-four-row terminal the list ran off the top, leaving what looked
|
|
6440
|
+
* like a stunted four-row panel. It also made the repainting region as tall as the
|
|
6441
|
+
* window, which is what makes Ink wipe the terminal on every render.
|
|
6442
|
+
*/
|
|
6443
|
+
/*
|
|
6444
|
+
* How many rows there is room for, and what gets dropped when there is not.
|
|
6445
|
+
*
|
|
6446
|
+
* Measured, not estimated. At 80x24 the pane came to twenty-five lines with the list
|
|
6447
|
+
* already cut to its minimum of three, and twenty-five lines in a twenty-four-row
|
|
6448
|
+
* window is what makes Ink wipe the whole terminal on every render. Shrinking the list
|
|
6449
|
+
* further could not have fixed that — the fixed parts were the problem — so on a short
|
|
6450
|
+
* terminal the pane drops its own furniture first: the heading, the blank beneath it,
|
|
6451
|
+
* and the line naming the config file. They are the three that say least.
|
|
6452
|
+
*/
|
|
6453
|
+
const { start, end } = settingsWindow(CONFIG_SCHEMA.length, settingRow, Math.max(3, termRows - chrome));
|
|
6454
|
+
if (start > 0)
|
|
6455
|
+
lines.push(` ${G.caret} ${start} more above`);
|
|
6369
6456
|
for (const [i, f] of CONFIG_SCHEMA.entries()) {
|
|
6457
|
+
if (i < start || i >= end)
|
|
6458
|
+
continue;
|
|
6370
6459
|
const selected = i === settingRow;
|
|
6371
6460
|
const beingTyped = editing?.key === String(f.key);
|
|
6372
6461
|
// An endpoint always has a source worth naming, so it never reads as unset or as
|
|
@@ -6393,11 +6482,15 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
6393
6482
|
: ` enter to edit${f.clearable ? ', x clears' : ''}`;
|
|
6394
6483
|
lines.push(`${mark} ${f.label.padEnd(labelW)} ${selected ? '**' + shown + '**' : shown}${hint}`);
|
|
6395
6484
|
}
|
|
6485
|
+
if (end < CONFIG_SCHEMA.length) {
|
|
6486
|
+
lines.push(` ${G.caret} ${CONFIG_SCHEMA.length - end} more below`);
|
|
6487
|
+
}
|
|
6396
6488
|
lines.push('');
|
|
6397
6489
|
lines.push(editing
|
|
6398
6490
|
? 'Typing. **enter** saves, **ctrl+u** empties the field, **esc** abandons.'
|
|
6399
6491
|
: 'Up/down choose · enter change · x clear · left/right tabs');
|
|
6400
|
-
|
|
6492
|
+
if (!compact)
|
|
6493
|
+
lines.push(`Saved to ${CONFIG_FILE}`);
|
|
6401
6494
|
}
|
|
6402
6495
|
if (usagePane === 'session') {
|
|
6403
6496
|
const wall = Date.now() - sessionStartRef.current;
|
|
@@ -6579,7 +6672,7 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
6579
6672
|
// footer. They consume six rows and the picker itself has six non-choice rows (margin,
|
|
6580
6673
|
// border, title/search and pager), so reserve thirteen in total. Letting 16 choices into
|
|
6581
6674
|
// a 24-row frame made Ink reflow the live region and visibly blink on every keypress.
|
|
6582
|
-
const pickerRows = pickerRowLimit(picker.kind, list.length,
|
|
6675
|
+
const pickerRows = pickerRowLimit(picker.kind, list.length, termRows, altScreen ? 13 : undefined);
|
|
6583
6676
|
// Keep the highlighted row inside the visible window as the selection moves.
|
|
6584
6677
|
const start = Math.max(0, Math.min(pickIndex - Math.floor(pickerRows / 2), list.length - pickerRows));
|
|
6585
6678
|
const shown = list.slice(Math.max(0, start), Math.max(0, start) + pickerRows);
|
|
@@ -6671,17 +6764,17 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
6671
6764
|
return (_jsx(Box, { marginTop: 1, children: _jsx(Panel, { width: panelWidth(lines, barWidth, 46), color: AMBER, title: `Permission — ${modeSpec(mode).label} mode`, children: lines }) }));
|
|
6672
6765
|
})()] }), sidePanelOpen && (_jsx(Box, { flexDirection: "column", width: diffPanelWidth, flexShrink: 0, children: diffPanelOpen && _jsx(DiffPanel, { files: liveDiffs, width: diffPanelWidth, height: Math.max(6, frameRows - 6), activityRows: activityPanelOpen ? 4 : 0, activity: activityPanelOpen ? _jsxs(_Fragment, { children: [_jsxs(Text, { color: CYAN, bold: true, children: ["Activity \u00B7 ", fmtElapsed(busyStart.current ? Date.now() - busyStart.current : 0)] }), _jsx(Text, { color: AMBER, wrap: "truncate", children: ask ? 'Waiting for your approval' : liveToolRef.current
|
|
6673
6766
|
? 'Running ' + liveToolRef.current.name : thinkingRef.current.chars > 0
|
|
6674
|
-
? 'Waiting for model · reasoning received' : 'Waiting for model' }), _jsx(Text, { color: MUTED, wrap: "truncate", children: liveToolRef.current?.detail || plan?.steps.find(s => s.state !== 'done')?.text || 'esc to interrupt' })] }) : undefined }) }))] }),
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6767
|
+
? 'Waiting for model · reasoning received' : 'Waiting for model' }), _jsx(Text, { color: MUTED, wrap: "truncate", children: liveToolRef.current?.detail || plan?.steps.find(s => s.state !== 'done')?.text || 'esc to interrupt' })] }) : undefined }) }))] }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: busy ? DIM : CYAN, paddingX: 1, width: uiWidth - 5, flexShrink: 0, children: _jsxs(Text, { wrap: "wrap", children: [_jsxs(Text, { color: CYAN, bold: true, children: [G.caret, " "] }), (() => {
|
|
6768
|
+
const sel = selectionRange(selAnchor, caret, draft.length);
|
|
6769
|
+
if (sel) {
|
|
6770
|
+
return (_jsxs(_Fragment, { children: [_jsx(Text, { color: INK, children: draft.slice(0, sel[0]) }), _jsx(Text, { backgroundColor: CYAN, color: "#10222A", children: draft.slice(sel[0], sel[1]) }), _jsx(Text, { color: INK, children: draft.slice(sel[1]) })] }));
|
|
6771
|
+
}
|
|
6772
|
+
return (_jsxs(_Fragment, { children: [_jsx(Text, { color: INK, children: draft.slice(0, caret) }), busy
|
|
6773
|
+
? _jsx(Text, { color: INK, children: draft.slice(caret) })
|
|
6774
|
+
: caret < draft.length
|
|
6775
|
+
? _jsxs(_Fragment, { children: [_jsx(Text, { backgroundColor: CYAN, color: "#10222A", children: draft[caret] }), _jsx(Text, { color: INK, children: draft.slice(caret + 1) })] })
|
|
6776
|
+
: _jsx(Text, { color: CYAN, children: "\u2588" })] }));
|
|
6777
|
+
})()] }) }), copyNotice && _jsx(Text, { color: CYAN, children: copyNotice }), _jsx(Box, { marginTop: 1, paddingX: 1, width: uiWidth - 4, flexShrink: 0, children: _jsxs(Text, { wrap: "truncate", backgroundColor: "#24343B", children: [repaint % 2 === 1 ? '\u200b' : '', _jsx(Text, { color: MUTED, children: "TAB " }), _jsx(Text, { color: INK, children: "Analytics" }), _jsx(Text, { color: MUTED, children: " / " }), _jsx(Text, { color: CYAN, children: "/HELP" }), _jsx(Text, { color: INK, children: " Commands" }), _jsx(Text, { color: MUTED, children: " / SHIFT+TAB " }), _jsx(Text, { color: INK, children: "Mode" }), _jsx(Text, { color: MUTED, children: " / " }), _jsx(Text, { color: statusColor, children: statusLabel.toLowerCase() }), plan && planSummary(plan) && (_jsxs(_Fragment, { children: [_jsx(Text, { color: MUTED, children: " / PLAN: " }), _jsx(Text, { color: CYAN, children: planSummary(plan) })] })), _jsx(Text, { color: MUTED, children: " / MODE: " }), _jsx(Text, { color: mode === 'auto' ? INK : AMBER, children: modeSpec(mode).label }), _jsx(Text, { color: MUTED, children: " / EFFORT: " }), _jsx(Text, { color: effort === 'medium' ? INK : AMBER, children: effort }), autoUpdated && (_jsxs(_Fragment, { children: [_jsx(Text, { color: MUTED, children: " / " }), _jsxs(Text, { color: GREEN, children: [G.ok, " Update installed \u00B7 restart to use it"] })] })), mouse && (_jsxs(_Fragment, { children: [_jsx(Text, { color: MUTED, children: " / " }), _jsx(Text, { color: AMBER, children: "WHEEL: on \u2014 shift to select \u00B7 /mouse off" })] })), _jsx(Text, { color: MUTED, children: " / MODEL: " }), _jsx(Text, { color: INK, children: modelShort }), _jsx(Text, { color: MUTED, children: " / v" }), _jsx(Text, { color: updatePane?.behind ? AMBER : INK, children: VERSION }), updatePane?.behind && _jsx(Text, { color: AMBER, children: " \u2191" }), pasteCount > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { color: MUTED, children: " / " }), _jsxs(Text, { color: AMBER, children: [pasteCount, " paste", pasteCount === 1 ? '' : 's', " held, sent in full"] })] })), ' '.repeat(uiWidth)] }) })] }));
|
|
6685
6778
|
}
|
|
6686
6779
|
export async function runInkChatMode(config) {
|
|
6687
6780
|
// Nothing is written before the first frame. Filling the screen to push the prompt to the foot
|