@vincemakes/kiso-code 0.1.36 → 0.1.37
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/dispatch.js +62 -5
- package/dist/index.js +22 -25
- package/package.json +2 -2
package/dist/dispatch.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
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 { escapeTerminal, palette } from "@vincemakes/kiso-tui";
|
|
6
|
+
import { escapeTerminal, kUnit, palette } from "@vincemakes/kiso-tui";
|
|
7
7
|
import { buildAdapter } from "@vincemakes/kiso-runtime";
|
|
8
8
|
import { MODES, getMode, setMode } from "./mode.js";
|
|
9
|
-
import { agentModel, body, bodyLog, configModels, setAgentModel, setCurrentModelName } from "./state.js";
|
|
9
|
+
import { agentModel, body, bodyLog, configModels, dock, setAgentModel, setCurrentModelName } from "./state.js";
|
|
10
10
|
import { directWriteProfile, profileAvailable } from "./config.js";
|
|
11
11
|
/** The ONE dispatcher — slash commands, exit, and turns. The recovery
|
|
12
12
|
* replay routes through it too — a queued "/last" must never become a
|
|
@@ -164,19 +164,76 @@ export function dispatch(line, ctx) {
|
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
ctx.chainRef.current = ctx.chainRef.current.then(async () => {
|
|
167
|
+
// W18: the compaction indicator — the whole call runs under
|
|
168
|
+
// a live status row (the INDETERMINATE form: the summary is
|
|
169
|
+
// ONE adapter call, no fraction exists — kiso never invents
|
|
170
|
+
// a percentage), with a REAL cancel: esc aborts the signal,
|
|
171
|
+
// which the runtime observes at every phase boundary.
|
|
172
|
+
const abort = new AbortController();
|
|
173
|
+
let compactCancelled = false;
|
|
174
|
+
const onEscape = () => {
|
|
175
|
+
if (abort.signal.aborted)
|
|
176
|
+
return;
|
|
177
|
+
compactCancelled = true;
|
|
178
|
+
abort.abort();
|
|
179
|
+
};
|
|
180
|
+
ctx.input.onEscape(onEscape);
|
|
181
|
+
let compactStart = 0;
|
|
182
|
+
let compactTimer = null;
|
|
183
|
+
// the `as` on the initializer keeps the flow type the full union —
|
|
184
|
+
// onStart fills this during the call, but a closure assignment
|
|
185
|
+
// never re-narrows the outer scope (it would read `never`)
|
|
186
|
+
let compactInfo = null;
|
|
187
|
+
// the ctx estimate BEFORE the summarized event lands (the used
|
|
188
|
+
// fraction — the recap's "ctx 91% → 34%" drops after compacting)
|
|
189
|
+
let ctxBefore = null;
|
|
190
|
+
const compacting = (info) => {
|
|
191
|
+
const text = (elapsed) => `▘ compacting · ${info.rounds} rounds · ~${kUnit(info.tokens)} tokens · ${Math.max(0, elapsed)}s`;
|
|
192
|
+
compactStart = Date.now();
|
|
193
|
+
ctxBefore = Math.round(ctx.estimateCtx() * 100);
|
|
194
|
+
dock.setStatus(text(0), "esc to cancel");
|
|
195
|
+
compactTimer = setInterval(() => {
|
|
196
|
+
dock.setStatus(text(Math.round((Date.now() - compactStart) / 1000)), "esc to cancel");
|
|
197
|
+
}, 1000);
|
|
198
|
+
};
|
|
167
199
|
try {
|
|
168
|
-
const result = await ctx.session.summarize(
|
|
200
|
+
const result = await ctx.session.summarize({
|
|
201
|
+
signal: abort.signal,
|
|
202
|
+
onStart: (info) => {
|
|
203
|
+
compactInfo = info;
|
|
204
|
+
compacting(info);
|
|
205
|
+
},
|
|
206
|
+
});
|
|
169
207
|
if (result === null) {
|
|
170
208
|
body.notice("[/compact] nothing to compact — fewer than 5 rounds yet");
|
|
171
209
|
}
|
|
172
210
|
else {
|
|
173
|
-
|
|
211
|
+
// W18: the settled RECAP replaces the bare saved-token
|
|
212
|
+
// notice — it names what actually happened: the covered
|
|
213
|
+
// rounds, the one summary, the savings, the ctx drop
|
|
214
|
+
// (the estimate BEFORE vs AFTER — the same chars/4
|
|
215
|
+
// proxy the status bar shows, marked ~), and the time.
|
|
216
|
+
const ctxAfter = Math.round(ctx.estimateCtx() * 100);
|
|
217
|
+
const elapsed = compactStart > 0 ? ((Date.now() - compactStart) / 1000).toFixed(1) : "?";
|
|
218
|
+
// a non-null result implies onStart ran — the "?" is
|
|
219
|
+
// reachable only at the type level
|
|
220
|
+
body.notice(`[/compact] ▞ compacted · ${compactInfo?.rounds ?? "?"} rounds → 1 summary · saved ~${kUnit(result.savedTokens)} · ctx ${ctxBefore ?? "?"}% → ${ctxAfter}% · ${elapsed}s`);
|
|
174
221
|
}
|
|
175
222
|
}
|
|
176
223
|
catch (err) {
|
|
177
224
|
// Honest failure: nothing was persisted, the session
|
|
178
225
|
// is unchanged (ADR-0044 crash semantics).
|
|
179
|
-
|
|
226
|
+
if (compactCancelled) {
|
|
227
|
+
body.notice("[/compact] cancelled — nothing was persisted");
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
body.notice(`[/compact] failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
if (compactTimer !== null)
|
|
235
|
+
clearInterval(compactTimer);
|
|
236
|
+
ctx.paintIdle();
|
|
180
237
|
}
|
|
181
238
|
ctx.input.prompt();
|
|
182
239
|
});
|
package/dist/index.js
CHANGED
|
@@ -40,24 +40,6 @@ import { resume } from "./resume.js";
|
|
|
40
40
|
// The moved exports stay reachable from this entry — the test imports
|
|
41
41
|
// (project-trust, coding-agent) never change (B4: zero assertion changes).
|
|
42
42
|
export { applyProjectMerges } from "./trust-ui.js";
|
|
43
|
-
/** The banner: the block-letter logo (design fixed). TTY only — pipes, e2e
|
|
44
|
-
* drivers, and CI see byte-for-byte the old output; the extensions line
|
|
45
|
-
* merges into the third row on TTY and stays a standalone line off-TTY.
|
|
46
|
-
* v2a: the logo rows stay dim; the TAGLINE (row 2) is the blue identity
|
|
47
|
-
* accent. */
|
|
48
|
-
function startupBanner() {
|
|
49
|
-
// v3 §01: the banner is block-split — three independent logo rows
|
|
50
|
-
// (TOP / tagline / BOTTOM), then TWO info rows (version,
|
|
51
|
-
// extensions), each truncated at the window width; < 40 columns
|
|
52
|
-
// skips the logo. The historical `[N extensions: names]` text rides
|
|
53
|
-
// the extensions row verbatim (the e2e assertions keep matching).
|
|
54
|
-
const p = palette();
|
|
55
|
-
// A pty without a winsize reports columns = 0 (not undefined) — treat
|
|
56
|
-
// it as the default width, never as a 0-column truncation.
|
|
57
|
-
const W = process.stdout.columns ?? 0;
|
|
58
|
-
const rows = bannerLines(W > 0 ? W : 80, VERSION, bannerExtensionText().replace(/^ · /, ""));
|
|
59
|
-
return `${rows.map((r) => `${p.dim}${r}${p.reset}`).join("\n")}\n`;
|
|
60
|
-
}
|
|
61
43
|
/** The v2b behavior, unchanged: readline owns the line, SIGINT, and the
|
|
62
44
|
* prompt. Only ever constructed when stdin is NOT a TTY. The rl starts
|
|
63
45
|
* consuming stdin at construction (main), so 'line' events are buffered
|
|
@@ -187,11 +169,13 @@ function bannerExtensionText() {
|
|
|
187
169
|
parts.push(`project: ${projectExtensions.map(label).join(", ")}`);
|
|
188
170
|
return ` · [${total} extension${total === 1 ? "" : "s"}: ${parts.join(" · ")}]`;
|
|
189
171
|
}
|
|
190
|
-
/** E1: the startup banner line(s) — TTY: logo + merged extensions
|
|
191
|
-
*
|
|
192
|
-
|
|
172
|
+
/** E1: the startup banner line(s) — TTY: logo + merged extensions + the
|
|
173
|
+
* W5 resume list as a LIVE banner cell (W1: the tier re-derives on
|
|
174
|
+
* resize; the resume list re-gates with the tier); off-TTY: the
|
|
175
|
+
* historical `[N extensions: ...]` standalone line (zero change). */
|
|
176
|
+
function extensionsBanner(resume = []) {
|
|
193
177
|
if (process.stdout.isTTY) {
|
|
194
|
-
|
|
178
|
+
body.banner(VERSION, bannerExtensionText().replace(/^ · /, ""), resume);
|
|
195
179
|
return;
|
|
196
180
|
}
|
|
197
181
|
const text = bannerExtensionText();
|
|
@@ -199,6 +183,19 @@ function extensionsBanner() {
|
|
|
199
183
|
return;
|
|
200
184
|
bodyLog(`${text}\n`);
|
|
201
185
|
}
|
|
186
|
+
/** W5: the opening-screen resume list — up to 3 recent sessions, newest
|
|
187
|
+
* first, the CURRENT session excluded (it is not something to pick back
|
|
188
|
+
* up). Every field already exists behind the store's SessionMeta (the
|
|
189
|
+
* `kiso sessions` line shows the same data) — only the projection and
|
|
190
|
+
* the sort live here. */
|
|
191
|
+
function recentSessions(id, agent) {
|
|
192
|
+
return agent
|
|
193
|
+
.sessions()
|
|
194
|
+
.filter((m) => m.id !== id)
|
|
195
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
196
|
+
.slice(0, 3)
|
|
197
|
+
.map(({ title, events, runs, updatedAt }) => ({ title, events, runs, updatedAt }));
|
|
198
|
+
}
|
|
202
199
|
/**
|
|
203
200
|
* A area: the coding-agent system prompt — ONE constant, byte-stable for the
|
|
204
201
|
* session's lifetime (D area). Kept under ~80 lines; no template engine.
|
|
@@ -409,7 +406,7 @@ async function main() {
|
|
|
409
406
|
applyConfigMode();
|
|
410
407
|
const session = await agent.session({ id });
|
|
411
408
|
bodyLog(`session ${id}\n`);
|
|
412
|
-
extensionsBanner();
|
|
409
|
+
extensionsBanner(recentSessions(id, agent));
|
|
413
410
|
faux = currentFaux;
|
|
414
411
|
await chat(session, faux, input, resolveAutoCompact(mergedConfig));
|
|
415
412
|
break;
|
|
@@ -439,7 +436,7 @@ async function main() {
|
|
|
439
436
|
}
|
|
440
437
|
case "help": {
|
|
441
438
|
const p = palette();
|
|
442
|
-
console.log(`${p.dim}${bannerLines(80, VERSION, "").join("\n")}${p.reset}\n\n` +
|
|
439
|
+
console.log(`${p.dim}${bannerLines(80, process.stdout.rows ?? 0, VERSION, "").join("\n")}${p.reset}\n\n` +
|
|
443
440
|
"kiso — the coding agent that survives kill -9\n\n" +
|
|
444
441
|
" kiso [sessionId] interactive session (default command)\n" +
|
|
445
442
|
" kiso chat [sessionId] same as above\n" +
|
|
@@ -457,7 +454,7 @@ async function main() {
|
|
|
457
454
|
const agent = await makeAgent(fauxSkip(id));
|
|
458
455
|
const session = await agent.session({ id });
|
|
459
456
|
bodyLog(`session ${id}\n`);
|
|
460
|
-
extensionsBanner();
|
|
457
|
+
extensionsBanner(recentSessions(id, agent));
|
|
461
458
|
await chat(session, faux, input, autoCompactFromEnv());
|
|
462
459
|
break;
|
|
463
460
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
4
4
|
"description": "kiso CLI — the coding-agent reference product: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@vincemakes/kiso-provider-openai": "0.1.31",
|
|
25
25
|
"@vincemakes/kiso-runtime": "0.1.31",
|
|
26
26
|
"@vincemakes/kiso-tools-node": "0.1.31",
|
|
27
|
-
"@vincemakes/kiso-tui": "0.1.
|
|
27
|
+
"@vincemakes/kiso-tui": "0.1.37"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^26.1.2",
|