@vincemakes/kiso-code 0.16.2 → 0.16.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/dist/chat.js +8 -4
- package/dist/dispatch.js +6 -2
- package/dist/index.js +70 -39
- package/dist/resume-tail.d.ts +1 -1
- package/dist/resume-tail.js +16 -3
- package/package.json +15 -15
package/dist/chat.js
CHANGED
|
@@ -378,7 +378,7 @@ function taskVerdictWords(kind) {
|
|
|
378
378
|
}
|
|
379
379
|
/** W21 — the panel view for a permission_requested: the rule line (the
|
|
380
380
|
* why-asked speaker + the §3.5 fix hint), the toolTarget title, the
|
|
381
|
-
* "
|
|
381
|
+
* "⏸ run paused" status, and the ALWAYS-verbose args. */
|
|
382
382
|
function approvalView(name, ev, amended = false) {
|
|
383
383
|
const speaker = ev.speaker ?? "kiso";
|
|
384
384
|
const input = ev.input ?? {};
|
|
@@ -396,7 +396,7 @@ function approvalView(name, ev, amended = false) {
|
|
|
396
396
|
speaker,
|
|
397
397
|
...(hint !== undefined ? { hint } : {}),
|
|
398
398
|
...(risk !== null ? { riskHint: risk } : {}),
|
|
399
|
-
statusText: "
|
|
399
|
+
statusText: "⏸ run paused",
|
|
400
400
|
args: approvalArgs(name, input),
|
|
401
401
|
fallbackQuestion: `approve ${escapeTerminal(name)}? (y/n) `,
|
|
402
402
|
// TUI2-R3v2 ③: the v4 frame's "(amended)" marker. It says WHY this
|
|
@@ -549,7 +549,9 @@ submitTurn) {
|
|
|
549
549
|
// user's words. Provenance is honest on screen, not only in
|
|
550
550
|
// the log.
|
|
551
551
|
if (ev.source === "system") {
|
|
552
|
-
|
|
552
|
+
// R2 (law 1.3): the ◆ said nothing the words did not. What
|
|
553
|
+
// makes this row honest is that it NAMES itself machinery.
|
|
554
|
+
body.notice("verification pass");
|
|
553
555
|
body.notice(` ${typeof ev.content === "string" ? ev.content : ""}`);
|
|
554
556
|
break;
|
|
555
557
|
}
|
|
@@ -761,7 +763,9 @@ submitTurn) {
|
|
|
761
763
|
// recap (the truncation-guard philosophy: the cut is visible
|
|
762
764
|
// in the scrollback, the model's own text intact).
|
|
763
765
|
if (ev.outcome.kind === "max_tokens") {
|
|
764
|
-
|
|
766
|
+
// R2 (law 1.1): the notice was wearing a box corner. A notice
|
|
767
|
+
// is a sentence addressed to a human; it needs no edge.
|
|
768
|
+
body.notice('answer truncated at max_tokens — say "continue" to finish');
|
|
765
769
|
}
|
|
766
770
|
bodyLog(renderRecap({
|
|
767
771
|
seconds: Math.round((Date.now() - turnStart) / 1000),
|
package/dist/dispatch.js
CHANGED
|
@@ -203,7 +203,11 @@ export function dispatch(line, ctx) {
|
|
|
203
203
|
// user who sees it is exactly the user who needs
|
|
204
204
|
// the path spelled out
|
|
205
205
|
...(names.length === 0 ? { emptyNote: "no profiles — define models in ~/.kiso/config.json" } : {}),
|
|
206
|
-
},
|
|
206
|
+
},
|
|
207
|
+
// DC-12 (design §4): a panel WAITING ON A HUMAN says ⏸. The
|
|
208
|
+
// else-branch keeps ▸ — it names the current tier, which is
|
|
209
|
+
// what ▸ means everywhere else.
|
|
210
|
+
ctx.isRunning() ? "⏸ run paused" : `▸ ${getMode()}`), (v) => resolve(v.action === "picked" ? v.result : null));
|
|
207
211
|
});
|
|
208
212
|
ctx.paintIdle();
|
|
209
213
|
if (picked === null) {
|
|
@@ -365,7 +369,7 @@ export function dispatch(line, ctx) {
|
|
|
365
369
|
const elapsed = compactStart > 0 ? ((Date.now() - compactStart) / 1000).toFixed(1) : "?";
|
|
366
370
|
// a non-null result implies onStart ran — the "?" is
|
|
367
371
|
// reachable only at the type level
|
|
368
|
-
body.notice(`[/compact]
|
|
372
|
+
body.notice(`[/compact] ✦ compacted · ${compactInfo?.rounds ?? "?"} rounds → 1 summary · saved ~${kUnit(result.savedTokens)} · ctx ${ctxBefore ?? "?"}% → ${ctxAfter}% · ${elapsed}s`);
|
|
369
373
|
}
|
|
370
374
|
}
|
|
371
375
|
catch (err) {
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import { newSessionId } from "./session-id.js";
|
|
|
28
28
|
import { createInterface } from "node:readline";
|
|
29
29
|
import { fileURLToPath } from "node:url";
|
|
30
30
|
import { join } from "node:path";
|
|
31
|
-
import {
|
|
31
|
+
import { Body, Editor, bannerLines, currentGround, resolveGround, setGround, escapeTerminal, extensionsBannerText, idColumn, idleStatus, interactivePrompt, palette, renderSessionLine, sessionListFooter, sessionListRow } from "@vincemakes/kiso-tui";
|
|
32
32
|
import { createAgent, disposeExtensions, loadExtensions, loadProjectExtensions, SessionStore, } from "@vincemakes/kiso-runtime";
|
|
33
33
|
import { createFauxProvider } from "@vincemakes/kiso-evals";
|
|
34
34
|
import { createCodingTools } from "@vincemakes/kiso-tools-node";
|
|
@@ -43,7 +43,7 @@ import { loadProjectConfig, loadUserConfig, mergeConfigs, resolveAutoCompact, re
|
|
|
43
43
|
import { resume } from "./resume.js";
|
|
44
44
|
import { resumeTail } from "./resume-tail.js";
|
|
45
45
|
import { armByteTrace } from "./byte-trace.js";
|
|
46
|
-
import { tmpdir } from "node:os";
|
|
46
|
+
import { tmpdir, homedir } from "node:os";
|
|
47
47
|
import { clipboardImage } from "./clipboard.js";
|
|
48
48
|
import { collectSessionCards, projectSessionCard } from "./session-cards.js";
|
|
49
49
|
// The moved exports stay reachable from this entry — the test imports
|
|
@@ -233,10 +233,52 @@ function makeLineInput() {
|
|
|
233
233
|
if (process.stdin.isTTY) {
|
|
234
234
|
const editor = new Editor(() => (dock.active ? dock.redraw() : editor.selfRender()));
|
|
235
235
|
editor.enter();
|
|
236
|
+
// DC-3: ask the terminal what its background is, and paint the
|
|
237
|
+
// first frame without waiting for the answer.
|
|
238
|
+
//
|
|
239
|
+
// Every grey in the palette is a claim about the background, and
|
|
240
|
+
// kiso had never established one — inline code was chosen on a dark
|
|
241
|
+
// terminal and measured 1.54:1 on a white one. The question is one
|
|
242
|
+
// write; the answer comes back through the editor, which is already
|
|
243
|
+
// the process's single reader of stdin (DC-7 taught it to swallow
|
|
244
|
+
// OSC rather than type it into the draft). No timer, no second
|
|
245
|
+
// reader, nothing blocking: an answer that never arrives leaves the
|
|
246
|
+
// ground `unknown`, and `unknown` is a supported palette, not a
|
|
247
|
+
// failure — see packages/tui-cells/src/ground.ts.
|
|
248
|
+
// DC-14 (P1): the ladder is walked ONCE AT STARTUP, before the query
|
|
249
|
+
// and independently of it. It used to be walked only inside the
|
|
250
|
+
// reply callback below — so on a terminal that does not answer OSC
|
|
251
|
+
// 11 (tmux does not forward the reply by default), rung 1 never
|
|
252
|
+
// ran: `KISO_THEME=dark` is SETTLED as "an explicit answer always
|
|
253
|
+
// wins" and won nothing. Rung 3 was worse than dead — `COLORFGBG`
|
|
254
|
+
// exists FOR the terminals without OSC 11, and could only ever be
|
|
255
|
+
// consulted when an OSC reply had arrived and been malformed. With
|
|
256
|
+
// §3.2 recording that rung 2 is unmeasured, that was the mainline.
|
|
257
|
+
setGround(resolveGround({ theme: process.env.KISO_THEME, colorfgbg: process.env.COLORFGBG }));
|
|
258
|
+
// the reply, when there is one, re-walks the ladder WITH it — and
|
|
259
|
+
// `theme` goes in first again, so an explicit answer still wins.
|
|
260
|
+
editor.onOsc((reply) => {
|
|
261
|
+
const next = resolveGround({ theme: process.env.KISO_THEME, osc: reply, colorfgbg: process.env.COLORFGBG });
|
|
262
|
+
if (next === currentGround())
|
|
263
|
+
return;
|
|
264
|
+
setGround(next);
|
|
265
|
+
body.onGroundChange();
|
|
266
|
+
});
|
|
267
|
+
// The query is COLOUR machinery, so it obeys the colour gate: a piped
|
|
268
|
+
// stdout and NO_COLOR both carry zero ANSI, and an OSC written into a
|
|
269
|
+
// pipe would be the first byte to break that. `palette().bold` is the
|
|
270
|
+
// same test the dock activates on — one gate, not a second opinion.
|
|
271
|
+
if (palette().bold !== "" && process.stdout.isTTY)
|
|
272
|
+
process.stdout.write("\x1b]11;?\x07");
|
|
236
273
|
// W6: the box's prompt goes light — "› " (the box already says
|
|
237
274
|
// "input lives here"; the line-mode path keeps the brick ▌, so
|
|
238
275
|
// pipe bytes do not change)
|
|
239
|
-
|
|
276
|
+
// R2 (owner, 2026-08-27): no prompt glyph. The cursor sits at column
|
|
277
|
+
// one, between the two rules — the rules already say "input lives
|
|
278
|
+
// here", which is the argument W6 made for the box and the only part
|
|
279
|
+
// of it that survives. A prompt character is a third thing saying
|
|
280
|
+
// the same thing, and it cost the row a column.
|
|
281
|
+
dock.bindInput(() => editor.dockState(), "");
|
|
240
282
|
// TUI2-R3v2 ②: the click hit-test's wiring — the compositor places
|
|
241
283
|
// the panel's option rows, so the compositor is what the editor asks
|
|
242
284
|
// where they are. Neither side computes the other's geometry.
|
|
@@ -274,38 +316,19 @@ function extensionsBanner(resume = []) {
|
|
|
274
316
|
bodyLog(`${text}\n`);
|
|
275
317
|
return;
|
|
276
318
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
* bounded by the list, never the home. */
|
|
291
|
-
async function recentSessions(id, agent) {
|
|
292
|
-
const picked = agent
|
|
293
|
-
.sessions()
|
|
294
|
-
.filter((m) => m.id !== id)
|
|
295
|
-
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
296
|
-
.slice(0, 3);
|
|
297
|
-
const store = sessionStoreRef;
|
|
298
|
-
const out = [];
|
|
299
|
-
for (const m of picked) {
|
|
300
|
-
let badge;
|
|
301
|
-
if (store !== null) {
|
|
302
|
-
const session = await agent.session({ id: m.id, ...(acceptDrift() ? { acceptDrift: true } : {}) });
|
|
303
|
-
const card = projectSessionCard({ id: m.id, updatedAt: m.updatedAt, records: store.load(m.id), asks: session.pendingApprovals().length });
|
|
304
|
-
badge = BADGE_GLYPH[card.badge];
|
|
305
|
-
}
|
|
306
|
-
out.push({ title: m.title, events: m.events, runs: m.runs, updatedAt: m.updatedAt, ...(badge === undefined ? {} : { badge }) });
|
|
307
|
-
}
|
|
308
|
-
return out;
|
|
319
|
+
// R2: the opening answers the three questions a first screen is asked.
|
|
320
|
+
// The model and the tier come from the same state the status line reads
|
|
321
|
+
// (one source, so the two can never disagree); the workspace is the
|
|
322
|
+
// home-relative cwd, because `~/Desktop/devv/kiso` is what a human
|
|
323
|
+
// calls the place and `/Users/vinve/Desktop/devv/kiso` is what a
|
|
324
|
+
// filesystem calls it.
|
|
325
|
+
const home = homedir();
|
|
326
|
+
const cwd = process.cwd();
|
|
327
|
+
body.banner(VERSION, text.replace(/^ · /, ""), resume, {
|
|
328
|
+
model: agentModel,
|
|
329
|
+
mode: getMode() === "plan" ? "plan (read-only)" : getMode(),
|
|
330
|
+
cwd: cwd.startsWith(home) ? `~${cwd.slice(home.length)}` : cwd,
|
|
331
|
+
});
|
|
309
332
|
}
|
|
310
333
|
/**
|
|
311
334
|
* A area: the coding-agent system prompt — ONE constant, byte-stable for the
|
|
@@ -606,13 +629,21 @@ async function chatLoop(agent, firstId, input, autoCompact) {
|
|
|
606
629
|
// prompt inside a conversation with thousands of events — the
|
|
607
630
|
// durable log was right there and none of it was shown. Empty for
|
|
608
631
|
// a fresh session, so `kiso chat` is byte-identical.
|
|
609
|
-
for (const line of resumeTail(session.log.all))
|
|
632
|
+
for (const line of resumeTail(session.log.all, process.stdout.columns ?? 80))
|
|
610
633
|
bodyLog(line);
|
|
611
|
-
|
|
634
|
+
// R2 (owner, 2026-08-27): the resume list is NOT on the opening
|
|
635
|
+
// screen. `/resume` is where you go looking for a session; the
|
|
636
|
+
// opening's job is to say what THIS one is.
|
|
637
|
+
//
|
|
638
|
+
// It takes DC-8 with it: building those rows opened the three most
|
|
639
|
+
// recent sessions just to draw a badge, and `agent.session()`
|
|
640
|
+
// throws on profile drift — so one drifted session anywhere in the
|
|
641
|
+
// history stopped kiso from starting at all.
|
|
642
|
+
extensionsBanner();
|
|
612
643
|
}
|
|
613
644
|
else {
|
|
614
645
|
bodyLog(`session ${id} (switched — previous: ${prev}, /resume ${prev} returns)\n`);
|
|
615
|
-
for (const line of resumeTail(session.log.all))
|
|
646
|
+
for (const line of resumeTail(session.log.all, process.stdout.columns ?? 80))
|
|
616
647
|
bodyLog(line);
|
|
617
648
|
if (currentFaux)
|
|
618
649
|
session.setAdapter(createFauxProvider(readFauxScript().slice(fauxSkip(id))));
|
|
@@ -880,7 +911,7 @@ async function main() {
|
|
|
880
911
|
session.setAdapter(createFauxProvider(readFauxScript().slice(fauxSkip(id))));
|
|
881
912
|
// REL-0152-D5 — the same tail on the explicit-id form. NOT on
|
|
882
913
|
// the -p path above: that one's stdout is a machine's input.
|
|
883
|
-
for (const line of resumeTail(session.log.all))
|
|
914
|
+
for (const line of resumeTail(session.log.all, process.stdout.columns ?? 80))
|
|
884
915
|
bodyLog(line);
|
|
885
916
|
await resume(session, prompt, faux, input);
|
|
886
917
|
break;
|
package/dist/resume-tail.d.ts
CHANGED
package/dist/resume-tail.js
CHANGED
|
@@ -28,7 +28,7 @@ const clip = (s, n) => (s.length <= n ? s : `${s.slice(0, n - 1)}…`);
|
|
|
28
28
|
* prompt. Empty when there is nothing to show, and the caller prints
|
|
29
29
|
* nothing rather than an empty frame.
|
|
30
30
|
*/
|
|
31
|
-
export function resumeTail(events) {
|
|
31
|
+
export function resumeTail(events, W = 80) {
|
|
32
32
|
const turns = [];
|
|
33
33
|
let ask = null;
|
|
34
34
|
let reply = "";
|
|
@@ -51,9 +51,22 @@ export function resumeTail(events) {
|
|
|
51
51
|
return [];
|
|
52
52
|
const lines = [];
|
|
53
53
|
const skipped = turns.length - shown.length;
|
|
54
|
-
|
|
54
|
+
// R2 (law 1.1): the head is the ONE rule, labelled the way every
|
|
55
|
+
// other band on screen labels itself. It used to be `─ … ─`, a second
|
|
56
|
+
// divider weight that existed nowhere else in the product.
|
|
57
|
+
const label = skipped > 0 ? `resuming · ${turns.length} turns, showing the last ${shown.length}` : `resuming · ${turns.length} turn${turns.length === 1 ? "" : "s"}`;
|
|
58
|
+
const head = `\u254c\u254c\u254c ${label} `;
|
|
59
|
+
// the cut floor is the WIDTH, never a constant: `Math.max(4, W)` let a
|
|
60
|
+
// four-cell rule out at W=1..3, i.e. a chrome row wider than the
|
|
61
|
+
// terminal. Every glyph here is one cell (the label is ascii plus
|
|
62
|
+
// `·`), so a code-unit slice is a cell slice.
|
|
63
|
+
lines.push(`${head}${"\u254c".repeat(Math.max(1, W - head.length))}`.slice(0, Math.max(1, W)));
|
|
55
64
|
for (const t of shown) {
|
|
56
|
-
|
|
65
|
+
// the human's turn keeps the quote gutter — the one glyph in the
|
|
66
|
+
// product that means "these are somebody's exact words". The `\u203a`
|
|
67
|
+
// it used to carry is the composer's prompt, and the composer no
|
|
68
|
+
// longer has one.
|
|
69
|
+
lines.push(` \u2502 ${clip(oneLine(t.ask), 100)}`);
|
|
57
70
|
const body = oneLine(t.reply);
|
|
58
71
|
// A turn with no reply is a turn that was INTERRUPTED — the case
|
|
59
72
|
// resume exists for. Saying so beats printing a blank line.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.16.
|
|
4
|
-
"description": "kiso CLI
|
|
3
|
+
"version": "0.16.4",
|
|
4
|
+
"description": "kiso CLI — the durable coding agent that survives kill -9: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -18,19 +18,19 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-ask-ext": "0.16.
|
|
22
|
-
"@vincemakes/kiso-core": "0.16.
|
|
23
|
-
"@vincemakes/kiso-evals": "0.16.
|
|
24
|
-
"@vincemakes/kiso-mcp-ext": "0.16.
|
|
25
|
-
"@vincemakes/kiso-provider-anthropic": "0.16.
|
|
26
|
-
"@vincemakes/kiso-provider-openai": "0.16.
|
|
27
|
-
"@vincemakes/kiso-runtime": "0.16.
|
|
28
|
-
"@vincemakes/kiso-skills-ext": "0.16.
|
|
29
|
-
"@vincemakes/kiso-subagent-ext": "0.16.
|
|
30
|
-
"@vincemakes/kiso-task-ext": "0.16.
|
|
31
|
-
"@vincemakes/kiso-tools-node": "0.16.
|
|
32
|
-
"@vincemakes/kiso-tui": "0.16.
|
|
33
|
-
"@vincemakes/kiso-tui-cells": "0.16.
|
|
21
|
+
"@vincemakes/kiso-ask-ext": "0.16.4",
|
|
22
|
+
"@vincemakes/kiso-core": "0.16.4",
|
|
23
|
+
"@vincemakes/kiso-evals": "0.16.4",
|
|
24
|
+
"@vincemakes/kiso-mcp-ext": "0.16.4",
|
|
25
|
+
"@vincemakes/kiso-provider-anthropic": "0.16.4",
|
|
26
|
+
"@vincemakes/kiso-provider-openai": "0.16.4",
|
|
27
|
+
"@vincemakes/kiso-runtime": "0.16.4",
|
|
28
|
+
"@vincemakes/kiso-skills-ext": "0.16.4",
|
|
29
|
+
"@vincemakes/kiso-subagent-ext": "0.16.4",
|
|
30
|
+
"@vincemakes/kiso-task-ext": "0.16.4",
|
|
31
|
+
"@vincemakes/kiso-tools-node": "0.16.4",
|
|
32
|
+
"@vincemakes/kiso-tui": "0.16.4",
|
|
33
|
+
"@vincemakes/kiso-tui-cells": "0.16.4"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.1.2",
|