@vincemakes/kiso-code 0.15.3 → 0.15.5
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/index.js +14 -0
- package/dist/resume-tail.d.ts +26 -0
- package/dist/resume-tail.js +63 -0
- package/dist/session-cards.d.ts +5 -0
- package/dist/session-cards.js +2 -2
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ import { fauxSkip, readFauxScript } from "./faux-glue.js";
|
|
|
41
41
|
import { autoCompactFromEnv, chat, contextWindowTokens, estimateCtxRatio } from "./chat.js";
|
|
42
42
|
import { loadProjectConfig, loadUserConfig, mergeConfigs, resolveAutoCompact, resolveContextWindow, resolveModel } from "./config.js";
|
|
43
43
|
import { resume } from "./resume.js";
|
|
44
|
+
import { resumeTail } from "./resume-tail.js";
|
|
44
45
|
import { collectSessionCards, projectSessionCard } from "./session-cards.js";
|
|
45
46
|
// The moved exports stay reachable from this entry — the test imports
|
|
46
47
|
// (project-trust, coding-agent) never change (B4: zero assertion changes).
|
|
@@ -591,10 +592,19 @@ async function chatLoop(agent, firstId, input, autoCompact) {
|
|
|
591
592
|
const session = await agent.session({ id });
|
|
592
593
|
if (prev === null) {
|
|
593
594
|
bodyLog(`session ${id}\n`);
|
|
595
|
+
// REL-0152-D5: a session with history says what that history WAS.
|
|
596
|
+
// Resuming used to print this one line and drop you at an empty
|
|
597
|
+
// prompt inside a conversation with thousands of events — the
|
|
598
|
+
// durable log was right there and none of it was shown. Empty for
|
|
599
|
+
// a fresh session, so `kiso chat` is byte-identical.
|
|
600
|
+
for (const line of resumeTail(session.log.all))
|
|
601
|
+
bodyLog(line);
|
|
594
602
|
extensionsBanner(await recentSessions(id, agent));
|
|
595
603
|
}
|
|
596
604
|
else {
|
|
597
605
|
bodyLog(`session ${id} (switched — previous: ${prev}, /resume ${prev} returns)\n`);
|
|
606
|
+
for (const line of resumeTail(session.log.all))
|
|
607
|
+
bodyLog(line);
|
|
598
608
|
if (currentFaux)
|
|
599
609
|
session.setAdapter(createFauxProvider(readFauxScript().slice(fauxSkip(id))));
|
|
600
610
|
}
|
|
@@ -822,6 +832,10 @@ async function main() {
|
|
|
822
832
|
// exactly where `kiso resume <id>` would have.
|
|
823
833
|
if (faux && arg === undefined)
|
|
824
834
|
session.setAdapter(createFauxProvider(readFauxScript().slice(fauxSkip(id))));
|
|
835
|
+
// REL-0152-D5 — the same tail on the explicit-id form. NOT on
|
|
836
|
+
// the -p path above: that one's stdout is a machine's input.
|
|
837
|
+
for (const line of resumeTail(session.log.all))
|
|
838
|
+
bodyLog(line);
|
|
825
839
|
await resume(session, prompt, faux, input);
|
|
826
840
|
break;
|
|
827
841
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REL-0152-D5 — what a resumed session shows you.
|
|
3
|
+
*
|
|
4
|
+
* `kiso resume <id>` printed one line naming the session and nothing
|
|
5
|
+
* else, dropping the user at an empty prompt inside a conversation with
|
|
6
|
+
* thousands of events. The product's claim is that a long task survives
|
|
7
|
+
* interruption and you can come back to it; coming back meant being told
|
|
8
|
+
* the session exists and shown none of it.
|
|
9
|
+
*
|
|
10
|
+
* The history is right there — the forensics in this round rebuilt an
|
|
11
|
+
* entire screen from 6,581 recorded events. This projects a bounded tail
|
|
12
|
+
* of it so the user can see where they were.
|
|
13
|
+
*
|
|
14
|
+
* A PROJECTION, never a second truth: it reads the durable log and
|
|
15
|
+
* renders text. It writes nothing, decides nothing, and cannot disagree
|
|
16
|
+
* with the session because it holds no state of its own.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* The lines to print when a session is resumed — newest turn last, so it
|
|
20
|
+
* reads in conversation order and the newest text sits nearest the
|
|
21
|
+
* prompt. Empty when there is nothing to show, and the caller prints
|
|
22
|
+
* nothing rather than an empty frame.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resumeTail(events: readonly {
|
|
25
|
+
readonly type: string;
|
|
26
|
+
}[]): string[];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REL-0152-D5 — what a resumed session shows you.
|
|
3
|
+
*
|
|
4
|
+
* `kiso resume <id>` printed one line naming the session and nothing
|
|
5
|
+
* else, dropping the user at an empty prompt inside a conversation with
|
|
6
|
+
* thousands of events. The product's claim is that a long task survives
|
|
7
|
+
* interruption and you can come back to it; coming back meant being told
|
|
8
|
+
* the session exists and shown none of it.
|
|
9
|
+
*
|
|
10
|
+
* The history is right there — the forensics in this round rebuilt an
|
|
11
|
+
* entire screen from 6,581 recorded events. This projects a bounded tail
|
|
12
|
+
* of it so the user can see where they were.
|
|
13
|
+
*
|
|
14
|
+
* A PROJECTION, never a second truth: it reads the durable log and
|
|
15
|
+
* renders text. It writes nothing, decides nothing, and cannot disagree
|
|
16
|
+
* with the session because it holds no state of its own.
|
|
17
|
+
*/
|
|
18
|
+
/** How much of the tail is worth showing. Two turns is enough to
|
|
19
|
+
* recognise a conversation and short enough that resuming does not
|
|
20
|
+
* bury the prompt. */
|
|
21
|
+
const TURNS = 2;
|
|
22
|
+
const REPLY_CHARS = 400;
|
|
23
|
+
const oneLine = (s) => s.replace(/\s+/g, " ").trim();
|
|
24
|
+
const clip = (s, n) => (s.length <= n ? s : `${s.slice(0, n - 1)}…`);
|
|
25
|
+
/**
|
|
26
|
+
* The lines to print when a session is resumed — newest turn last, so it
|
|
27
|
+
* reads in conversation order and the newest text sits nearest the
|
|
28
|
+
* prompt. Empty when there is nothing to show, and the caller prints
|
|
29
|
+
* nothing rather than an empty frame.
|
|
30
|
+
*/
|
|
31
|
+
export function resumeTail(events) {
|
|
32
|
+
const turns = [];
|
|
33
|
+
let ask = null;
|
|
34
|
+
let reply = "";
|
|
35
|
+
for (const ev of events) {
|
|
36
|
+
const e = ev;
|
|
37
|
+
if (e.type === "user_input" && typeof e.content === "string") {
|
|
38
|
+
if (ask !== null)
|
|
39
|
+
turns.push({ ask, reply });
|
|
40
|
+
ask = e.content;
|
|
41
|
+
reply = "";
|
|
42
|
+
}
|
|
43
|
+
else if (e.type === "text_delta" && typeof e.text === "string" && ask !== null) {
|
|
44
|
+
reply += e.text;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (ask !== null)
|
|
48
|
+
turns.push({ ask, reply });
|
|
49
|
+
const shown = turns.slice(-TURNS);
|
|
50
|
+
if (shown.length === 0)
|
|
51
|
+
return [];
|
|
52
|
+
const lines = [];
|
|
53
|
+
const skipped = turns.length - shown.length;
|
|
54
|
+
lines.push(skipped > 0 ? `─ resuming · ${turns.length} turns, showing the last ${shown.length} ─` : `─ resuming · ${turns.length} turn${turns.length === 1 ? "" : "s"} ─`);
|
|
55
|
+
for (const t of shown) {
|
|
56
|
+
lines.push(` › ${clip(oneLine(t.ask), 100)}`);
|
|
57
|
+
const body = oneLine(t.reply);
|
|
58
|
+
// A turn with no reply is a turn that was INTERRUPTED — the case
|
|
59
|
+
// resume exists for. Saying so beats printing a blank line.
|
|
60
|
+
lines.push(body === "" ? " (no reply recorded — this is where it stopped)" : ` ${clip(body, REPLY_CHARS)}`);
|
|
61
|
+
}
|
|
62
|
+
return lines;
|
|
63
|
+
}
|
package/dist/session-cards.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ import { type StoreRecord } from "@vincemakes/kiso-runtime/internal";
|
|
|
33
33
|
export type SessionBadge = "uncertain" | "ask" | "interrupted" | "completed" | "failed";
|
|
34
34
|
export interface SessionCard {
|
|
35
35
|
readonly id: string;
|
|
36
|
+
/** REL-0152-D6b: what the conversation was ABOUT. An id is unique and
|
|
37
|
+
* says nothing; a picker of five ids is five rows the human cannot
|
|
38
|
+
* tell apart. From the runtime's one definition — never a second
|
|
39
|
+
* copy of the rule (the listing and the picker disagreed once). */
|
|
40
|
+
readonly title: string;
|
|
36
41
|
readonly badge: SessionBadge;
|
|
37
42
|
/** The human's unit: one user_input is one turn. (SessionMeta.runs
|
|
38
43
|
* counts runIds, which a resume increments without the human having
|
package/dist/session-cards.js
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* terminal layer (sessionNote — the KC3 §1 split); this file owns the
|
|
29
29
|
* FACTS.
|
|
30
30
|
*/
|
|
31
|
-
import { executionLedger, openRunId } from "@vincemakes/kiso-runtime/internal";
|
|
31
|
+
import { executionLedger, openRunId, sessionTitle } from "@vincemakes/kiso-runtime/internal";
|
|
32
32
|
/**
|
|
33
33
|
* The projection. `asks` arrives as DATA because its accessor lives on
|
|
34
34
|
* the session object (pendingApprovals) rather than on the record list —
|
|
@@ -74,7 +74,7 @@ export function projectSessionCard(input) {
|
|
|
74
74
|
: outcome === "completed"
|
|
75
75
|
? "completed"
|
|
76
76
|
: "failed";
|
|
77
|
-
return { id: input.id, badge, turns, updatedAt: input.updatedAt, uncertain, asks: input.asks, outcome };
|
|
77
|
+
return { id: input.id, title: sessionTitle(input.records), badge, turns, updatedAt: input.updatedAt, uncertain, asks: input.asks, outcome };
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
80
|
* The listing's cards, newest first — the order both the picker and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.5",
|
|
4
4
|
"description": "kiso CLI \u2014 the durable coding agent that survives kill -9: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,19 +18,19 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-ask-ext": "0.15.
|
|
22
|
-
"@vincemakes/kiso-core": "0.15.
|
|
23
|
-
"@vincemakes/kiso-evals": "0.15.
|
|
24
|
-
"@vincemakes/kiso-mcp-ext": "0.15.
|
|
25
|
-
"@vincemakes/kiso-provider-anthropic": "0.15.
|
|
26
|
-
"@vincemakes/kiso-provider-openai": "0.15.
|
|
27
|
-
"@vincemakes/kiso-runtime": "0.15.
|
|
28
|
-
"@vincemakes/kiso-skills-ext": "0.15.
|
|
29
|
-
"@vincemakes/kiso-subagent-ext": "0.15.
|
|
30
|
-
"@vincemakes/kiso-task-ext": "0.15.
|
|
31
|
-
"@vincemakes/kiso-tools-node": "0.15.
|
|
32
|
-
"@vincemakes/kiso-tui": "0.15.
|
|
33
|
-
"@vincemakes/kiso-tui-cells": "0.15.
|
|
21
|
+
"@vincemakes/kiso-ask-ext": "0.15.5",
|
|
22
|
+
"@vincemakes/kiso-core": "0.15.5",
|
|
23
|
+
"@vincemakes/kiso-evals": "0.15.5",
|
|
24
|
+
"@vincemakes/kiso-mcp-ext": "0.15.5",
|
|
25
|
+
"@vincemakes/kiso-provider-anthropic": "0.15.5",
|
|
26
|
+
"@vincemakes/kiso-provider-openai": "0.15.5",
|
|
27
|
+
"@vincemakes/kiso-runtime": "0.15.5",
|
|
28
|
+
"@vincemakes/kiso-skills-ext": "0.15.5",
|
|
29
|
+
"@vincemakes/kiso-subagent-ext": "0.15.5",
|
|
30
|
+
"@vincemakes/kiso-task-ext": "0.15.5",
|
|
31
|
+
"@vincemakes/kiso-tools-node": "0.15.5",
|
|
32
|
+
"@vincemakes/kiso-tui": "0.15.5",
|
|
33
|
+
"@vincemakes/kiso-tui-cells": "0.15.5"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.1.2",
|