@vincemakes/kiso-code 0.43.0 → 0.45.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/builtin.js +5 -6
- package/dist/chat.d.ts +3 -1
- package/dist/chat.js +101 -127
- package/dist/index.js +6 -1
- package/dist/state.d.ts +5 -2
- package/dist/state.js +2 -2
- package/dist/trust-ui.js +2 -2
- package/package.json +14 -15
package/dist/builtin.js
CHANGED
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* E5 (the composition round, finding E5-F1/F2): the task extension left
|
|
9
9
|
* the default composition — on 13 consecutive real-provider sessions it
|
|
10
|
-
* paid its rent (
|
|
11
|
-
* was never called, not even on the guidance's own designed trigger
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* to shadow or collide with).
|
|
10
|
+
* paid its rent (its guidance and its tool on every request) and
|
|
11
|
+
* was never called, not even on the guidance's own designed trigger —
|
|
12
|
+
* and 0.44.0 retired it outright (no host used it either). A user or
|
|
13
|
+
* project extension named "task" is a plain extension — no built-in to
|
|
14
|
+
* shadow or collide with.
|
|
16
15
|
*
|
|
17
16
|
* - a user extension may SHADOW a built-in by name — the user's deliberate
|
|
18
17
|
* install wins (a built-in cannot be uninstalled), loudly, and the
|
package/dist/chat.d.ts
CHANGED
|
@@ -369,7 +369,9 @@ export declare function consumeRun(session: AgentSession, run: Run, input: LineI
|
|
|
369
369
|
/** W21: the amend words ("Yes + feedback") ride the NEXT user turn —
|
|
370
370
|
* threaded from chat's submitTurn; absent in the recovery flow
|
|
371
371
|
* (resume) where a dropped amend is noticed instead. */
|
|
372
|
-
submitTurn?: (line: string) => void
|
|
372
|
+
submitTurn?: (line: string) => void,
|
|
373
|
+
/** ADR-0057: a steer landed — an input after the run's own. */
|
|
374
|
+
onInputLanded?: () => void): Promise<import("@vincemakes/kiso-core").Event | undefined>;
|
|
373
375
|
/** Interactive REPL: stream events, pause for approvals, Ctrl+C aborts. */
|
|
374
376
|
/** The chat loop's ENDING: exit closes the process's REPL for good; a
|
|
375
377
|
* switch hands main another session id to re-enter chat with — the
|
package/dist/chat.js
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* estimates. All bodies moved verbatim from index.ts.
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync, statSync } from "node:fs";
|
|
8
|
-
import { escapeTerminal, cacheHitPct, decodeRate, idleStatus, palette, renderEvent, renderRecap, runningStatus, toolTarget,
|
|
8
|
+
import { escapeTerminal, cacheHitPct, decodeRate, idleStatus, palette, renderEvent, renderRecap, runningStatus, toolTarget, STATUS_GLYPHS, kUnit, } from "@vincemakes/kiso-tui";
|
|
9
9
|
import { askView, coldResumeLine, coldResumeView, deletionRiskHint, editFileDiff, writeFileDiff } from "@vincemakes/kiso-tui";
|
|
10
10
|
import { canonicalTargetPath, isProtectedPath, protectedIdentity, shellProgressPath } from "@vincemakes/kiso-tools-node";
|
|
11
11
|
import { queuedSwitchLines } from "./state.js";
|
|
12
12
|
import { echoText } from "@vincemakes/kiso-tui-cells/render";
|
|
13
|
-
import { canonicalizeUsage } from "@vincemakes/kiso-runtime";
|
|
13
|
+
import { canonicalizeUsage, RunClosedError } from "@vincemakes/kiso-runtime";
|
|
14
14
|
import { canonicalizeUsageForModel, requestBudget } from "@vincemakes/kiso-runtime/internal";
|
|
15
15
|
import { dispatch, abortBangCommand } from "./dispatch.js";
|
|
16
16
|
import { paintWindowTitle } from "./window-title.js";
|
|
@@ -756,21 +756,6 @@ export function parseSaferOptions(text) {
|
|
|
756
756
|
}
|
|
757
757
|
return out.length === 0 ? null : out;
|
|
758
758
|
}
|
|
759
|
-
/** TV-1B — the plain-word verdict tail for the settled checklist.
|
|
760
|
-
* "no passing check yet" covers both never-ran and ran-and-failed
|
|
761
|
-
* without lying; "outdated" claims only what the trajectory proves. */
|
|
762
|
-
function taskVerdictWords(kind) {
|
|
763
|
-
switch (kind) {
|
|
764
|
-
case "verified":
|
|
765
|
-
return "checked \u2713";
|
|
766
|
-
case "stale":
|
|
767
|
-
return "check outdated \u2014 work may have changed after it";
|
|
768
|
-
case "none":
|
|
769
|
-
return "no passing check yet";
|
|
770
|
-
case "unreadable":
|
|
771
|
-
return "task list unreadable";
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
759
|
/** W21 — the panel view for a permission_requested: the rule line (the
|
|
775
760
|
* why-asked speaker + the §3.5 fix hint), the toolTarget title, the
|
|
776
761
|
* "❯ run paused" status, and the ALWAYS-verbose args. */
|
|
@@ -821,34 +806,6 @@ function approvalArgs(name, input) {
|
|
|
821
806
|
* stream here. Events without a render (stop, expired, resolved, …) → null,
|
|
822
807
|
* and the consumer skips them — the pipe bytes stay identical.
|
|
823
808
|
*/
|
|
824
|
-
/**
|
|
825
|
-
* round 6 (the task round): translate a do-not-compact-tagged tool result whose
|
|
826
|
-
* content follows the task echo contract (a [task] header line + one
|
|
827
|
-
* `[pending|active|done] text` line per item) into the checklist cell's
|
|
828
|
-
* structured items. Null = not a checklist — the ordinary result cell
|
|
829
|
-
* renders. Keyed on the TAG (what the extension declared), never on a
|
|
830
|
-
* tool name; the parse is graceful so a foreign tagged result still
|
|
831
|
-
* renders normally.
|
|
832
|
-
*/
|
|
833
|
-
function parseChecklist(tags, content) {
|
|
834
|
-
if (!(tags ?? []).includes("do-not-compact"))
|
|
835
|
-
return null;
|
|
836
|
-
const items = [];
|
|
837
|
-
let header = "";
|
|
838
|
-
for (const line of content.split("\n")) {
|
|
839
|
-
const head = /^\[task\] (.*)$/.exec(line);
|
|
840
|
-
if (head !== null) {
|
|
841
|
-
header = head[1];
|
|
842
|
-
continue;
|
|
843
|
-
}
|
|
844
|
-
const m = /^\[(pending|active|done)\] (.*)$/.exec(line);
|
|
845
|
-
if (m !== null)
|
|
846
|
-
items.push({ text: m[2], status: m[1] });
|
|
847
|
-
}
|
|
848
|
-
if (items.length === 0)
|
|
849
|
-
return null;
|
|
850
|
-
return { header, items };
|
|
851
|
-
}
|
|
852
809
|
function toRenderInput(ev) {
|
|
853
810
|
switch (ev.type) {
|
|
854
811
|
case "user_input":
|
|
@@ -897,8 +854,11 @@ export async function consumeRun(session, run, input, turnNo, faux, statusCb,
|
|
|
897
854
|
/** W21: the amend words ("Yes + feedback") ride the NEXT user turn —
|
|
898
855
|
* threaded from chat's submitTurn; absent in the recovery flow
|
|
899
856
|
* (resume) where a dropped amend is noticed instead. */
|
|
900
|
-
submitTurn
|
|
857
|
+
submitTurn,
|
|
858
|
+
/** ADR-0057: a steer landed — an input after the run's own. */
|
|
859
|
+
onInputLanded) {
|
|
901
860
|
let last;
|
|
861
|
+
let inputsSeen = 0;
|
|
902
862
|
// W22: the TURN's usage — null until a call settles, then the sum of
|
|
903
863
|
// every call in this turn (accumulateUsage). Null rather than an empty
|
|
904
864
|
// accumulator, so the first call is the sum rather than an addition to
|
|
@@ -974,6 +934,9 @@ submitTurn) {
|
|
|
974
934
|
// construction (ADR-0040).
|
|
975
935
|
switch (ev.type) {
|
|
976
936
|
case "user_input":
|
|
937
|
+
inputsSeen += 1;
|
|
938
|
+
if (inputsSeen > 1)
|
|
939
|
+
onInputLanded?.();
|
|
977
940
|
// The window title is re-derived here because THIS is when a
|
|
978
941
|
// session stops being nameless: the tab opened as `kiso —
|
|
979
942
|
// <workspace>` and the first substantive prompt is what gives
|
|
@@ -984,7 +947,9 @@ submitTurn) {
|
|
|
984
947
|
// TV-1B: a system-sourced input is PRODUCT MACHINERY — visible
|
|
985
948
|
// (every durable input renders) but never painted as the
|
|
986
949
|
// user's words. Provenance is honest on screen, not only in
|
|
987
|
-
// the log.
|
|
950
|
+
// the log. Since 0.44.0 (the verify offer retired) the CLI
|
|
951
|
+
// starts no such run itself; the branch keeps the rule for
|
|
952
|
+
// any run that carries one, as replay.ts does for old logs.
|
|
988
953
|
if (ev.source === "system") {
|
|
989
954
|
// R2 (law 1.3): the ◆ said nothing the words did not. What
|
|
990
955
|
// makes this row honest is that it NAMES itself machinery.
|
|
@@ -1033,8 +998,7 @@ submitTurn) {
|
|
|
1033
998
|
const text = typeof ev.content === "string" ? ev.content : "";
|
|
1034
999
|
// W19: a DENIED call carries its reason — extracted from the
|
|
1035
1000
|
// result's "[Permission denied] " prefix, keyed on the
|
|
1036
|
-
// "denied" tag (the
|
|
1037
|
-
// declares, the prefix confirms). The ToolCell renders the
|
|
1001
|
+
// "denied" tag (the tag declares, the prefix confirms). The ToolCell renders the
|
|
1038
1002
|
// pinned row (full name, target, reason, no timing).
|
|
1039
1003
|
let reason = null;
|
|
1040
1004
|
if ((ev.tags ?? []).includes("denied")) {
|
|
@@ -1043,14 +1007,6 @@ submitTurn) {
|
|
|
1043
1007
|
reason = m[1];
|
|
1044
1008
|
}
|
|
1045
1009
|
body.toolResult(ev.callId, { content: text, isError: ev.isError, reason });
|
|
1046
|
-
// round 6 (the task round): a result tagged do-not-compact whose content
|
|
1047
|
-
// follows the checklist shape also renders as the durable
|
|
1048
|
-
// checklist cell (the CLI translates Event → the tui's own
|
|
1049
|
-
// shape; a non-matching parse falls back to the ordinary
|
|
1050
|
-
// result cell — never hide information).
|
|
1051
|
-
const checklist = parseChecklist(ev.tags, text);
|
|
1052
|
-
if (checklist !== null)
|
|
1053
|
-
body.checklist(checklist.header, checklist.items);
|
|
1054
1010
|
break;
|
|
1055
1011
|
}
|
|
1056
1012
|
case "text_delta":
|
|
@@ -1202,19 +1158,6 @@ submitTurn) {
|
|
|
1202
1158
|
// events (zero tokens). The dock's status bar still paints.
|
|
1203
1159
|
statusCb?.(turnUsage() ?? UNKNOWN_USAGE, displayCtxRatio(session));
|
|
1204
1160
|
const ratio = displayCtxRatio(session);
|
|
1205
|
-
// TV-1B: the settle verdict — the checklist stops lying. When
|
|
1206
|
-
// every item is CLAIMED done, the settled block's tail says
|
|
1207
|
-
// what the projection actually proves ("no passing check yet"
|
|
1208
|
-
// covers never-ran AND ran-and-failed; "may have changed"
|
|
1209
|
-
// claims trajectory knowledge, never filesystem knowledge).
|
|
1210
|
-
// A run that emitted no task_set still gets the block: the
|
|
1211
|
-
// claims live in the durable log, and the settle SYNTHESIZES
|
|
1212
|
-
// the display from session.assessTasks() — a UI projection,
|
|
1213
|
-
// never a new durable fact.
|
|
1214
|
-
const tv = session.assessTasks();
|
|
1215
|
-
if (tv.claims.length > 0 && tv.allClaimedDone) {
|
|
1216
|
-
body.checklist(taskVerdictWords(tv.evidence.kind), tv.claims.map((c) => ({ text: c.text, status: c.status })));
|
|
1217
|
-
}
|
|
1218
1161
|
// W14: the turn record closes HERE — before the recap logs, so
|
|
1219
1162
|
// the commit loop folds the quiet turn's held cells first (the
|
|
1220
1163
|
// fold line lands above the recap, natural cell order).
|
|
@@ -1328,13 +1271,11 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1328
1271
|
console.log("\n[exit requested]");
|
|
1329
1272
|
input.close();
|
|
1330
1273
|
};
|
|
1331
|
-
const turn = (text,
|
|
1274
|
+
const turn = (text, via) => new Promise((resolve, reject) => {
|
|
1332
1275
|
queued = Math.max(0, queued - 1); // a queued turn starts
|
|
1333
1276
|
// REL-0152-D11: a turn that names an image file carries it. The
|
|
1334
1277
|
// scan returns the STRING unchanged when it finds nothing, so a
|
|
1335
1278
|
// turn without one is byte-identical to before the feature.
|
|
1336
|
-
// Seeded turns are the product's own words and are never
|
|
1337
|
-
// scanned — nothing it writes to itself is an attachment.
|
|
1338
1279
|
// REL-0152-D16: the capsules' files come from the editor, which
|
|
1339
1280
|
// is the only thing that knows which number stands for which
|
|
1340
1281
|
// screenshot.
|
|
@@ -1342,8 +1283,8 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1342
1283
|
// 0.40.0: a skill turn is not scanned either — its text is a
|
|
1343
1284
|
// SKILL.md body, and a body that mentions `diagram.png` must not
|
|
1344
1285
|
// attach a file from the workspace the person never pointed at.
|
|
1345
|
-
const content =
|
|
1346
|
-
const run =
|
|
1286
|
+
const content = via !== undefined ? text : attachImages(text, input.attachments?.(), protectedFiles());
|
|
1287
|
+
const run = via !== undefined ? session.run(content, { via }) : session.run(content);
|
|
1347
1288
|
currentRun = run;
|
|
1348
1289
|
turnNo += 1;
|
|
1349
1290
|
const myTurn = turnNo;
|
|
@@ -1359,10 +1300,11 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1359
1300
|
(async () => {
|
|
1360
1301
|
let last;
|
|
1361
1302
|
try {
|
|
1362
|
-
last = await consumeRun(session, run, input, myTurn, faux, statusCb, submitTurn);
|
|
1303
|
+
last = await consumeRun(session, run, input, myTurn, faux, statusCb, submitTurn, landed);
|
|
1363
1304
|
stopSpinner();
|
|
1364
1305
|
paintIdle();
|
|
1365
1306
|
currentRun = null;
|
|
1307
|
+
handBack(run);
|
|
1366
1308
|
// 0.40.5: the kiso on disk may no longer be the one this
|
|
1367
1309
|
// session runs (an upgrade since it started) — said once per
|
|
1368
1310
|
// installed version (stale-version.ts).
|
|
@@ -1381,41 +1323,6 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1381
1323
|
// exit, so the release always runs.
|
|
1382
1324
|
if (eotSeen)
|
|
1383
1325
|
exitAtEmptyPrompt();
|
|
1384
|
-
// TV-1B — the thin task driver. A VERIFICATION turn settling
|
|
1385
|
-
// consumes the task-set identity it produced (a verifier's
|
|
1386
|
-
// own task_set belongs to the SAME accepted offer) and never
|
|
1387
|
-
// opens another offer. A normal COMPLETED settle may offer —
|
|
1388
|
-
// gated so the suggestion always yields to human intent.
|
|
1389
|
-
if (seedSource === "system") {
|
|
1390
|
-
const after = session.assessTasks();
|
|
1391
|
-
if (after.lastTaskSetSeq !== null)
|
|
1392
|
-
offeredTaskSeqs.add(after.lastTaskSetSeq);
|
|
1393
|
-
}
|
|
1394
|
-
else if (last?.type === "terminal" &&
|
|
1395
|
-
last.outcome.kind === "completed" &&
|
|
1396
|
-
process.stdin.isTTY &&
|
|
1397
|
-
!cancelled &&
|
|
1398
|
-
!eotSeen &&
|
|
1399
|
-
pendingAsk === null &&
|
|
1400
|
-
pendingTurns.length === 0 &&
|
|
1401
|
-
input.line() === "") {
|
|
1402
|
-
const tv = session.assessTasks();
|
|
1403
|
-
if (tv.claims.length > 0 &&
|
|
1404
|
-
tv.allClaimedDone &&
|
|
1405
|
-
(tv.evidence.kind === "none" || tv.evidence.kind === "stale") &&
|
|
1406
|
-
tv.lastTaskSetSeq !== null &&
|
|
1407
|
-
!offeredTaskSeqs.has(tv.lastTaskSetSeq)) {
|
|
1408
|
-
const verdict = await askPanel(input, verifyOfferView());
|
|
1409
|
-
// an explicit answer — Yes, Not now, OR Esc — consumes
|
|
1410
|
-
// the offer for THIS claims-set; only gate-suppression
|
|
1411
|
-
// (above) leaves it live for a later settle.
|
|
1412
|
-
offeredTaskSeqs.add(tv.lastTaskSetSeq);
|
|
1413
|
-
if (verdict.action === "allow") {
|
|
1414
|
-
queued += 1; // turn() decrements — keep the ledger honest
|
|
1415
|
-
chainRef.current = chainRef.current.then(() => turn(VERIFY_SEED, "system"));
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
1326
|
// round 8: after EVERY turn the prompt is re-armed — the human
|
|
1420
1327
|
// never types blind after the first turn.
|
|
1421
1328
|
input.prompt();
|
|
@@ -1435,6 +1342,7 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1435
1342
|
}
|
|
1436
1343
|
console.error(`\n[run failed] ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1437
1344
|
currentRun = null;
|
|
1345
|
+
handBack(run);
|
|
1438
1346
|
input.prompt();
|
|
1439
1347
|
resolve();
|
|
1440
1348
|
}
|
|
@@ -1447,6 +1355,7 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1447
1355
|
// aborted terminal, which the consumer keeps consuming.
|
|
1448
1356
|
console.log("\n[aborting run]");
|
|
1449
1357
|
pendingAsk?.();
|
|
1358
|
+
giveBack();
|
|
1450
1359
|
currentRun.abort();
|
|
1451
1360
|
}
|
|
1452
1361
|
else if (pendingAsk !== null) {
|
|
@@ -1479,6 +1388,7 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1479
1388
|
if (currentRun) {
|
|
1480
1389
|
console.log("\n[aborting run]");
|
|
1481
1390
|
pendingAsk?.();
|
|
1391
|
+
giveBack();
|
|
1482
1392
|
currentRun.abort();
|
|
1483
1393
|
}
|
|
1484
1394
|
});
|
|
@@ -1492,6 +1402,9 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1492
1402
|
return dispatch(line, dispatchCtx);
|
|
1493
1403
|
console.log("\n[redirecting run]");
|
|
1494
1404
|
pendingAsk?.();
|
|
1405
|
+
// ADR-0057: steers that had not landed ride the correction — one next
|
|
1406
|
+
// message, not a queue; the correction first (§3: it corrects them).
|
|
1407
|
+
const unsent = handed(currentRun.retract().length);
|
|
1495
1408
|
currentRun.abort();
|
|
1496
1409
|
// §3: a correction must run BEFORE the follow-ups queued earlier —
|
|
1497
1410
|
// it is a correction OF them. The existing slot mechanics compose
|
|
@@ -1506,9 +1419,9 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1506
1419
|
const jumped = pendingTurns.map((s) => ({ content: s.content, via: s.via }));
|
|
1507
1420
|
for (let i = jumped.length; i > 0; i -= 1)
|
|
1508
1421
|
popQueue();
|
|
1509
|
-
|
|
1422
|
+
queueTurn(unsent.length > 0 ? [line, ...unsent.map((u) => u.content)].join("\n\n") : line);
|
|
1510
1423
|
for (const j of jumped)
|
|
1511
|
-
|
|
1424
|
+
queueTurn(j.content, j.via);
|
|
1512
1425
|
});
|
|
1513
1426
|
// round 5 (P1-11): the PERSISTENT line listener is installed BEFORE the
|
|
1514
1427
|
// startup recovery — a cancelled question's re-emitted "line" needs a
|
|
@@ -1542,14 +1455,6 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1542
1455
|
// the editor — for a skill, the line the person TYPED; `content` is
|
|
1543
1456
|
// what the turn submits (the skill's body and args).
|
|
1544
1457
|
const pendingTurns = [];
|
|
1545
|
-
// TV-1B — the offer memory: session-local BY DESIGN (a dead process's
|
|
1546
|
-
// "not now" should not silence a live one; resume re-offers once,
|
|
1547
|
-
// honestly), keyed by the assessed claims' identity.
|
|
1548
|
-
const offeredTaskSeqs = new Set();
|
|
1549
|
-
// The fixed verification seed — durable with source:"system": WHO asked
|
|
1550
|
-
// is provenance in the log; on the provider wire it stays an ordinary
|
|
1551
|
-
// user-role message (never a system-prompt escalation).
|
|
1552
|
-
const VERIFY_SEED = "Verify the completed work: run the project's checks and report what passes and what fails.";
|
|
1553
1458
|
// v2b: the live status bar (docked only). Modes: /mode switches repaint
|
|
1554
1459
|
// it immediately through paintStatus (the last turn stats are kept).
|
|
1555
1460
|
// v3 §03: the status bar has TWO states. Idle: the mode is ALWAYS
|
|
@@ -1649,7 +1554,7 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1649
1554
|
return;
|
|
1650
1555
|
spentUsd = (spentUsd ?? 0) + usd;
|
|
1651
1556
|
};
|
|
1652
|
-
const
|
|
1557
|
+
const queueTurn = (line, via) => {
|
|
1653
1558
|
const slot = { line: via?.line ?? line, content: line, ...(via !== undefined ? { via } : {}), cancelled: false };
|
|
1654
1559
|
pendingTurns.push(slot);
|
|
1655
1560
|
queued += 1;
|
|
@@ -1674,7 +1579,7 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1674
1579
|
if (session.uncertainExecutions().length > 0)
|
|
1675
1580
|
await resolveUncertains(session, input, () => cancelled);
|
|
1676
1581
|
if (session.uncertainExecutions().length === 0)
|
|
1677
|
-
return turn(line,
|
|
1582
|
+
return turn(line, via);
|
|
1678
1583
|
// The human declined (round 10: a cancelled ask records NOTHING —
|
|
1679
1584
|
// the execution stays uncertain and durable), so the turn does not
|
|
1680
1585
|
// start. It is never swallowed in silence: the held text is
|
|
@@ -1696,10 +1601,78 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1696
1601
|
queued = Math.max(0, queued - 1);
|
|
1697
1602
|
return slot.line;
|
|
1698
1603
|
};
|
|
1604
|
+
// ADR-0057 (the owner's ruling, 2026-09-28): Enter while a run is live
|
|
1605
|
+
// is a STEER — there is no queue. `steering` mirrors what the run holds
|
|
1606
|
+
// and has not admitted; the run takes ALL of it at its next quiescent
|
|
1607
|
+
// boundary, and the landed user_input clears the rows. Piped and
|
|
1608
|
+
// task-file input keep one turn per line: a script has nobody steering.
|
|
1609
|
+
const steerable = process.stdin.isTTY === true && input.literal !== true;
|
|
1610
|
+
let steering = [];
|
|
1611
|
+
const steer = (line, via) => {
|
|
1612
|
+
if (!steerable || currentRun === null)
|
|
1613
|
+
return false;
|
|
1614
|
+
try {
|
|
1615
|
+
currentRun.steer(via !== undefined ? line : attachImages(line, input.attachments?.(), protectedFiles()));
|
|
1616
|
+
}
|
|
1617
|
+
catch (err) {
|
|
1618
|
+
if (err instanceof RunClosedError)
|
|
1619
|
+
return false; // the run is ending: this is the next turn
|
|
1620
|
+
throw err;
|
|
1621
|
+
}
|
|
1622
|
+
steering.push({ line: via?.line ?? line, content: line, ...(via !== undefined ? { via } : {}) });
|
|
1623
|
+
return true;
|
|
1624
|
+
};
|
|
1625
|
+
const submitTurn = (line, via) => {
|
|
1626
|
+
if (!steer(line, via))
|
|
1627
|
+
queueTurn(line, via);
|
|
1628
|
+
};
|
|
1629
|
+
/** The mirror of the last `n` steers the run handed back; clears it. */
|
|
1630
|
+
const handed = (n) => {
|
|
1631
|
+
const out = n > 0 ? steering.slice(-n) : [];
|
|
1632
|
+
steering = [];
|
|
1633
|
+
return out;
|
|
1634
|
+
};
|
|
1635
|
+
const landed = () => {
|
|
1636
|
+
steering = [];
|
|
1637
|
+
};
|
|
1638
|
+
/** A run sealed with steers still pending (max_turns, an END_TURN
|
|
1639
|
+
* result, an error): they are the person's next message, sent now. */
|
|
1640
|
+
const handBack = (run) => {
|
|
1641
|
+
const left = handed(run.unadmitted().length);
|
|
1642
|
+
if (left.length === 1)
|
|
1643
|
+
queueTurn(left[0].content, left[0].via);
|
|
1644
|
+
else if (left.length > 1)
|
|
1645
|
+
queueTurn(left.map((s) => s.content).join("\n\n"));
|
|
1646
|
+
};
|
|
1647
|
+
/** Esc / ctrl+c: a stop is not a send — steers that have not landed
|
|
1648
|
+
* go back to the editor. */
|
|
1649
|
+
const giveBack = () => {
|
|
1650
|
+
if (currentRun === null)
|
|
1651
|
+
return;
|
|
1652
|
+
const back = handed(currentRun.retract().length);
|
|
1653
|
+
if (back.length > 0)
|
|
1654
|
+
input.restore?.(back.map((s) => s.line).join("\n"));
|
|
1655
|
+
};
|
|
1656
|
+
/** ↑ takes back the LAST steer that has not landed (the others go back
|
|
1657
|
+
* in, in order); with none, the pipe-era queue pop. */
|
|
1658
|
+
const popPending = () => {
|
|
1659
|
+
if (currentRun !== null && steering.length > 0) {
|
|
1660
|
+
const back = currentRun.retract();
|
|
1661
|
+
const mine = handed(back.length);
|
|
1662
|
+
const last = mine.pop();
|
|
1663
|
+
for (const c of back.slice(0, -1))
|
|
1664
|
+
currentRun.steer(c);
|
|
1665
|
+
steering = mine;
|
|
1666
|
+
if (last !== undefined)
|
|
1667
|
+
return last.line;
|
|
1668
|
+
}
|
|
1669
|
+
return popQueue();
|
|
1670
|
+
};
|
|
1699
1671
|
// W22: the visibility invariant's binds — the dock renders the
|
|
1700
|
-
// pending chips
|
|
1701
|
-
|
|
1702
|
-
|
|
1672
|
+
// pending chips, the editor routes the pop keys.
|
|
1673
|
+
const pendingLines = () => [...steering.map((s) => s.line), ...pendingTurns.map((s) => s.line)];
|
|
1674
|
+
dock.bindQueue(pendingLines);
|
|
1675
|
+
input.bindQueue(pendingLines, popPending);
|
|
1703
1676
|
const dispatchCtx = {
|
|
1704
1677
|
session,
|
|
1705
1678
|
input,
|
|
@@ -1825,8 +1798,9 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1825
1798
|
runStart = Date.now();
|
|
1826
1799
|
runUsage = { in: null, out: null, cache: null, known: false };
|
|
1827
1800
|
lastTokPerSec = null; // TPS-1: nothing has settled in THIS turn yet
|
|
1828
|
-
const last = await consumeRun(session, recoveryRun, input, turnNo, faux, statusCb, submitTurn);
|
|
1801
|
+
const last = await consumeRun(session, recoveryRun, input, turnNo, faux, statusCb, submitTurn, landed);
|
|
1829
1802
|
currentRun = null;
|
|
1803
|
+
handBack(recoveryRun);
|
|
1830
1804
|
failOnFauxExhaustion(last, faux, input);
|
|
1831
1805
|
// E group (the graceful-exit gate ③): the same deferred re-check
|
|
1832
1806
|
// as the turn path — the recovery run's end is also a safe point.
|
package/dist/index.js
CHANGED
|
@@ -319,6 +319,10 @@ function editorInput(editor) {
|
|
|
319
319
|
bindQueue(state, pop) {
|
|
320
320
|
editor.bindQueue(state, pop);
|
|
321
321
|
},
|
|
322
|
+
// ADR-0057: the steers a stop handed back return to the buffer.
|
|
323
|
+
restore(text) {
|
|
324
|
+
editor.restore(text);
|
|
325
|
+
},
|
|
322
326
|
// R3a: cross-session history — the CLI owns the file I/O.
|
|
323
327
|
bindHistory(seed, persist) {
|
|
324
328
|
editor.bindHistory(seed, persist);
|
|
@@ -477,7 +481,8 @@ function makeLineInput() {
|
|
|
477
481
|
* the user-level names, then the project-level ones marked `project:`.
|
|
478
482
|
* The built-in column is the banner's truthful face of the built-in layer:
|
|
479
483
|
* a fresh install reads `[3 extensions: built-in: mcp, skills, subagent]`
|
|
480
|
-
* with zero disk setup (
|
|
484
|
+
* with zero disk setup (the task extension, opt-in from E5, was
|
|
485
|
+
* retired in 0.44.0). */
|
|
481
486
|
function bannerExtensionText() {
|
|
482
487
|
// KC3.5 slice ⓪ (the extraction): the composition moved to the
|
|
483
488
|
// terminal layer (extensionsBannerText — a pure function of the three
|
package/dist/state.d.ts
CHANGED
|
@@ -179,6 +179,9 @@ export interface LineInput {
|
|
|
179
179
|
* live slots (each pop cancels the turn), esc ends the walk after
|
|
180
180
|
* one more pop. The chips are the compositor's own bindQueue. */
|
|
181
181
|
bindQueue(state: () => readonly string[], pop: () => string | null): void;
|
|
182
|
+
/** ADR-0057: put text back into the editor — the steers a stop handed
|
|
183
|
+
* back before they landed. The pipe path has no editor; optional. */
|
|
184
|
+
restore?(text: string): void;
|
|
182
185
|
/** R3a: cross-session history — seed the recall buffer, register the
|
|
183
186
|
* append sink. The pipe path has no recall keys; optional. */
|
|
184
187
|
bindHistory?(seed: readonly string[], persist: (line: string) => void): void;
|
|
@@ -318,8 +321,8 @@ export declare function setNeverInherited(value: (call: import("@vincemakes/kiso
|
|
|
318
321
|
export declare let loadedExtensions: readonly KisoExtension[];
|
|
319
322
|
/** R-D 0.1.45: the BUILT-IN layer — the three default official extensions,
|
|
320
323
|
* shipped with the cli (module imports, never a disk scan; builtin.ts).
|
|
321
|
-
*
|
|
322
|
-
* column; a user extension may shadow a built-in. */
|
|
324
|
+
* The task extension, opt-in from E5, was retired in 0.44.0. The
|
|
325
|
+
* banner's marked column; a user extension may shadow a built-in. */
|
|
323
326
|
export declare let builtInExtensions: readonly KisoExtension[];
|
|
324
327
|
/** E1: the USER-level extensions alone — the banner's unmarked part (E3:
|
|
325
328
|
* loadedExtensions later includes the project-level ones too). */
|
package/dist/state.js
CHANGED
|
@@ -420,8 +420,8 @@ export function setNeverInherited(value) {
|
|
|
420
420
|
export let loadedExtensions = [];
|
|
421
421
|
/** R-D 0.1.45: the BUILT-IN layer — the three default official extensions,
|
|
422
422
|
* shipped with the cli (module imports, never a disk scan; builtin.ts).
|
|
423
|
-
*
|
|
424
|
-
* column; a user extension may shadow a built-in. */
|
|
423
|
+
* The task extension, opt-in from E5, was retired in 0.44.0. The
|
|
424
|
+
* banner's marked column; a user extension may shadow a built-in. */
|
|
425
425
|
export let builtInExtensions = [];
|
|
426
426
|
/** E1: the USER-level extensions alone — the banner's unmarked part (E3:
|
|
427
427
|
* loadedExtensions later includes the project-level ones too). */
|
package/dist/trust-ui.js
CHANGED
|
@@ -87,8 +87,8 @@ opts) {
|
|
|
87
87
|
// line typed at it was discarded anyway.
|
|
88
88
|
//
|
|
89
89
|
// The y/n fallback below still serves the views that really
|
|
90
|
-
// do take a yes or no — the uncertainty gate
|
|
91
|
-
// prompt
|
|
90
|
+
// do take a yes or no — the uncertainty gate and the trust
|
|
91
|
+
// prompt. Those have a dock-less form.
|
|
92
92
|
settled = true;
|
|
93
93
|
pendingAsk = null;
|
|
94
94
|
bodyLog(view.fallbackQuestion);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "kiso-code \u2014 the coding agent on kiso, the durable runtime for AI agents: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,20 +18,19 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-ask-ext": "0.
|
|
22
|
-
"@vincemakes/kiso-core": "0.
|
|
23
|
-
"@vincemakes/kiso-evals": "0.
|
|
24
|
-
"@vincemakes/kiso-mcp-ext": "0.
|
|
25
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
26
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
27
|
-
"@vincemakes/kiso-provider-openai-responses": "0.
|
|
28
|
-
"@vincemakes/kiso-runtime": "0.
|
|
29
|
-
"@vincemakes/kiso-skills-ext": "0.
|
|
30
|
-
"@vincemakes/kiso-subagent-ext": "0.
|
|
31
|
-
"@vincemakes/kiso-
|
|
32
|
-
"@vincemakes/kiso-
|
|
33
|
-
"@vincemakes/kiso-tui": "0.
|
|
34
|
-
"@vincemakes/kiso-tui-cells": "0.43.0"
|
|
21
|
+
"@vincemakes/kiso-ask-ext": "0.45.0",
|
|
22
|
+
"@vincemakes/kiso-core": "0.45.0",
|
|
23
|
+
"@vincemakes/kiso-evals": "0.45.0",
|
|
24
|
+
"@vincemakes/kiso-mcp-ext": "0.45.0",
|
|
25
|
+
"@vincemakes/kiso-provider-anthropic": "0.45.0",
|
|
26
|
+
"@vincemakes/kiso-provider-openai": "0.45.0",
|
|
27
|
+
"@vincemakes/kiso-provider-openai-responses": "0.45.0",
|
|
28
|
+
"@vincemakes/kiso-runtime": "0.45.0",
|
|
29
|
+
"@vincemakes/kiso-skills-ext": "0.45.0",
|
|
30
|
+
"@vincemakes/kiso-subagent-ext": "0.45.0",
|
|
31
|
+
"@vincemakes/kiso-tools-node": "0.45.0",
|
|
32
|
+
"@vincemakes/kiso-tui": "0.45.0",
|
|
33
|
+
"@vincemakes/kiso-tui-cells": "0.45.0"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
36
|
"@types/node": "^26.1.2",
|