koneck 2.124.0 → 2.126.0
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/engine.d.ts.map +1 -1
- package/dist/engine.js +5 -3
- package/dist/engine.js.map +1 -1
- package/dist/frame-pace.d.ts +63 -0
- package/dist/frame-pace.d.ts.map +1 -0
- package/dist/frame-pace.js +92 -0
- package/dist/frame-pace.js.map +1 -0
- package/dist/frame-sync.d.ts +25 -0
- package/dist/frame-sync.d.ts.map +1 -1
- package/dist/frame-sync.js +66 -0
- package/dist/frame-sync.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/ink-chat.d.ts +3 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +45 -9
- package/dist/ink-chat.js.map +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +17 -0
- package/dist/permissions.js.map +1 -1
- package/dist/tool-args.d.ts +22 -0
- package/dist/tool-args.d.ts.map +1 -0
- package/dist/tool-args.js +42 -0
- package/dist/tool-args.js.map +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +28 -5
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/ink-chat.js
CHANGED
|
@@ -3,7 +3,8 @@ import React, { useState, useEffect, useRef } from 'react';
|
|
|
3
3
|
import { Box, Static, Text, render, useApp, useInput, useStdin } from 'ink';
|
|
4
4
|
import { execa } from 'execa';
|
|
5
5
|
import { resolveAgentConcurrency, toolsUnsupportedNotice } from './engine-facts.js';
|
|
6
|
-
import { withSynchronizedFrames } from './frame-sync.js';
|
|
6
|
+
import { withSynchronizedFrames, probeSynchronizedOutput } from './frame-sync.js';
|
|
7
|
+
import { pacedInterval, startingInterval, floorFor } from './frame-pace.js';
|
|
7
8
|
import { estimateCost, formatCost, rateText } from './pricing.js';
|
|
8
9
|
import { generateSessionId, saveSession, listSessions, loadSession, relativeAge, renameSession, forkSession, archiveSession, findSession, } from './session.js';
|
|
9
10
|
import { loadMemory } from './memory.js';
|
|
@@ -1265,7 +1266,7 @@ export function DiffPanel({ files, width, height }) {
|
|
|
1265
1266
|
* Typechecking passes on that code and no test could reach it, because nothing in the suite mounted
|
|
1266
1267
|
* an Ink app. Something does now.
|
|
1267
1268
|
*/
|
|
1268
|
-
export function App({ config: initialConfig, clearFrame }) {
|
|
1269
|
+
export function App({ config: initialConfig, clearFrame, synchronizedFrames = false }) {
|
|
1269
1270
|
const { exit } = useApp();
|
|
1270
1271
|
const [cfg, setCfg] = useState(initialConfig);
|
|
1271
1272
|
// Tool approval callbacks belong to a session created on an earlier render. Keep the control
|
|
@@ -2197,6 +2198,17 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2197
2198
|
/** Tools this turn has run, which is what separates work from a plain answer. */
|
|
2198
2199
|
const turnToolsRef = useRef(0); // a tool failed and was worked around
|
|
2199
2200
|
const busyStart = useRef(0);
|
|
2201
|
+
/*
|
|
2202
|
+
* Whether this terminal composites a redraw, decided once before Ink started.
|
|
2203
|
+
*
|
|
2204
|
+
* A ref rather than state: it never changes for the life of the process, and the ticker reads it
|
|
2205
|
+
* when it starts. It gates the frame rate, because in alt-screen mode every frame rewrites the
|
|
2206
|
+
* whole screen — invisible where the terminal composites it, a blink of the entire interface
|
|
2207
|
+
* where it does not. See frame-sync.ts and frame-pace.ts.
|
|
2208
|
+
*/
|
|
2209
|
+
const syncedFramesRef = useRef(synchronizedFrames);
|
|
2210
|
+
/** Whether the diff panel is showing, so the transcript does not draw the same diff again. */
|
|
2211
|
+
const diffPanelOpenRef = useRef(false);
|
|
2200
2212
|
const wordTimer = useRef(0);
|
|
2201
2213
|
// Register on the session bus so other running KONECK instances can reach this one.
|
|
2202
2214
|
useEffect(() => {
|
|
@@ -2267,18 +2279,32 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2267
2279
|
// each frame atomically; see frame-sync.ts.
|
|
2268
2280
|
let timer;
|
|
2269
2281
|
const wordEvery = Math.max(1, Math.round(WORD_HOLD_MS / FRAME_MS));
|
|
2282
|
+
/*
|
|
2283
|
+
* The rate this machine can actually sustain, not the one we would like.
|
|
2284
|
+
*
|
|
2285
|
+
* Each frame records how long the timer really took to arrive. On time means the loop is
|
|
2286
|
+
* keeping up and the rate stands; late means the event loop had more work than the frame's own
|
|
2287
|
+
* budget, and asking again just as soon only queues a frame that cannot be painted — which is
|
|
2288
|
+
* how a Pi ends up lagging while the same build feels instant on a laptop. See frame-pace.ts.
|
|
2289
|
+
*/
|
|
2290
|
+
const floor = floorFor(syncedFramesRef.current);
|
|
2291
|
+
let interval = startingInterval(syncedFramesRef.current);
|
|
2292
|
+
let asked = Date.now();
|
|
2270
2293
|
const tick = () => {
|
|
2294
|
+
const now = Date.now();
|
|
2295
|
+
interval = pacedInterval(interval, now - asked, floor);
|
|
2271
2296
|
// Elapsed is read from the clock rather than counted in ticks, so a frame the event loop
|
|
2272
2297
|
// delivers late moves the animation on by what actually passed instead of falling behind.
|
|
2273
|
-
const elapsed =
|
|
2298
|
+
const elapsed = now - busyStart.current;
|
|
2274
2299
|
const { spin, shimmer } = animFrames(elapsed, SPINNER.length);
|
|
2275
2300
|
setAnim({ spin, shimmer, elapsed });
|
|
2276
2301
|
wordTimer.current += 1;
|
|
2277
2302
|
if (wordTimer.current % wordEvery === 0)
|
|
2278
2303
|
setSpinWord(w => w + 1);
|
|
2279
|
-
|
|
2304
|
+
asked = Date.now();
|
|
2305
|
+
timer = setTimeout(tick, interval);
|
|
2280
2306
|
};
|
|
2281
|
-
timer = setTimeout(tick,
|
|
2307
|
+
timer = setTimeout(tick, interval);
|
|
2282
2308
|
return () => clearTimeout(timer);
|
|
2283
2309
|
}, [busy]);
|
|
2284
2310
|
// A queued prompt runs the moment the agent is free again. Keyed on `busy` rather than done
|
|
@@ -5352,6 +5378,9 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
5352
5378
|
* scrollback full-width, and there is no frame to put a second column in.
|
|
5353
5379
|
*/
|
|
5354
5380
|
const diffPanelOpen = showDiffPanel && altScreen && liveDiffs.length > 0;
|
|
5381
|
+
// Read by renderStep, which is defined above this line, so it goes through a ref rather than
|
|
5382
|
+
// depending on declaration order.
|
|
5383
|
+
diffPanelOpenRef.current = diffPanelOpen;
|
|
5355
5384
|
const diffPanelWidth = diffPanelOpen
|
|
5356
5385
|
? Math.max(34, Math.min(Math.floor(uiWidth * 0.46), 96))
|
|
5357
5386
|
: 0;
|
|
@@ -5406,7 +5435,7 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
5406
5435
|
const ms = (step.endedAt ?? Date.now()) - step.startedAt;
|
|
5407
5436
|
const glyph = running ? SPINNER[spinFrame] : step.ok === false ? G.fail : G.ok;
|
|
5408
5437
|
const color = running ? AMBER : step.ok === false ? CRIMSON : GREEN;
|
|
5409
|
-
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: color, children: glyph }), _jsx(Text, { color: AMBER, bold: true, children: step.name }), _jsx(Text, { color: DIM, children: "\u00B7" }), _jsx(Text, { color: MUTED, children: fmtElapsed(ms) })] }), step.detail !== '' && (step.diffs ?? []).length === 0 && (_jsxs(Box, { paddingLeft: 2, gap: 1, children: [_jsx(Text, { color: DIM, children: "\u2514" }), _jsx(Text, { color: MUTED, children: step.detail })] })), step.ok === false && step.error && (_jsxs(Box, { paddingLeft: 2, gap: 1, children: [_jsx(Text, { color: DIM, children: "\u2514" }), _jsx(Text, { color: CRIMSON, children: fitCells(step.error, Math.max(20, barWidth - 6)) })] })), running && step.output && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: DIM, children: fitCells(step.output, Math.max(20, barWidth - 6)) }) })), (step.diffs ?? []).map((d, i) => renderDiff(d, i))] }, key));
|
|
5438
|
+
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: color, children: glyph }), _jsx(Text, { color: AMBER, bold: true, children: step.name }), _jsx(Text, { color: DIM, children: "\u00B7" }), _jsx(Text, { color: MUTED, children: fmtElapsed(ms) })] }), step.detail !== '' && (step.diffs ?? []).length === 0 && (_jsxs(Box, { paddingLeft: 2, gap: 1, children: [_jsx(Text, { color: DIM, children: "\u2514" }), _jsx(Text, { color: MUTED, children: step.detail })] })), step.ok === false && step.error && (_jsxs(Box, { paddingLeft: 2, gap: 1, children: [_jsx(Text, { color: DIM, children: "\u2514" }), _jsx(Text, { color: CRIMSON, children: fitCells(step.error, Math.max(20, barWidth - 6)) })] })), running && step.output && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: DIM, children: fitCells(step.output, Math.max(20, barWidth - 6)) }) })), !diffPanelOpenRef.current && (step.diffs ?? []).map((d, i) => renderDiff(d, i))] }, key));
|
|
5410
5439
|
};
|
|
5411
5440
|
/**
|
|
5412
5441
|
* One transcript row.
|
|
@@ -5474,7 +5503,7 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
5474
5503
|
//
|
|
5475
5504
|
// Both halves matter. Clipping the bottom would hide what just happened; anchoring
|
|
5476
5505
|
// always to the bottom is what put the banner there.
|
|
5477
|
-
_jsx(Box, { flexGrow: 0, flexShrink: 1, flexDirection: "column", justifyContent: "flex-end", overflowY: "hidden", children: visibleRows.map((row, i) => (_jsx(Box, { flexDirection: "column", flexShrink: 0, children: renderRow(row, i) }, i))) })), altScreen && scrollBack > 0 && (_jsx(Text, { color: AMBER, children: `${G.caret} scrolled back ${scrollBack} rows — PageDown, or just type, to follow again` })), showAnalytics && (_jsxs(Box, { borderStyle: "round", borderColor: AMBER, paddingX: 1, marginBottom: 1, flexDirection: "column", width: barWidth, children: [_jsx(Text, { color: AMBER, bold: true, children: "\u25C6 Session Analytics" }), _jsxs(Text, { color: MUTED, children: ["Turns : ", _jsx(Text, { color: INK, children: stats.turns })] }), _jsxs(Text, { color: MUTED, children: ["Prompt tok : ", _jsx(Text, { color: INK, children: stats.promptTokens.toLocaleString() })] }), _jsxs(Text, { color: MUTED, children: ["Output tok : ", _jsx(Text, { color: INK, children: stats.completionTokens.toLocaleString() })] }), _jsxs(Text, { color: MUTED, children: ["Total tok : ", _jsx(Text, { color: INK, children: stats.totalTokens.toLocaleString() })] }), _jsxs(Text, { color: MUTED, children: ["Model : ", _jsx(Text, { color: INK, children: modelShort }), " Provider: ", _jsx(Text, { color: INK, children: cfg.provider })] }), _jsx(Text, { color: DIM, children: "Tab to close" })] })), _jsx(Static, { items: altScreen ? [] : rows, children: (row, index) => renderRow(row, index) }), _jsx(Box, { flexDirection: "column", flexShrink: 0, children: busy && (agentState === 'processing' || agentState === 'syncing') && (_jsxs(Box, { flexDirection: "column", children: [sayRef.current.trim() !== '' && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: CYAN, bold: true, children: replyStartedRef.current ? ' ' : G.brand }), _jsx(Box, { flexDirection: "column", children: tailLines(sayRef.current, liveReplyLines(termRows), replyWidth - 3)
|
|
5506
|
+
_jsx(Box, { flexGrow: 0, flexShrink: 1, flexDirection: "column", justifyContent: "flex-end", overflowY: "hidden", children: visibleRows.map((row, i) => (_jsx(Box, { flexDirection: "column", flexShrink: 0, children: renderRow(row, i) }, i))) })), altScreen && scrollBack > 0 && (_jsx(Text, { color: AMBER, children: `${G.caret} scrolled back ${scrollBack} rows — PageDown, or just type, to follow again` })), showAnalytics && (_jsxs(Box, { borderStyle: "round", borderColor: AMBER, paddingX: 1, marginBottom: 1, flexDirection: "column", width: barWidth, children: [_jsx(Text, { color: AMBER, bold: true, children: "\u25C6 Session Analytics" }), _jsxs(Text, { color: MUTED, children: ["Turns : ", _jsx(Text, { color: INK, children: stats.turns })] }), _jsxs(Text, { color: MUTED, children: ["Prompt tok : ", _jsx(Text, { color: INK, children: stats.promptTokens.toLocaleString() })] }), _jsxs(Text, { color: MUTED, children: ["Output tok : ", _jsx(Text, { color: INK, children: stats.completionTokens.toLocaleString() })] }), _jsxs(Text, { color: MUTED, children: ["Total tok : ", _jsx(Text, { color: INK, children: stats.totalTokens.toLocaleString() })] }), _jsxs(Text, { color: MUTED, children: ["Model : ", _jsx(Text, { color: INK, children: modelShort }), " Provider: ", _jsx(Text, { color: INK, children: cfg.provider })] }), _jsx(Text, { color: DIM, children: "Tab to close" })] })), _jsx(Static, { items: altScreen ? [] : rows, children: (row, index) => renderRow(row, index) }), _jsx(Box, { flexDirection: "column", flexShrink: 0, children: busy && (agentState === 'processing' || agentState === 'syncing') && (_jsxs(Box, { flexDirection: "column", children: [!ask && sayRef.current.trim() !== '' && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: CYAN, bold: true, children: replyStartedRef.current ? ' ' : G.brand }), _jsx(Box, { flexDirection: "column", children: tailLines(sayRef.current, liveReplyLines(termRows), replyWidth - 3)
|
|
5478
5507
|
.split('\n').map((line, i) => _jsx(Text, { color: INK, children: line }, i)) })] })), liveToolRef.current && renderStep(liveToolRef.current, 1), queued.length > 0 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: AMBER, children: "\u25AA" }), _jsxs(Text, { color: MUTED, children: [queued.length, " queued, will run when this finishes \u00B7 /aside to ask without waiting"] })] }), queued.map((q, i) => (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: DIM, children: fitCells(q.split('\n')[0] ?? '', Math.max(20, barWidth - 6), true) }) }, i)))] })), agents.length > 0 && (() => {
|
|
5479
5508
|
const done = agents.filter(a => a.status !== 'running').length;
|
|
5480
5509
|
const failed = agents.filter(a => a.status === 'failed').length;
|
|
@@ -5504,7 +5533,7 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
5504
5533
|
: elapsedMs > 8_000 ? `no response from ${cfg.provider} yet`
|
|
5505
5534
|
: 'starting', thinkingRef.current.chars > 0 && Date.now() - thinkingRef.current.at < 2_000
|
|
5506
5535
|
? ` | thinking, ${fmtTokens(Math.round(thinkingRef.current.chars / 4))} reasoning tokens`
|
|
5507
|
-
: '', ")"] })] }) }), showThinking && thinkingTextRef.current.trim() !== '' && (_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: thinkingTail(thinkingTextRef.current, THINKING_LINES, barWidth - 6).map((line, i) => (_jsx(Text, { color: DIM, italic: true, children: line }, i))) })), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: DIM, children: "esc to cancel and keep your input \u00B7 ctrl+c to exit" }) })] })) }), usagePane && (() => {
|
|
5536
|
+
: '', ")"] })] }) }), showThinking && !ask && thinkingTextRef.current.trim() !== '' && (_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: thinkingTail(thinkingTextRef.current, THINKING_LINES, barWidth - 6).map((line, i) => (_jsx(Text, { color: DIM, italic: true, children: line }, i))) })), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: DIM, children: "esc to cancel and keep your input \u00B7 ctrl+c to exit" }) })] })) }), usagePane && (() => {
|
|
5508
5537
|
const width = Math.min(barWidth, usagePane === 'settings' ? 96 : 66);
|
|
5509
5538
|
const inner = width - 4;
|
|
5510
5539
|
const BAR_W = Math.max(12, inner - 14);
|
|
@@ -5844,7 +5873,14 @@ export async function runInkChatMode(config) {
|
|
|
5844
5873
|
// Frames go out bracketed as synchronized updates, so the erase-then-redraw Ink performs on
|
|
5845
5874
|
// every repaint is composited by the terminal instead of being shown as a blank flash. That is
|
|
5846
5875
|
// what allows the animation to run at FRAME_MS; see frame-sync.ts.
|
|
5847
|
-
|
|
5876
|
+
/*
|
|
5877
|
+
* Asked before Ink starts, because the reply arrives on stdin and Ink owns stdin from here.
|
|
5878
|
+
*
|
|
5879
|
+
* The answer sets the animation's floor: fast where a redraw is composited, slow where every
|
|
5880
|
+
* frame is a visible blink of the whole screen. 150ms at worst, once, on startup.
|
|
5881
|
+
*/
|
|
5882
|
+
const synchronizedFrames = await probeSynchronizedOutput().catch(() => false);
|
|
5883
|
+
const app = render(_jsx(App, { config: config, synchronizedFrames: synchronizedFrames, clearFrame: () => inkClear?.() }), { stdout: withSynchronizedFrames(process.stdout),
|
|
5848
5884
|
exitOnCtrlC: false, patchConsole: false });
|
|
5849
5885
|
inkClear = app.clear;
|
|
5850
5886
|
// The engine is imported on demand so the banner and the prompt are not waiting on the OpenAI
|