@vincemakes/kiso-code 0.1.48 → 0.2.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/chat.d.ts +40 -0
- package/dist/chat.js +80 -19
- package/dist/diff.d.ts +32 -0
- package/dist/diff.js +122 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/index.js +25 -2
- package/package.json +13 -13
package/dist/chat.d.ts
CHANGED
|
@@ -32,6 +32,46 @@ export declare function contextWindowTokens(): number;
|
|
|
32
32
|
* the model window. Marked ~ everywhere it is shown; no counting API.
|
|
33
33
|
*/
|
|
34
34
|
export declare function estimateCtxRatio(session: AgentSession): number;
|
|
35
|
+
/** E2 (1.3.0) — the CLI's usage consumer: one event in, the RunUsage the
|
|
36
|
+
* status line and recap render plus the miss estimate. `total` is the
|
|
37
|
+
* carrier for the NEXT turn's miss estimate — the overlap of consecutive
|
|
38
|
+
* prompts is a total-side quantity, never a fresh delta. */
|
|
39
|
+
export interface UsageDelta {
|
|
40
|
+
readonly usage: RunUsage;
|
|
41
|
+
/** The canonical total (fresh + cache) — the miss estimate's carrier.
|
|
42
|
+
* null when the event carried no usage (the canonical total of an
|
|
43
|
+
* unknown event is 0, and a 0 carrier keeps the estimate below the
|
|
44
|
+
* floor — the old consumer's carrier stayed null forever; this one
|
|
45
|
+
* recovers on the next known event). */
|
|
46
|
+
readonly total: number | null;
|
|
47
|
+
readonly missed: number | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The CLI's usage consumer (E2 1.3.0, the R2a-1 ruling 2026-08-13) — the
|
|
51
|
+
* HEAL: the mixed-convention consumer is now CANONICAL at the route (the
|
|
52
|
+
* accounting boundary), the same derivation the trace block carries.
|
|
53
|
+
*
|
|
54
|
+
* EXISTING-BEHAVIOR CHANGE — declared, never a silent side-fix:
|
|
55
|
+
* - openai-compat: `in` was the provider-raw TOTAL (fresh + cache); it is
|
|
56
|
+
* now the canonical FRESH count. The >100% cache-ratio disease: raw
|
|
57
|
+
* {input 111, cacheRead 1024} previously rendered "in 111" and the
|
|
58
|
+
* recap's cache % (then cache/in) read 923%; now "in 0" and the recap
|
|
59
|
+
* divides cache by the TOTAL (in + cache — T5) — never > 100%.
|
|
60
|
+
* - the miss estimate is numerically IDENTICAL on openai-compat: its old
|
|
61
|
+
* `in` WAS the total, and the carrier is the canonical total
|
|
62
|
+
* (input + cacheRead), which equals the raw total by construction.
|
|
63
|
+
* - anthropic: `in` was already fresh — unchanged; the miss estimate was
|
|
64
|
+
* min-of-fresh-deltas − cacheRead (always below the floor — silent);
|
|
65
|
+
* it is now min-of-totals, the semantics the openai-compat side always
|
|
66
|
+
* had (a fix, and it can fire).
|
|
67
|
+
* - the unknown-usage carrier: the old consumer's null carrier killed the
|
|
68
|
+
* miss signal forever; the canonical total of an unknown event is 0, so
|
|
69
|
+
* the signal recovers on the next known event.
|
|
70
|
+
* The route key mirrors the trace path's fallback by construction: an
|
|
71
|
+
* absent provider resolves like the tracer's "adapter" identity — the
|
|
72
|
+
* total convention (INPUT_CONVENTIONS), never a crash.
|
|
73
|
+
*/
|
|
74
|
+
export declare function usageFromEvent(route: string | undefined, ev: import("@vincemakes/kiso-core").Usage, prevTotal: number | null): UsageDelta;
|
|
35
75
|
/** v2b: the spinner merged into the STATUS BAR (the v2a standalone glyph
|
|
36
76
|
* is gone) — docked only, 200ms rotation between the request and the
|
|
37
77
|
* first event. */
|
package/dist/chat.js
CHANGED
|
@@ -8,6 +8,7 @@ import { readFileSync } from "node:fs";
|
|
|
8
8
|
import { escapeTerminal, kUnit, palette, renderEvent, renderRecap, toolTarget, } from "@vincemakes/kiso-tui";
|
|
9
9
|
import { editFileDiff, writeFileDiff } from "@vincemakes/kiso-tui";
|
|
10
10
|
import { canonicalTargetPath } from "@vincemakes/kiso-tools-node";
|
|
11
|
+
import { canonicalizeUsage } from "@vincemakes/kiso-runtime";
|
|
11
12
|
import { dispatch } from "./dispatch.js";
|
|
12
13
|
import { agentModel, body, bodyLog, configuredWindow, dock } from "./state.js";
|
|
13
14
|
import { addDontAskAgainRule, askPanel, fixHintFor, pendingAsk, resolveUncertains } from "./trust-ui.js";
|
|
@@ -47,6 +48,49 @@ export function estimateCtxRatio(session) {
|
|
|
47
48
|
const chars = JSON.stringify(projected).length;
|
|
48
49
|
return chars / 4 / contextWindowTokens();
|
|
49
50
|
}
|
|
51
|
+
/** R-C item 4: the per-turn cache miss — the overlap with the previous
|
|
52
|
+
* prompt that SHOULD have been cached but was re-sent uncached:
|
|
53
|
+
* missed = min(prevIn, in) − cacheRead. Below the 1024-token floor
|
|
54
|
+
* (Anthropic's minimum cacheable block) it is noise — not surfaced. */
|
|
55
|
+
const CACHE_MISS_FLOOR = 1024;
|
|
56
|
+
/**
|
|
57
|
+
* The CLI's usage consumer (E2 1.3.0, the R2a-1 ruling 2026-08-13) — the
|
|
58
|
+
* HEAL: the mixed-convention consumer is now CANONICAL at the route (the
|
|
59
|
+
* accounting boundary), the same derivation the trace block carries.
|
|
60
|
+
*
|
|
61
|
+
* EXISTING-BEHAVIOR CHANGE — declared, never a silent side-fix:
|
|
62
|
+
* - openai-compat: `in` was the provider-raw TOTAL (fresh + cache); it is
|
|
63
|
+
* now the canonical FRESH count. The >100% cache-ratio disease: raw
|
|
64
|
+
* {input 111, cacheRead 1024} previously rendered "in 111" and the
|
|
65
|
+
* recap's cache % (then cache/in) read 923%; now "in 0" and the recap
|
|
66
|
+
* divides cache by the TOTAL (in + cache — T5) — never > 100%.
|
|
67
|
+
* - the miss estimate is numerically IDENTICAL on openai-compat: its old
|
|
68
|
+
* `in` WAS the total, and the carrier is the canonical total
|
|
69
|
+
* (input + cacheRead), which equals the raw total by construction.
|
|
70
|
+
* - anthropic: `in` was already fresh — unchanged; the miss estimate was
|
|
71
|
+
* min-of-fresh-deltas − cacheRead (always below the floor — silent);
|
|
72
|
+
* it is now min-of-totals, the semantics the openai-compat side always
|
|
73
|
+
* had (a fix, and it can fire).
|
|
74
|
+
* - the unknown-usage carrier: the old consumer's null carrier killed the
|
|
75
|
+
* miss signal forever; the canonical total of an unknown event is 0, so
|
|
76
|
+
* the signal recovers on the next known event.
|
|
77
|
+
* The route key mirrors the trace path's fallback by construction: an
|
|
78
|
+
* absent provider resolves like the tracer's "adapter" identity — the
|
|
79
|
+
* total convention (INPUT_CONVENTIONS), never a crash.
|
|
80
|
+
*/
|
|
81
|
+
export function usageFromEvent(route, ev, prevTotal) {
|
|
82
|
+
const c = canonicalizeUsage(route ?? "adapter", ev);
|
|
83
|
+
const total = c.input + c.cacheRead + (c.cacheWrite ?? 0);
|
|
84
|
+
let missed = null;
|
|
85
|
+
// R-C item 4: min(prevTotal, total) is the part that could have been
|
|
86
|
+
// cached; what cacheRead did NOT cover is the miss. A below-floor or
|
|
87
|
+
// non-positive difference is noise — not surfaced.
|
|
88
|
+
if (prevTotal !== null) {
|
|
89
|
+
const m = Math.min(prevTotal, total) - c.cacheRead;
|
|
90
|
+
missed = m > CACHE_MISS_FLOOR ? m : null;
|
|
91
|
+
}
|
|
92
|
+
return { usage: { in: c.input, out: c.output, cache: c.cacheRead, known: ev.known }, total, missed };
|
|
93
|
+
}
|
|
50
94
|
/** v2b: the spinner merged into the STATUS BAR (the v2a standalone glyph
|
|
51
95
|
* is gone) — docked only, 200ms rotation between the request and the
|
|
52
96
|
* first event. */
|
|
@@ -212,12 +256,7 @@ export async function consumeRun(session, run, input, turnNo, faux, statusCb,
|
|
|
212
256
|
submitTurn) {
|
|
213
257
|
let last;
|
|
214
258
|
let usage = { in: null, out: null, cache: null, known: false };
|
|
215
|
-
|
|
216
|
-
// prompt that SHOULD have been cached but was re-sent uncached:
|
|
217
|
-
// missed = min(prevIn, in) − cacheRead. Below the 1024-token floor
|
|
218
|
-
// (Anthropic's minimum cacheable block) it is noise — not surfaced.
|
|
219
|
-
const CACHE_MISS_FLOOR = 1024;
|
|
220
|
-
let prevIn = null;
|
|
259
|
+
let prevTotal = null;
|
|
221
260
|
let missed = null;
|
|
222
261
|
// v3 §02: the recap line derives ENTIRELY from the local event stream
|
|
223
262
|
// (zero tokens) — wall seconds, tool/edit counts, usage, ctx left.
|
|
@@ -297,14 +336,10 @@ submitTurn) {
|
|
|
297
336
|
body.textEnd();
|
|
298
337
|
break;
|
|
299
338
|
case "usage": {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const m = Math.min(prevIn, usage.in) - usage.cache;
|
|
305
|
-
missed = m > CACHE_MISS_FLOOR ? m : null;
|
|
306
|
-
}
|
|
307
|
-
prevIn = usage.in;
|
|
339
|
+
const delta = usageFromEvent(session.provider, ev, prevTotal);
|
|
340
|
+
usage = delta.usage;
|
|
341
|
+
prevTotal = delta.total;
|
|
342
|
+
missed = delta.missed;
|
|
308
343
|
statusCb?.(usage, estimateCtxRatio(session));
|
|
309
344
|
break;
|
|
310
345
|
}
|
|
@@ -443,6 +478,23 @@ submitTurn) {
|
|
|
443
478
|
export async function chat(session, faux, input, autoCompact) {
|
|
444
479
|
let currentRun = null;
|
|
445
480
|
let cancelled = false;
|
|
481
|
+
// E group (the graceful-exit gate ③, R-G 0.1.48): the terminal can
|
|
482
|
+
// close MID-run — the stream 'end' fires while currentRun is set, so
|
|
483
|
+
// the EOT callback defers. eotSeen remembers it; each run's end
|
|
484
|
+
// re-evaluates the exit condition (the safe point), so the release
|
|
485
|
+
// always runs.
|
|
486
|
+
let eotSeen = false;
|
|
487
|
+
/** The exit condition, shared by the EOT callback and the deferred
|
|
488
|
+
* re-check: no pending ask, an empty line. currentRun is checked by
|
|
489
|
+
* the callers (the callback when 'end' fires; the run-end re-checks
|
|
490
|
+
* run only after currentRun was nulled). */
|
|
491
|
+
const exitAtEmptyPrompt = () => {
|
|
492
|
+
if (pendingAsk !== null || input.line() !== "")
|
|
493
|
+
return;
|
|
494
|
+
cancelled = true;
|
|
495
|
+
console.log("\n[exit requested]");
|
|
496
|
+
input.close();
|
|
497
|
+
};
|
|
446
498
|
const turn = (text) => new Promise((resolve, reject) => {
|
|
447
499
|
queued = Math.max(0, queued - 1); // a queued turn starts
|
|
448
500
|
const run = session.run(text);
|
|
@@ -471,6 +523,11 @@ export async function chat(session, faux, input, autoCompact) {
|
|
|
471
523
|
// chat to main's finally/catch, never an orphaned
|
|
472
524
|
// unhandled rejection from the IIFE.
|
|
473
525
|
failOnFauxExhaustion(last, faux, input);
|
|
526
|
+
// E group (the graceful-exit gate ③): the fd may have closed
|
|
527
|
+
// mid-run — the run's end is the safe point for the deferred
|
|
528
|
+
// exit, so the release always runs.
|
|
529
|
+
if (eotSeen)
|
|
530
|
+
exitAtEmptyPrompt();
|
|
474
531
|
// round 8: after EVERY turn the prompt is re-armed — the human
|
|
475
532
|
// never types blind after the first turn.
|
|
476
533
|
input.prompt();
|
|
@@ -517,11 +574,11 @@ export async function chat(session, faux, input, autoCompact) {
|
|
|
517
574
|
}
|
|
518
575
|
});
|
|
519
576
|
input.onEot(() => {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
577
|
+
// E group (the graceful-exit gate ③): the 'end' may fire MID-run —
|
|
578
|
+
// the exit defers to the run's end (the re-checks below).
|
|
579
|
+
eotSeen = true;
|
|
580
|
+
if (!currentRun)
|
|
581
|
+
exitAtEmptyPrompt();
|
|
525
582
|
});
|
|
526
583
|
input.onEscape(() => {
|
|
527
584
|
if (currentRun) {
|
|
@@ -662,6 +719,10 @@ export async function chat(session, faux, input, autoCompact) {
|
|
|
662
719
|
const last = await consumeRun(session, recoveryRun, input, turnNo, faux, statusCb, submitTurn);
|
|
663
720
|
currentRun = null;
|
|
664
721
|
failOnFauxExhaustion(last, faux, input);
|
|
722
|
+
// E group (the graceful-exit gate ③): the same deferred re-check
|
|
723
|
+
// as the turn path — the recovery run's end is also a safe point.
|
|
724
|
+
if (eotSeen)
|
|
725
|
+
exitAtEmptyPrompt();
|
|
665
726
|
maybeAutoCompact(); // the ergonomics batch C8: the recovery run ended too — same check (awaited by the exit re-await)
|
|
666
727
|
}
|
|
667
728
|
if (cancelled) {
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2e — the diff renderer: edit/write changes as inline ± lines, zero
|
|
3
|
+
* dependencies, no syntax highlighting (the spec's scope line). Shown at
|
|
4
|
+
* the approval moment ONLY — the frozen summary stays one line (v2d's
|
|
5
|
+
* anti-leak principle), /last has the full data.
|
|
6
|
+
*
|
|
7
|
+
* edit_file diffs IN PLACE (the search→replace windows are known — no
|
|
8
|
+
* general engine needed); write_file does a row-level LCS over the old
|
|
9
|
+
* file (small files are the target). Context: 2 rows each side. The
|
|
10
|
+
* RENDERER truncates (18 head + 18 tail + "… N lines"); the stats come
|
|
11
|
+
* from the full diff.
|
|
12
|
+
*/
|
|
13
|
+
/** The diff block's per-row kind. */
|
|
14
|
+
export type DiffLine = {
|
|
15
|
+
kind: "-" | "+" | " ";
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
export interface DiffResult {
|
|
19
|
+
/** The FULL diff (with context, not truncated) — the display truncates. */
|
|
20
|
+
lines: DiffLine[];
|
|
21
|
+
added: number;
|
|
22
|
+
removed: number;
|
|
23
|
+
}
|
|
24
|
+
/** The RENDERER's truncation: head + "… N lines (/last for full)" + tail. */
|
|
25
|
+
export declare function truncateDiff(diff: DiffLine[]): DiffLine[];
|
|
26
|
+
/** edit_file: the search→replace windows replace in place — the changed
|
|
27
|
+
* region is KNOWN, so the diff is the old window vs the new window,
|
|
28
|
+
* context from the surrounding file. */
|
|
29
|
+
export declare function editFileDiff(oldContent: string, search: string, replace: string): DiffResult;
|
|
30
|
+
/** write_file: a new file is all +; an existing file diffs row-level
|
|
31
|
+
* against its old content. */
|
|
32
|
+
export declare function writeFileDiff(oldContent: string | null, newContent: string): DiffResult;
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2e — the diff renderer: edit/write changes as inline ± lines, zero
|
|
3
|
+
* dependencies, no syntax highlighting (the spec's scope line). Shown at
|
|
4
|
+
* the approval moment ONLY — the frozen summary stays one line (v2d's
|
|
5
|
+
* anti-leak principle), /last has the full data.
|
|
6
|
+
*
|
|
7
|
+
* edit_file diffs IN PLACE (the search→replace windows are known — no
|
|
8
|
+
* general engine needed); write_file does a row-level LCS over the old
|
|
9
|
+
* file (small files are the target). Context: 2 rows each side. The
|
|
10
|
+
* RENDERER truncates (18 head + 18 tail + "… N lines"); the stats come
|
|
11
|
+
* from the full diff.
|
|
12
|
+
*/
|
|
13
|
+
/** A line-level LCS diff — the classic two-row DP, ~small inputs. */
|
|
14
|
+
function lcsDiff(oldLines, newLines) {
|
|
15
|
+
const n = oldLines.length;
|
|
16
|
+
const m = newLines.length;
|
|
17
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
18
|
+
for (let i = n - 1; i >= 0; i -= 1) {
|
|
19
|
+
for (let j = m - 1; j >= 0; j -= 1) {
|
|
20
|
+
dp[i][j] = oldLines[i] === newLines[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const out = [];
|
|
24
|
+
let i = 0;
|
|
25
|
+
let j = 0;
|
|
26
|
+
while (i < n && j < m) {
|
|
27
|
+
if (oldLines[i] === newLines[j]) {
|
|
28
|
+
out.push({ kind: " ", text: oldLines[i] });
|
|
29
|
+
i += 1;
|
|
30
|
+
j += 1;
|
|
31
|
+
}
|
|
32
|
+
else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
33
|
+
out.push({ kind: "-", text: oldLines[i] });
|
|
34
|
+
i += 1;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
out.push({ kind: "+", text: newLines[j] });
|
|
38
|
+
j += 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
while (i < n) {
|
|
42
|
+
out.push({ kind: "-", text: oldLines[i] });
|
|
43
|
+
i += 1;
|
|
44
|
+
}
|
|
45
|
+
while (j < m) {
|
|
46
|
+
out.push({ kind: "+", text: newLines[j] });
|
|
47
|
+
j += 1;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/** Keep 2 context rows around each change — the unified-style window. */
|
|
52
|
+
function withContext(diff) {
|
|
53
|
+
const out = [];
|
|
54
|
+
let lastAdded = -10;
|
|
55
|
+
for (let k = 0; k < diff.length; k += 1) {
|
|
56
|
+
if (diff[k].kind === " ")
|
|
57
|
+
continue;
|
|
58
|
+
const from = Math.max(0, k - 2);
|
|
59
|
+
const to = Math.min(diff.length - 1, k + 2);
|
|
60
|
+
for (let c = from; c <= to; c += 1) {
|
|
61
|
+
if (c > lastAdded) {
|
|
62
|
+
out.push(diff[c]);
|
|
63
|
+
lastAdded = c;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
lastAdded = to;
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
const MAX_DIFF_LINES = 40; // the RENDERED cap
|
|
71
|
+
const TRUNCATE_KEEP = 18;
|
|
72
|
+
/** The RENDERER's truncation: head + "… N lines (/last for full)" + tail. */
|
|
73
|
+
export function truncateDiff(diff) {
|
|
74
|
+
if (diff.length <= MAX_DIFF_LINES)
|
|
75
|
+
return diff;
|
|
76
|
+
const omitted = diff.length - 2 * TRUNCATE_KEEP;
|
|
77
|
+
return [
|
|
78
|
+
...diff.slice(0, TRUNCATE_KEEP),
|
|
79
|
+
{ kind: " ", text: `… ${omitted} lines (/last for full)` },
|
|
80
|
+
...diff.slice(diff.length - TRUNCATE_KEEP),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
function stats(diff) {
|
|
84
|
+
let added = 0;
|
|
85
|
+
let removed = 0;
|
|
86
|
+
for (const d of diff) {
|
|
87
|
+
if (d.kind === "+")
|
|
88
|
+
added += 1;
|
|
89
|
+
else if (d.kind === "-")
|
|
90
|
+
removed += 1;
|
|
91
|
+
}
|
|
92
|
+
return { added, removed };
|
|
93
|
+
}
|
|
94
|
+
/** edit_file: the search→replace windows replace in place — the changed
|
|
95
|
+
* region is KNOWN, so the diff is the old window vs the new window,
|
|
96
|
+
* context from the surrounding file. */
|
|
97
|
+
export function editFileDiff(oldContent, search, replace) {
|
|
98
|
+
const oldLines = oldContent.split("\n");
|
|
99
|
+
const searchLines = search.split("\n");
|
|
100
|
+
const replaceLines = replace.split("\n");
|
|
101
|
+
// Locate the search window (the first occurrence — the edit tool's own
|
|
102
|
+
// semantics); no occurrence → the whole file is the old side.
|
|
103
|
+
let at = -1;
|
|
104
|
+
for (let i = 0; i + searchLines.length <= oldLines.length; i += 1) {
|
|
105
|
+
if (oldLines.slice(i, i + searchLines.length).join("\n") === search) {
|
|
106
|
+
at = i;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const lines = at < 0 ? withContext(lcsDiff(oldLines, replaceLines)) : withContext(lcsDiff(oldLines, [...oldLines.slice(0, at), ...replaceLines, ...oldLines.slice(at + searchLines.length)]));
|
|
111
|
+
return { lines, ...stats(lines) };
|
|
112
|
+
}
|
|
113
|
+
/** write_file: a new file is all +; an existing file diffs row-level
|
|
114
|
+
* against its old content. */
|
|
115
|
+
export function writeFileDiff(oldContent, newContent) {
|
|
116
|
+
if (oldContent === null) {
|
|
117
|
+
const lines = newContent.split("\n").map((text) => ({ kind: "+", text }));
|
|
118
|
+
return { lines, added: lines.length, removed: 0 };
|
|
119
|
+
}
|
|
120
|
+
const lines = withContext(lcsDiff(oldContent.split("\n"), newContent.split("\n")));
|
|
121
|
+
return { lines, ...stats(lines) };
|
|
122
|
+
}
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* turns. The bodies moved verbatim from chat()'s closure; chat provides
|
|
4
4
|
* the context (the chain, the run state, the prompt arming).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import type { AgentSession } from "@vincemakes/kiso-runtime";
|
|
7
7
|
import { type LineInput } from "./state.js";
|
|
8
8
|
/** Everything dispatch touches that chat() owns. */
|
|
9
9
|
export interface DispatchCtx {
|
package/dist/dispatch.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* the context (the chain, the run state, the prompt arming).
|
|
5
5
|
*/
|
|
6
6
|
import { escapeTerminal, kUnit, palette } from "@vincemakes/kiso-tui";
|
|
7
|
-
import { buildAdapter } from "@vincemakes/kiso-runtime";
|
|
7
|
+
import { buildAdapter } from "@vincemakes/kiso-runtime/internal";
|
|
8
8
|
import { MODES, getMode, setMode } from "./mode.js";
|
|
9
9
|
import { agentModel, body, bodyLog, configModels, dock, setAgentModel, setCurrentModelName } from "./state.js";
|
|
10
10
|
import { directWriteProfile, profileAvailable } from "./config.js";
|
package/dist/index.js
CHANGED
|
@@ -395,6 +395,15 @@ async function makeAgent(sessionId, input, modelFlag) {
|
|
|
395
395
|
return createAgent(definition);
|
|
396
396
|
}
|
|
397
397
|
async function main() {
|
|
398
|
+
// E group (the graceful-exit gate ③, R-G 0.1.48): a terminal closing
|
|
399
|
+
// turns the in-flight stdout/stderr writes into EIO, and node's
|
|
400
|
+
// unhandled 'error' event on the WriteStream kills the process —
|
|
401
|
+
// mid-exit the release never runs (the 60-byte residue the gate
|
|
402
|
+
// caught). The bytes are undeliverable anyway (the terminal is
|
|
403
|
+
// gone): the error must never abort the exit sequence. The listener
|
|
404
|
+
// swallows it — the standard "the stream may die under me" idiom.
|
|
405
|
+
process.stdout.on("error", () => { });
|
|
406
|
+
process.stderr.on("error", () => { });
|
|
398
407
|
// Modes: --mode <name> wins over KISO_MODE — both applied before the
|
|
399
408
|
// first makeAgent (the tier extensions read `current` live). The flag
|
|
400
409
|
// is stripped from the positional args, so it works in any position.
|
|
@@ -493,7 +502,15 @@ async function main() {
|
|
|
493
502
|
break;
|
|
494
503
|
}
|
|
495
504
|
case "sessions": {
|
|
496
|
-
|
|
505
|
+
// R-I-p2 audit (the argument-consistency mandate): the
|
|
506
|
+
// read-only listing NEVER writes through the input, but the
|
|
507
|
+
// trust gate lives inside makeAgent and ASKS through it — on
|
|
508
|
+
// a TTY with a first-discovery .kiso, the undefined input
|
|
509
|
+
// crashed identically to the bare command (finding R-I-p-2,
|
|
510
|
+
// "reading 'question'" on the dock-less branch). The input
|
|
511
|
+
// exists so the gate's ask can be answered; the listing
|
|
512
|
+
// itself never touches it.
|
|
513
|
+
agent = await makeAgent(undefined, input, modelFlag);
|
|
497
514
|
for (const meta of agent.sessions()) {
|
|
498
515
|
console.log(renderSessionLine(meta));
|
|
499
516
|
}
|
|
@@ -516,7 +533,13 @@ async function main() {
|
|
|
516
533
|
// chat — the first argument is the session id.
|
|
517
534
|
const id = command ?? new Date().toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
518
535
|
dock.enter();
|
|
519
|
-
|
|
536
|
+
// R-I-p2 (finding R-I-p-2): the bare command passes the SAME
|
|
537
|
+
// input source and model flag as chat/resume — the pre-patch
|
|
538
|
+
// call dropped both, and the first-run trust gate read
|
|
539
|
+
// through the undefined input: "Cannot read properties of
|
|
540
|
+
// undefined" at the ask (panelAsk with the dock, question on
|
|
541
|
+
// the dock-less fallback).
|
|
542
|
+
agent = await makeAgent(id, input, modelFlag);
|
|
520
543
|
const session = await agent.session({ id });
|
|
521
544
|
bodyLog(`session ${id}\n`);
|
|
522
545
|
extensionsBanner(recentSessions(id, agent));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "kiso CLI — the coding-agent reference product: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-core": "0.
|
|
22
|
-
"@vincemakes/kiso-evals": "0.
|
|
23
|
-
"@vincemakes/kiso-mcp-ext": "0.
|
|
24
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
25
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
26
|
-
"@vincemakes/kiso-runtime": "0.
|
|
27
|
-
"@vincemakes/kiso-skills-ext": "0.
|
|
28
|
-
"@vincemakes/kiso-subagent-ext": "0.
|
|
29
|
-
"@vincemakes/kiso-task-ext": "0.
|
|
30
|
-
"@vincemakes/kiso-tools-node": "0.
|
|
31
|
-
"@vincemakes/kiso-tui": "0.
|
|
32
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
21
|
+
"@vincemakes/kiso-core": "0.2.0",
|
|
22
|
+
"@vincemakes/kiso-evals": "0.2.0",
|
|
23
|
+
"@vincemakes/kiso-mcp-ext": "0.2.0",
|
|
24
|
+
"@vincemakes/kiso-provider-anthropic": "0.2.0",
|
|
25
|
+
"@vincemakes/kiso-provider-openai": "0.2.0",
|
|
26
|
+
"@vincemakes/kiso-runtime": "0.2.0",
|
|
27
|
+
"@vincemakes/kiso-skills-ext": "0.2.0",
|
|
28
|
+
"@vincemakes/kiso-subagent-ext": "0.2.0",
|
|
29
|
+
"@vincemakes/kiso-task-ext": "0.2.0",
|
|
30
|
+
"@vincemakes/kiso-tools-node": "0.2.0",
|
|
31
|
+
"@vincemakes/kiso-tui": "0.2.0",
|
|
32
|
+
"@vincemakes/kiso-tui-cells": "0.2.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^26.1.2",
|