@vincemakes/kiso-code 0.51.1 → 0.53.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/auth/credentials.js +1 -1
- package/dist/chat.d.ts +2 -0
- package/dist/chat.js +13 -0
- package/dist/coding-prompt.js +0 -1
- package/dist/config-door.d.ts +35 -0
- package/dist/config-door.js +35 -0
- package/dist/config.js +1 -1
- package/dist/demo-script.d.ts +12 -0
- package/dist/demo-script.js +41 -0
- package/dist/dispatch.d.ts +6 -1
- package/dist/dispatch.js +65 -2
- package/dist/faux-glue.d.ts +2 -7
- package/dist/faux-glue.js +2 -41
- package/dist/first-run.js +1 -1
- package/dist/index.js +59 -4
- package/dist/mode.d.ts +2 -2
- package/dist/mode.js +9 -8
- package/dist/policy.d.ts +16 -0
- package/dist/policy.js +16 -0
- package/dist/preferences.js +1 -1
- package/dist/replay.d.ts +1 -1
- package/dist/replay.js +4 -1
- package/dist/state.d.ts +6 -4
- package/dist/state.js +7 -6
- package/dist/task-notice.js +6 -2
- package/package.json +25 -14
package/dist/auth/credentials.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
23
23
|
import { dirname, join } from "node:path";
|
|
24
|
-
import { kisoHome } from "
|
|
24
|
+
import { kisoHome } from "@vincemakes/kiso-runtime";
|
|
25
25
|
export class AuthError extends Error {
|
|
26
26
|
}
|
|
27
27
|
/** The providers a credential can be stored for (the runtime's built-in
|
package/dist/chat.d.ts
CHANGED
|
@@ -411,5 +411,7 @@ export interface ChatNav {
|
|
|
411
411
|
/** 0.40.0: an id outside this project's folder — refused, or opened where it is */
|
|
412
412
|
readonly route?: (id: string) => SessionRoute | null;
|
|
413
413
|
readonly pick?: () => Promise<string | null>;
|
|
414
|
+
/** the session's first message was sent: the caller may title the session from it (Session.title) */
|
|
415
|
+
readonly titled?: (firstMessage: string) => void;
|
|
414
416
|
}
|
|
415
417
|
export declare function chat(session: AgentSession, faux: boolean, input: LineInput, autoCompact?: AutoCompact, nav?: ChatNav, seed?: readonly string[]): Promise<ChatEnd>;
|
package/dist/chat.js
CHANGED
|
@@ -1386,7 +1386,11 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1386
1386
|
// 0.40.0: a skill turn is not scanned either — its text is a
|
|
1387
1387
|
// SKILL.md body, and a body that mentions `diagram.png` must not
|
|
1388
1388
|
// attach a file from the workspace the person never pointed at.
|
|
1389
|
+
input.setGhost?.(null); // a new turn: the last run's follow-up no longer applies
|
|
1389
1390
|
const content = via !== undefined ? text : attachImages(text, input.attachments?.(), protectedFiles());
|
|
1391
|
+
// the person's first message of the session: a title is made from it beside the run, never in its way
|
|
1392
|
+
if (via === undefined && !session.log.all.some((e) => e.type === "user_input"))
|
|
1393
|
+
nav?.titled?.(text);
|
|
1390
1394
|
// ADR-0058: a task notice is the runtime's input — source "system"
|
|
1391
1395
|
const run = via !== undefined ? session.run(content, { via, ...(via.kind === "tasks" ? { source: "system" } : {}) }) : session.run(content);
|
|
1392
1396
|
currentRun = run;
|
|
@@ -1435,6 +1439,15 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1435
1439
|
// round 8: after EVERY turn the prompt is re-armed — the human
|
|
1436
1440
|
// never types blind after the first turn.
|
|
1437
1441
|
input.prompt();
|
|
1442
|
+
// the follow-up (owner 2026-11-03): after a run that ended well, the line the person may send next,
|
|
1443
|
+
// asked of the session's own model with the run's own prefix (Session.followUp); shown dim in the
|
|
1444
|
+
// empty box only while no other turn has started or waits; off with the faux model and KISO_FOLLOWUP=off
|
|
1445
|
+
if (last?.type === "terminal" && last.outcome.kind === "completed" && !faux && input.setGhost !== undefined && process.env["KISO_FOLLOWUP"] !== "off" && queued === 0) {
|
|
1446
|
+
void session.followUp({ maxTokens: 200, signal: AbortSignal.timeout(15_000) }).then((line) => {
|
|
1447
|
+
if (turnNo === myTurn && currentRun === null && queued === 0)
|
|
1448
|
+
input.setGhost?.(line === "" ? null : line);
|
|
1449
|
+
}, () => undefined);
|
|
1450
|
+
}
|
|
1438
1451
|
// the ergonomics batch C8: the opt-in auto-compact — checked AFTER the
|
|
1439
1452
|
// turn ended (the run's terminal is in the log, the ratio
|
|
1440
1453
|
// is post-run).
|
package/dist/coding-prompt.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* project-instruction injection. Moved verbatim from index.ts (R1,
|
|
4
4
|
* 2026-09-23): the entry module keeps no prompt text of its own.
|
|
5
5
|
*/
|
|
6
|
-
import { protectedFiles } from "./state.js";
|
|
7
6
|
import { isProtectedPath, protectedIdentity } from "@vincemakes/kiso-tools-node";
|
|
8
7
|
import { join } from "node:path";
|
|
9
8
|
import { readFileSync } from "node:fs";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coding product's configuration and sign-in, as a library door: the
|
|
3
|
+
* profiles in `~/.kiso/config.json`, the credential store, the adapter
|
|
4
|
+
* options a profile resolves to, the efforts the person last picked, and the
|
|
5
|
+
* per-project session folders. A host that is not the terminal (the desktop
|
|
6
|
+
* app) reads the same login and the same Works as the terminal does.
|
|
7
|
+
*
|
|
8
|
+
* Almost read-only: the one write is `setCredential`, the terminal's own single
|
|
9
|
+
* credential write path (an API key; no OAuth flow), and `scaffoldFirstRun`, the
|
|
10
|
+
* terminal's first-run files. No project config (it needs the terminal's trust prompt). Nothing here
|
|
11
|
+
* reaches the terminal's state or UI; a test holds that line.
|
|
12
|
+
*/
|
|
13
|
+
import { type KisoConfig, type ModelProfile } from "./config.js";
|
|
14
|
+
export { ConfigError, applyEffortDeclarations, authForProfile, loadUserConfig, mergeConfigs, profileAvailable, resolveAutoCompact, resolveContextWindow, resolveModel, unavailableReason, } from "./config.js";
|
|
15
|
+
export type { KisoConfig, ModelProfile, ProfileAuth, ProfileKind, ResolvedModel } from "./config.js";
|
|
16
|
+
export { adapterOptionsFor } from "./auth/adapter-options.js";
|
|
17
|
+
export type { AdapterAuth, AdapterOptions } from "./auth/adapter-options.js";
|
|
18
|
+
export { AuthError, KNOWN_PROVIDERS, endpointCredentialId, maskSecret, providerIdOf, setCredential } from "./auth/credentials.js";
|
|
19
|
+
export type { Credential } from "./auth/credentials.js";
|
|
20
|
+
export { preferences, rememberEffort, startingEffort } from "./preferences.js";
|
|
21
|
+
export { isFirstRun, scaffoldFirstRun } from "./first-run.js";
|
|
22
|
+
export { fauxScript as demoScript } from "./demo-script.js";
|
|
23
|
+
export { canonicalPath, claimProjectDir, projectDirFor, projectLayoutActive, sessionFolders } from "./projects.js";
|
|
24
|
+
export type { SessionFolder } from "./projects.js";
|
|
25
|
+
/**
|
|
26
|
+
* The context window someone stated for a profile, or null when nobody has: the window the person set (the environment, the profile,
|
|
27
|
+
* the configuration, in that order), else the metadata registry's for the profile's endpoint, else for the model's own identity.
|
|
28
|
+
* The terminal's own chain, less what only a running terminal knows (a window an endpoint's refusal taught it).
|
|
29
|
+
*/
|
|
30
|
+
export declare function contextWindowOf(profile: Pick<ModelProfile, "model" | "baseUrl" | "contextWindow">, config?: KisoConfig): number | null;
|
|
31
|
+
/** The reasoning levels a profile's route accepts, and the one it uses when none is sent; null when nobody states them. */
|
|
32
|
+
export declare function effortOptions(profile: Pick<ModelProfile, "model" | "baseUrl">): {
|
|
33
|
+
readonly levels: readonly string[];
|
|
34
|
+
readonly default: string | null;
|
|
35
|
+
} | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coding product's configuration and sign-in, as a library door: the
|
|
3
|
+
* profiles in `~/.kiso/config.json`, the credential store, the adapter
|
|
4
|
+
* options a profile resolves to, the efforts the person last picked, and the
|
|
5
|
+
* per-project session folders. A host that is not the terminal (the desktop
|
|
6
|
+
* app) reads the same login and the same Works as the terminal does.
|
|
7
|
+
*
|
|
8
|
+
* Almost read-only: the one write is `setCredential`, the terminal's own single
|
|
9
|
+
* credential write path (an API key; no OAuth flow), and `scaffoldFirstRun`, the
|
|
10
|
+
* terminal's first-run files. No project config (it needs the terminal's trust prompt). Nothing here
|
|
11
|
+
* reaches the terminal's state or UI; a test holds that line.
|
|
12
|
+
*/
|
|
13
|
+
import { lookupContextWindow, lookupModelMetadata } from "@vincemakes/kiso-runtime/internal";
|
|
14
|
+
import { resolveContextWindow } from "./config.js";
|
|
15
|
+
export { ConfigError, applyEffortDeclarations, authForProfile, loadUserConfig, mergeConfigs, profileAvailable, resolveAutoCompact, resolveContextWindow, resolveModel, unavailableReason, } from "./config.js";
|
|
16
|
+
export { adapterOptionsFor } from "./auth/adapter-options.js";
|
|
17
|
+
export { AuthError, KNOWN_PROVIDERS, endpointCredentialId, maskSecret, providerIdOf, setCredential } from "./auth/credentials.js";
|
|
18
|
+
export { preferences, rememberEffort, startingEffort } from "./preferences.js";
|
|
19
|
+
export { isFirstRun, scaffoldFirstRun } from "./first-run.js";
|
|
20
|
+
export { fauxScript as demoScript } from "./demo-script.js";
|
|
21
|
+
export { canonicalPath, claimProjectDir, projectDirFor, projectLayoutActive, sessionFolders } from "./projects.js";
|
|
22
|
+
/**
|
|
23
|
+
* The context window someone stated for a profile, or null when nobody has: the window the person set (the environment, the profile,
|
|
24
|
+
* the configuration, in that order), else the metadata registry's for the profile's endpoint, else for the model's own identity.
|
|
25
|
+
* The terminal's own chain, less what only a running terminal knows (a window an endpoint's refusal taught it).
|
|
26
|
+
*/
|
|
27
|
+
export function contextWindowOf(profile, config) {
|
|
28
|
+
const set = resolveContextWindow(config ?? {}, profile);
|
|
29
|
+
return set ?? lookupContextWindow(profile.model, profile.baseUrl)?.tokens ?? null;
|
|
30
|
+
}
|
|
31
|
+
/** The reasoning levels a profile's route accepts, and the one it uses when none is sent; null when nobody states them. */
|
|
32
|
+
export function effortOptions(profile) {
|
|
33
|
+
const effort = lookupModelMetadata(profile.model, profile.baseUrl)?.capabilities.reasoning?.effort ?? null;
|
|
34
|
+
return effort === null ? null : { levels: effort.levels, default: effort.default };
|
|
35
|
+
}
|
package/dist/config.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
import { readFileSync, statSync } from "node:fs";
|
|
35
35
|
import { homedir } from "node:os";
|
|
36
36
|
import { join, win32 } from "node:path";
|
|
37
|
-
import { kisoHome } from "
|
|
37
|
+
import { kisoHome } from "@vincemakes/kiso-runtime";
|
|
38
38
|
import { MODE_VALUES, parseMode } from "./mode.js";
|
|
39
39
|
import { AuthError, effectiveBaseUrl, endpointCredentialId, getCredential, providerIdOf } from "./auth/credentials.js";
|
|
40
40
|
import { BUILTIN_MANIFESTS, declareEffort } from "@vincemakes/kiso-runtime/internal";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keyless demo script, as plain data with no imports of its own: the terminal's first-run demo and any other host that shows the
|
|
3
|
+
* same tour (the desktop app's first-run guide) read it from here.
|
|
4
|
+
*/
|
|
5
|
+
import type { FauxScript } from "@vincemakes/kiso-evals";
|
|
6
|
+
/**
|
|
7
|
+
* The keyless demo script: tours the tools so `kiso chat` exercises them.
|
|
8
|
+
* FOUR turns: each user turn consumes two model rounds (call → result →
|
|
9
|
+
* summary), so at least two consecutive user turns work in one process
|
|
10
|
+
* (F group).
|
|
11
|
+
*/
|
|
12
|
+
export declare function fauxScript(): FauxScript;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keyless demo script: tours the tools so `kiso chat` exercises them.
|
|
3
|
+
* FOUR turns: each user turn consumes two model rounds (call → result →
|
|
4
|
+
* summary), so at least two consecutive user turns work in one process
|
|
5
|
+
* (F group).
|
|
6
|
+
*/
|
|
7
|
+
export function fauxScript() {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
events: [
|
|
11
|
+
// bootstrap P1: a multi-delta thinking block — renders as ONE
|
|
12
|
+
// streaming segment, not one line per token.
|
|
13
|
+
{ type: "thinking", text: "Let me think about" },
|
|
14
|
+
{ type: "thinking", text: " the workspace" },
|
|
15
|
+
{ type: "thinking", text: " before acting." },
|
|
16
|
+
{ type: "text_start" },
|
|
17
|
+
{ type: "text_delta", text: "I'm the faux model. Let me look at the working directory." },
|
|
18
|
+
{ type: "tool_call_end", callId: "c1", name: "list_dir", input: {} },
|
|
19
|
+
{ type: "stop", reason: "tool_use" },
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
events: [
|
|
24
|
+
{ type: "text_delta", text: "I see the workspace. What would you like me to inspect or change?" },
|
|
25
|
+
{ type: "stop", reason: "end_turn" },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
events: [
|
|
30
|
+
{ type: "text_delta", text: "The faux model is still here, with full context." },
|
|
31
|
+
{ type: "stop", reason: "end_turn" },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
events: [
|
|
36
|
+
{ type: "text_delta", text: "And this is the end of the scripted tour." },
|
|
37
|
+
{ type: "stop", reason: "end_turn" },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
}
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* the context (the chain, the run state, the prompt arming).
|
|
5
5
|
*/
|
|
6
6
|
import type { SessionRoute } from "./projects.js";
|
|
7
|
-
import type
|
|
7
|
+
import { type StoreRecord } from "@vincemakes/kiso-runtime/internal";
|
|
8
|
+
import { type AgentSession } from "@vincemakes/kiso-runtime";
|
|
9
|
+
import type { SessionCardView } from "@vincemakes/kiso-tui";
|
|
8
10
|
import { type LineInput } from "./state.js";
|
|
9
11
|
import type { UserInputVia } from "@vincemakes/kiso-core";
|
|
10
12
|
/** Everything dispatch touches that chat() owns. */
|
|
@@ -72,3 +74,6 @@ export declare function parseBang(trimmed: string): {
|
|
|
72
74
|
export declare function markCompactWhy(why: string): void;
|
|
73
75
|
export declare function markAutoCompact(): void;
|
|
74
76
|
export declare function dispatch(line: string, ctx: DispatchCtx): void;
|
|
77
|
+
/** /fork's rows: each finished run's reply, newest first, by its first line and when it ended; the opened row says the
|
|
78
|
+
* line it answered. The card's id is the seq of the run's terminal, where SessionStore.fork cuts. */
|
|
79
|
+
export declare function replyCards(records: readonly StoreRecord[]): SessionCardView[];
|
package/dist/dispatch.js
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { STATUS_GLYPHS, contextRows, contextSheetRows, contextUnavailableRows, contextUnavailableSheetRows, displayVerb, escapeTerminal, helpRows, toolTarget, infoSheetRows, kUnit, modePickView, modelPickView, namedPickView, settingsPickView, compactingStatus, liveRow, palette, renderEvent, settledLabel, slashCommandNames } from "@vincemakes/kiso-tui";
|
|
7
7
|
import { newSessionId } from "./session-id.js";
|
|
8
|
-
import { buildAdapter, lookupModelMetadata, readSessionName, resolveContinuationScope, resolveReasoning, sessionTitle, tiersFor, writeSessionName } from "@vincemakes/kiso-runtime/internal";
|
|
8
|
+
import { buildAdapter, isForkNote, lookupModelMetadata, readSessionName, resolveContinuationScope, resolveReasoning, sessionTitle, tiersFor, writeSessionName } from "@vincemakes/kiso-runtime/internal";
|
|
9
|
+
import { ForkRefusedError, SessionStore } from "@vincemakes/kiso-runtime";
|
|
9
10
|
import { MODE_LABEL, MODE_NOTE, OFFERED_MODES, applyModeSetting, getDontAsk, getMode, modeDisplay, parseMode, setDontAsk } from "./mode.js";
|
|
10
11
|
import { clipboardWrite, lastAnswer } from "./clipboard.js";
|
|
11
12
|
import { protectedBangReason, protectedShellVerdict } from "./protected-shell.js";
|
|
12
|
-
import { activeStoreDir, agentBaseUrl, currentProfileName, setCurrentProfileName, currentModelName, agentModel, body, bodyLog, codingToolOptions, protectedFiles, kisoHome, configModels, dock, lastBinding, loadedSkillsCatalog, mergedConfig, readContextLedger, retryOnRow, sessionsDir, setAgentModel, setConfiguredWindow, setCurrentModelName, setModelChoice, setRetryShown, upstreamOf, VERSION, setLastBinding } from "./state.js";
|
|
13
|
+
import { activeStoreDir, sessionStoreRef, agentBaseUrl, currentProfileName, setCurrentProfileName, currentModelName, agentModel, body, bodyLog, codingToolOptions, protectedFiles, kisoHome, configModels, dock, lastBinding, loadedSkillsCatalog, mergedConfig, readContextLedger, retryOnRow, sessionsDir, setAgentModel, setConfiguredWindow, setCurrentModelName, setModelChoice, setRetryShown, upstreamOf, VERSION, setLastBinding } from "./state.js";
|
|
13
14
|
import { adapterOptionsFor } from "./auth/adapter-options.js";
|
|
14
15
|
import { profileProviderLabel, providerLabel } from "./provider-label.js";
|
|
15
16
|
import { installedVersion, versionStatusLine } from "./stale-version.js";
|
|
@@ -1384,6 +1385,44 @@ export function dispatch(line, ctx) {
|
|
|
1384
1385
|
ctx.requestReload();
|
|
1385
1386
|
return;
|
|
1386
1387
|
}
|
|
1388
|
+
if (trimmed === "/fork") {
|
|
1389
|
+
// owner 2026-11-03: the web app's fork, one rule — a new session from after one of this session's replies
|
|
1390
|
+
// (SessionStore.fork), then the terminal switches to it as /resume does; the replies are picked in /resume's band
|
|
1391
|
+
if (ctx.isRunning()) {
|
|
1392
|
+
body.notice("[/fork] a run is in flight — let it finish (esc stops it), then fork");
|
|
1393
|
+
ctx.input.prompt();
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
ctx.chainRef.current = ctx.chainRef.current.then(async () => {
|
|
1397
|
+
const cards = replyCards(sessionStoreRef?.load(ctx.session.id) ?? []);
|
|
1398
|
+
if (cards.length === 0) {
|
|
1399
|
+
body.notice("no finished reply to fork after yet");
|
|
1400
|
+
ctx.input.prompt();
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
const fork = (afterSeq) => {
|
|
1404
|
+
try {
|
|
1405
|
+
const made = new SessionStore(activeStoreDir).fork(ctx.session.id, { afterSeq });
|
|
1406
|
+
body.notice(`forked "${escapeTerminal(readSessionName(activeStoreDir, made.id) ?? made.id)}" — switching to it`);
|
|
1407
|
+
ctx.requestSwitch(made.id);
|
|
1408
|
+
}
|
|
1409
|
+
catch (error) {
|
|
1410
|
+
body.notice(error instanceof ForkRefusedError ? `[/fork] ${error.message}` : `[/fork] ${error instanceof Error ? error.message : String(error)}`);
|
|
1411
|
+
ctx.input.prompt();
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
// no band (a pipe, a dock-less terminal): after the last reply, the sidebar's Fork
|
|
1415
|
+
if (ctx.input.pick === undefined || !dock.active)
|
|
1416
|
+
return fork(Number(cards[0].id));
|
|
1417
|
+
ctx.input.pick(() => cards, (picked) => {
|
|
1418
|
+
if (picked === null)
|
|
1419
|
+
ctx.input.prompt(); // esc: nothing forked, nothing said
|
|
1420
|
+
else
|
|
1421
|
+
fork(Number(picked));
|
|
1422
|
+
}, undefined, "fork");
|
|
1423
|
+
});
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1387
1426
|
if (trimmed === "/resume" || trimmed.startsWith("/resume ")) {
|
|
1388
1427
|
// the mini-spec: the in-session door to the durable sessions —
|
|
1389
1428
|
// /resume <id> switches directly; bare /resume opens the SAME
|
|
@@ -1589,3 +1628,27 @@ async function tasksCommand(arg, ctx) {
|
|
|
1589
1628
|
else if (action === 1)
|
|
1590
1629
|
stop(task);
|
|
1591
1630
|
}
|
|
1631
|
+
/** /fork's rows: each finished run's reply, newest first, by its first line and when it ended; the opened row says the
|
|
1632
|
+
* line it answered. The card's id is the seq of the run's terminal, where SessionStore.fork cuts. */
|
|
1633
|
+
export function replyCards(records) {
|
|
1634
|
+
const cards = [];
|
|
1635
|
+
let asked = "";
|
|
1636
|
+
let reply = "";
|
|
1637
|
+
for (const { event: e, ts } of records) {
|
|
1638
|
+
if (e.type === "user_input" && e.source !== "system") {
|
|
1639
|
+
const content = Array.isArray(e.content) ? e.content.filter((b) => !(b.type === "text" && isForkNote(b.text ?? ""))) : e.content;
|
|
1640
|
+
asked = (typeof content === "string" ? content : content.map((b) => (b.type === "text" ? (b.text ?? "") : "")).join(" ")).trim();
|
|
1641
|
+
reply = "";
|
|
1642
|
+
}
|
|
1643
|
+
else if (e.type === "tool_result")
|
|
1644
|
+
reply = "";
|
|
1645
|
+
else if (e.type === "text_delta")
|
|
1646
|
+
reply += e.text;
|
|
1647
|
+
else if (e.type === "terminal" && e.outcome.kind === "completed") {
|
|
1648
|
+
// the row is how the person recognizes the reply: its first line, inline code read as plain text
|
|
1649
|
+
const first = (reply.split("\n").map((l) => l.trim()).find((l) => l !== "") ?? "(no reply text)").replace(/`([^`]*)`/g, "$1");
|
|
1650
|
+
cards.unshift({ id: String(e.seq), title: first, badge: "completed", turns: null, updatedAt: ts, uncertain: 0, asks: 0, outcome: null, detail: `answered: ${asked.split("\n")[0] ?? ""}` });
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return cards;
|
|
1654
|
+
}
|
package/dist/faux-glue.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { FauxScript } from "@vincemakes/kiso-evals";
|
|
7
7
|
import { type LineInput } from "./state.js";
|
|
8
|
+
import { fauxScript } from "./demo-script.js";
|
|
9
|
+
export { fauxScript };
|
|
8
10
|
/**
|
|
9
11
|
* E area: how many faux-script turns a session has already consumed. The faux
|
|
10
12
|
* provider's script counter is per-process, so a FRESH process that resumes
|
|
@@ -23,13 +25,6 @@ export declare function fauxSkip(id: string): number;
|
|
|
23
25
|
* multi-tool trajectory. Absent → the built-in demo script.
|
|
24
26
|
*/
|
|
25
27
|
export declare function readFauxScript(): FauxScript;
|
|
26
|
-
/**
|
|
27
|
-
* The keyless demo script: tours the tools so `kiso chat` exercises them.
|
|
28
|
-
* FOUR turns: each user turn consumes two model rounds (call → result →
|
|
29
|
-
* summary), so at least two consecutive user turns work in one process
|
|
30
|
-
* (F group).
|
|
31
|
-
*/
|
|
32
|
-
export declare function fauxScript(): FauxScript;
|
|
33
28
|
/** round 10: a faux-mode run whose scripted turns are exhausted must NOT print a
|
|
34
29
|
* provider error and exit 0 — the honest outcome is a loud message and a
|
|
35
30
|
* non-zero exit. Thrown as a CONTROLLED exception (never process.exit):
|
package/dist/faux-glue.js
CHANGED
|
@@ -7,6 +7,8 @@ import { readFileSync } from "node:fs";
|
|
|
7
7
|
import { escapeTerminal } from "@vincemakes/kiso-tui";
|
|
8
8
|
import { SessionStore } from "@vincemakes/kiso-runtime";
|
|
9
9
|
import { sessionsDir } from "./state.js";
|
|
10
|
+
import { fauxScript } from "./demo-script.js";
|
|
11
|
+
export { fauxScript };
|
|
10
12
|
/**
|
|
11
13
|
* E area: how many faux-script turns a session has already consumed. The faux
|
|
12
14
|
* provider's script counter is per-process, so a FRESH process that resumes
|
|
@@ -47,47 +49,6 @@ export function readFauxScript() {
|
|
|
47
49
|
process.exit(1);
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
|
-
/**
|
|
51
|
-
* The keyless demo script: tours the tools so `kiso chat` exercises them.
|
|
52
|
-
* FOUR turns: each user turn consumes two model rounds (call → result →
|
|
53
|
-
* summary), so at least two consecutive user turns work in one process
|
|
54
|
-
* (F group).
|
|
55
|
-
*/
|
|
56
|
-
export function fauxScript() {
|
|
57
|
-
return [
|
|
58
|
-
{
|
|
59
|
-
events: [
|
|
60
|
-
// bootstrap P1: a multi-delta thinking block — renders as ONE
|
|
61
|
-
// streaming segment, not one line per token.
|
|
62
|
-
{ type: "thinking", text: "Let me think about" },
|
|
63
|
-
{ type: "thinking", text: " the workspace" },
|
|
64
|
-
{ type: "thinking", text: " before acting." },
|
|
65
|
-
{ type: "text_start" },
|
|
66
|
-
{ type: "text_delta", text: "I'm the faux model. Let me look at the working directory." },
|
|
67
|
-
{ type: "tool_call_end", callId: "c1", name: "list_dir", input: {} },
|
|
68
|
-
{ type: "stop", reason: "tool_use" },
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
events: [
|
|
73
|
-
{ type: "text_delta", text: "I see the workspace. What would you like me to inspect or change?" },
|
|
74
|
-
{ type: "stop", reason: "end_turn" },
|
|
75
|
-
],
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
events: [
|
|
79
|
-
{ type: "text_delta", text: "The faux model is still here, with full context." },
|
|
80
|
-
{ type: "stop", reason: "end_turn" },
|
|
81
|
-
],
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
events: [
|
|
85
|
-
{ type: "text_delta", text: "And this is the end of the scripted tour." },
|
|
86
|
-
{ type: "stop", reason: "end_turn" },
|
|
87
|
-
],
|
|
88
|
-
},
|
|
89
|
-
];
|
|
90
|
-
}
|
|
91
52
|
/** round 10: a faux-mode run whose scripted turns are exhausted must NOT print a
|
|
92
53
|
* provider error and exit 0 — the honest outcome is a loud message and a
|
|
93
54
|
* non-zero exit. Thrown as a CONTROLLED exception (never process.exit):
|
package/dist/first-run.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
-
import { kisoHome } from "
|
|
13
|
+
import { kisoHome } from "@vincemakes/kiso-runtime";
|
|
14
14
|
/** The sentinel: present = the home has seen the 0.1.45 first-run
|
|
15
15
|
* sequence (its config surface exists — no re-scaffold, no re-note). */
|
|
16
16
|
const SENTINEL = "first-run";
|
package/dist/index.js
CHANGED
|
@@ -29,10 +29,11 @@ import { newSessionId } from "./session-id.js";
|
|
|
29
29
|
import { createInterface } from "node:readline";
|
|
30
30
|
import { Readable } from "node:stream";
|
|
31
31
|
import { fileURLToPath } from "node:url";
|
|
32
|
-
import {
|
|
32
|
+
import { createRequire } from "node:module";
|
|
33
|
+
import { basename, dirname, join } from "node:path";
|
|
33
34
|
import { Body, Editor, PROMPT, bannerLines, currentGround, currentGroundRgb, parseOscColor, resolveGround, setGround, escapeTerminal, extensionsBannerText, extensionsFact, idColumn, interactivePrompt, palette, renderSessionLine, sessionListFooter, sessionListHeader, sessionListRow, sessionListUnknownLine, slashCommandNames } from "@vincemakes/kiso-tui";
|
|
34
35
|
import { disposeExtensions, SessionStore } from "@vincemakes/kiso-runtime";
|
|
35
|
-
import { listSessionSidecars, migrateSummaries, readProfile, readSessionName, summaryMigrationPending } from "@vincemakes/kiso-runtime/internal";
|
|
36
|
+
import { listSessionSidecars, migrateSummaries, readProfile, readSessionName, summaryMigrationPending, writeSessionName } from "@vincemakes/kiso-runtime/internal";
|
|
36
37
|
import { skillMenuItems } from "./skill-invoke.js";
|
|
37
38
|
import { canonicalPath, hasSession, locateSession, projectLayoutActive, sessionFolders } from "./projects.js";
|
|
38
39
|
import { reverseMigration } from "./session-migration.js";
|
|
@@ -331,8 +332,8 @@ function editorInput(editor) {
|
|
|
331
332
|
// TUI2-R2 ②: the session picker — the editor owns the keys (the
|
|
332
333
|
// selection walk, the filter, enter/esc), the compositor draws the
|
|
333
334
|
// band, and the id comes back here.
|
|
334
|
-
pick(cards, onPick, here) {
|
|
335
|
-
editor.beginPick(cards, onPick, here);
|
|
335
|
+
pick(cards, onPick, here, kind) {
|
|
336
|
+
editor.beginPick(cards, onPick, here, kind);
|
|
336
337
|
},
|
|
337
338
|
// W22: the pending-turn queue — the ↑ pop walk (the keys); the
|
|
338
339
|
// chips are the compositor's bindQueue (the dock side).
|
|
@@ -340,6 +341,9 @@ function editorInput(editor) {
|
|
|
340
341
|
editor.bindQueue(state, pop);
|
|
341
342
|
},
|
|
342
343
|
// ADR-0057: the steers a stop handed back return to the buffer.
|
|
344
|
+
setGhost(text) {
|
|
345
|
+
editor.setGhost(text);
|
|
346
|
+
},
|
|
343
347
|
restore(text) {
|
|
344
348
|
editor.restore(text);
|
|
345
349
|
},
|
|
@@ -1157,6 +1161,30 @@ async function chatLoop(first, firstId, input, autoCompact) {
|
|
|
1157
1161
|
sessions: () => agent.sessionIds(),
|
|
1158
1162
|
route: (sessionId) => routeSession(sessionId),
|
|
1159
1163
|
...(process.stdin.isTTY ? { pick: () => pickSession(agent, input) } : {}),
|
|
1164
|
+
// owner 2026-11-03: a session is titled by a summary of its first message, in the background; the person's own
|
|
1165
|
+
// name wins, before or after; off with KISO_AUTO_TITLE=off and with the faux model, and never without a TTY:
|
|
1166
|
+
// a piped chat is a script or a bench leg, where one more request per session is not the person's
|
|
1167
|
+
...(currentFaux || process.env["KISO_AUTO_TITLE"] === "off" || process.stdin.isTTY !== true
|
|
1168
|
+
? {}
|
|
1169
|
+
: {
|
|
1170
|
+
titled: (firstMessage) => {
|
|
1171
|
+
// the session and its folder as they are NOW: a /resume or a switch before the title lands must not
|
|
1172
|
+
// send it to another session, nor repaint another session's window title
|
|
1173
|
+
const [titling, mine, dir] = [session, session.id, activeStoreDir];
|
|
1174
|
+
if (readSessionName(dir, mine) !== null)
|
|
1175
|
+
return;
|
|
1176
|
+
void titling.title(firstMessage, { signal: AbortSignal.timeout(15_000) }).then((title) => {
|
|
1177
|
+
if (title === "" || readSessionName(dir, mine) !== null)
|
|
1178
|
+
return;
|
|
1179
|
+
writeSessionName(dir, mine, title);
|
|
1180
|
+
// `id` is the loop's: the session open now (a switch moves it on)
|
|
1181
|
+
if (id !== mine)
|
|
1182
|
+
return;
|
|
1183
|
+
setTitleName(title);
|
|
1184
|
+
paintWindowTitle();
|
|
1185
|
+
}, () => undefined);
|
|
1186
|
+
},
|
|
1187
|
+
}),
|
|
1160
1188
|
};
|
|
1161
1189
|
const end = await chat(session, currentFaux, input, autoCompact, nav, seed);
|
|
1162
1190
|
if (end.next === "exit") {
|
|
@@ -1266,6 +1294,32 @@ async function main() {
|
|
|
1266
1294
|
process.exitCode = await applyPatch(args.slice(1));
|
|
1267
1295
|
return;
|
|
1268
1296
|
}
|
|
1297
|
+
// W2 (2026-11-01): `kiso web` runs the web app, its own package (@vincemakes/kiso-web), when it is
|
|
1298
|
+
// installed beside kiso, and names the install command when it is not. The FIRST argument alone
|
|
1299
|
+
// triggers it, before any flag is read: every other argument is the web app's (`kiso web --no-open`),
|
|
1300
|
+
// and a session named "web" still opens with `kiso chat web`. No terminal UI is drawn on this path,
|
|
1301
|
+
// so the host's own ctrl+C handling decides, and its exit is this command's exit.
|
|
1302
|
+
if (args[0] === "web") {
|
|
1303
|
+
let bin;
|
|
1304
|
+
try {
|
|
1305
|
+
const manifest = createRequire(import.meta.url).resolve("@vincemakes/kiso-web/package.json");
|
|
1306
|
+
bin = join(dirname(manifest), JSON.parse(readFileSync(manifest, "utf8")).bin["kiso-web"] ?? "");
|
|
1307
|
+
}
|
|
1308
|
+
catch {
|
|
1309
|
+
process.stderr.write("kiso web: the web app is not installed — npm i -g @vincemakes/kiso-web\n");
|
|
1310
|
+
process.exitCode = 1;
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const child = spawn(process.execPath, [bin, ...args.slice(1)], { stdio: "inherit" });
|
|
1314
|
+
process.on("SIGINT", () => { }); // the terminal sends it to the host too
|
|
1315
|
+
for (const sig of ["SIGTERM", "SIGHUP"])
|
|
1316
|
+
process.on(sig, () => child.kill(sig));
|
|
1317
|
+
process.exitCode = await new Promise((done) => {
|
|
1318
|
+
child.on("error", () => done(1));
|
|
1319
|
+
child.on("exit", (code) => done(code ?? 1));
|
|
1320
|
+
});
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1269
1323
|
// XP-1: --accept-drift (a flag, never an env var) authorizes opening a
|
|
1270
1324
|
// session whose recorded profile materially drifted — the
|
|
1271
1325
|
// acknowledgement is recorded as a new revision by the runtime.
|
|
@@ -1864,6 +1918,7 @@ async function main() {
|
|
|
1864
1918
|
" kiso logout <provider> remove the stored credential (or --endpoint <url>)\n" +
|
|
1865
1919
|
" kiso auth list stored credentials (keys masked)\n" +
|
|
1866
1920
|
" kiso update install the latest release (npm i -g @vincemakes/kiso-code@latest)\n" +
|
|
1921
|
+
" kiso web [--port <n>] [--no-open] the web app in the browser (its own package: npm i -g @vincemakes/kiso-web)\n" +
|
|
1867
1922
|
" kiso help this help\n\n" +
|
|
1868
1923
|
"flags (any position):\n" +
|
|
1869
1924
|
" --model <profile|provider/model> pick the model (also /model in-session)\n" +
|
package/dist/mode.d.ts
CHANGED
|
@@ -122,8 +122,8 @@ export declare function applyModeState(s: ModeState): void;
|
|
|
122
122
|
* deny>allow>ask over the SPEAKING verdicts (abstain = no opinion), so a
|
|
123
123
|
* user extension's deny wins over any mode tier, full-access included
|
|
124
124
|
* (the monotonicity e2e pins it). */
|
|
125
|
-
export declare function modeExtensions(workspaceRoot?: () => string): readonly KisoExtension[];
|
|
125
|
+
export declare function modeExtensions(workspaceRoot?: () => string, currentTier?: () => Mode): readonly KisoExtension[];
|
|
126
126
|
/** The plan tier's system prompt addition — injected at startup when the
|
|
127
127
|
* initial mode is plan (the session prompt is fixed at creation; runtime
|
|
128
128
|
* switches are guided by the deny reason). */
|
|
129
|
-
export declare function modeSystemPrompt(): string | undefined;
|
|
129
|
+
export declare function modeSystemPrompt(tier?: Mode): string | undefined;
|
package/dist/mode.js
CHANGED
|
@@ -165,9 +165,9 @@ export function applyModeState(s) {
|
|
|
165
165
|
current = s.mode;
|
|
166
166
|
dontAsk = s.dontAsk;
|
|
167
167
|
}
|
|
168
|
-
/** The per-tier verdict for a tool call — only when this tier is current. */
|
|
169
|
-
function tierVerdict(tier, call, workspaceRoot) {
|
|
170
|
-
if (tier !==
|
|
168
|
+
/** The per-tier verdict for a tool call — only when this tier is current (`now`: the tier in force, the process's own by default). */
|
|
169
|
+
function tierVerdict(tier, call, workspaceRoot, now) {
|
|
170
|
+
if (tier !== now)
|
|
171
171
|
return { action: "abstain" }; // not our tier — no opinion
|
|
172
172
|
switch (tier) {
|
|
173
173
|
case "manual":
|
|
@@ -215,12 +215,13 @@ function tierVerdict(tier, call, workspaceRoot) {
|
|
|
215
215
|
* deny>allow>ask over the SPEAKING verdicts (abstain = no opinion), so a
|
|
216
216
|
* user extension's deny wins over any mode tier, full-access included
|
|
217
217
|
* (the monotonicity e2e pins it). */
|
|
218
|
-
export function modeExtensions(workspaceRoot = () => process.cwd()) {
|
|
219
|
-
|
|
218
|
+
export function modeExtensions(workspaceRoot = () => process.cwd(), currentTier = getMode) {
|
|
219
|
+
// the tier in force is read on every decision, so a host that holds several sessions can give each its own (the terminal passes none)
|
|
220
|
+
return [...MODES.filter((m) => m === currentTier()), ...MODES.filter((m) => m !== currentTier())].map((m) => ({
|
|
220
221
|
name: `mode:${m}`,
|
|
221
222
|
approvals: [
|
|
222
223
|
{
|
|
223
|
-
decide: async (payload) => tierVerdict(m, { name: payload.name, input: payload.input ?? {} }, workspaceRoot),
|
|
224
|
+
decide: async (payload) => tierVerdict(m, { name: payload.name, input: payload.input ?? {} }, workspaceRoot, currentTier()),
|
|
224
225
|
},
|
|
225
226
|
],
|
|
226
227
|
}));
|
|
@@ -228,8 +229,8 @@ export function modeExtensions(workspaceRoot = () => process.cwd()) {
|
|
|
228
229
|
/** The plan tier's system prompt addition — injected at startup when the
|
|
229
230
|
* initial mode is plan (the session prompt is fixed at creation; runtime
|
|
230
231
|
* switches are guided by the deny reason). */
|
|
231
|
-
export function modeSystemPrompt() {
|
|
232
|
-
if (
|
|
232
|
+
export function modeSystemPrompt(tier = current) {
|
|
233
|
+
if (tier !== "plan")
|
|
233
234
|
return undefined;
|
|
234
235
|
return ("plan mode: read-only. You may inspect the workspace (read_file, list_dir, search_text, " +
|
|
235
236
|
"read_skill) but every write/edit/shell call is DENIED with 'plan mode: read-only'. " +
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coding product's policy, as a library door: the prompt and tool rules,
|
|
3
|
+
* and the extensions that bound what a coding agent may do (the catastrophe
|
|
4
|
+
* floor, the protected-file shell guard, the read-only shell allow, the loop
|
|
5
|
+
* breaker, the mode tiers). Everything here is state-free except the mode
|
|
6
|
+
* tier, which is process-local (`getMode` / `setMode`). A host that is not
|
|
7
|
+
* the terminal (the desktop app) composes the same agent from these and the
|
|
8
|
+
* public runtime and tool packages.
|
|
9
|
+
*/
|
|
10
|
+
export { CODING_TOOL_RULES, SYSTEM_PROMPT, composeSystemPrompt } from "./coding-prompt.js";
|
|
11
|
+
export { breakerExtension } from "./breaker.js";
|
|
12
|
+
export { floorExtension } from "./floor.js";
|
|
13
|
+
export { MODE_LABEL, OFFERED_MODES, getMode, modeExtensions, modeSystemPrompt, setMode } from "./mode.js";
|
|
14
|
+
export { protectedShellExtension } from "./protected-shell.js";
|
|
15
|
+
export { guardSavedAllow, neverInheritedCall } from "./protected-writes.js";
|
|
16
|
+
export { readOnlyShellExtension } from "./readonly-shell.js";
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coding product's policy, as a library door: the prompt and tool rules,
|
|
3
|
+
* and the extensions that bound what a coding agent may do (the catastrophe
|
|
4
|
+
* floor, the protected-file shell guard, the read-only shell allow, the loop
|
|
5
|
+
* breaker, the mode tiers). Everything here is state-free except the mode
|
|
6
|
+
* tier, which is process-local (`getMode` / `setMode`). A host that is not
|
|
7
|
+
* the terminal (the desktop app) composes the same agent from these and the
|
|
8
|
+
* public runtime and tool packages.
|
|
9
|
+
*/
|
|
10
|
+
export { CODING_TOOL_RULES, SYSTEM_PROMPT, composeSystemPrompt } from "./coding-prompt.js";
|
|
11
|
+
export { breakerExtension } from "./breaker.js";
|
|
12
|
+
export { floorExtension } from "./floor.js";
|
|
13
|
+
export { MODE_LABEL, OFFERED_MODES, getMode, modeExtensions, modeSystemPrompt, setMode } from "./mode.js";
|
|
14
|
+
export { protectedShellExtension } from "./protected-shell.js";
|
|
15
|
+
export { guardSavedAllow, neverInheritedCall } from "./protected-writes.js";
|
|
16
|
+
export { readOnlyShellExtension } from "./readonly-shell.js";
|
package/dist/preferences.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { lookupModelMetadata, resolveReasoning } from "@vincemakes/kiso-runtime/internal";
|
|
18
|
-
import { kisoHome } from "
|
|
18
|
+
import { kisoHome } from "@vincemakes/kiso-runtime";
|
|
19
19
|
let prefs = null;
|
|
20
20
|
let prefsPath = null;
|
|
21
21
|
export function preferencesPath(home = kisoHome()) {
|
package/dist/replay.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* summary (what the model sees next) and the turns it covers, read in
|
|
20
20
|
* the viewer — then any uncovered earlier turns folded as above.
|
|
21
21
|
*/
|
|
22
|
-
import type
|
|
22
|
+
import { type TaskInfo } from "@vincemakes/kiso-runtime/internal";
|
|
23
23
|
import type { NoticeMark } from "@vincemakes/kiso-tui";
|
|
24
24
|
/** The Body surface the replay drives — the live run's own mutations. */
|
|
25
25
|
export interface ReplayBody {
|
package/dist/replay.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { echoText } from "@vincemakes/kiso-tui-cells/render";
|
|
23
23
|
import { bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
24
24
|
import { cappedWaitLine, taskNoticeLines, taskNoticeRows, taskWhat } from "./task-notice.js";
|
|
25
|
+
import { isForkNote } from "@vincemakes/kiso-runtime/internal";
|
|
25
26
|
/** How many turns stay on screen in full. */
|
|
26
27
|
const FULL_TURNS = 2;
|
|
27
28
|
/** A user turn's opening line, or null when this input does not open one
|
|
@@ -32,7 +33,9 @@ function askOf(e) {
|
|
|
32
33
|
if (e.type !== "user_input" || e.source === "system")
|
|
33
34
|
return null;
|
|
34
35
|
const via = e.via;
|
|
35
|
-
|
|
36
|
+
// a fork's first input carries the fork note before the person's words: the model's, not the line they typed
|
|
37
|
+
const content = Array.isArray(e.content) ? e.content.filter((b) => !(b.type === "text" && isForkNote(b.text ?? ""))) : e.content;
|
|
38
|
+
const text = typeof via?.line === "string" ? via.line : echoText(content);
|
|
36
39
|
return text.trim() === "" ? null : text;
|
|
37
40
|
}
|
|
38
41
|
/** Split events into turns: each starts at a user turn's input; anything
|
package/dist/state.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Dock, type AtItem, type Body, type PanelVerdict, type PanelView, type SaferAnswer, type SessionCardView } from "@vincemakes/kiso-tui";
|
|
9
9
|
import type { TaskDeliveryItem } from "@vincemakes/kiso-core";
|
|
10
|
-
import type
|
|
10
|
+
import { kisoHome, type KisoExtension, type StoreRecord } from "@vincemakes/kiso-runtime";
|
|
11
11
|
import type { ReasoningSetting } from "@vincemakes/kiso-runtime/internal";
|
|
12
12
|
import { TaskManager } from "@vincemakes/kiso-runtime/internal";
|
|
13
13
|
import { type ShellTasks } from "@vincemakes/kiso-tools-node";
|
|
@@ -15,7 +15,7 @@ import { type ShellTasks } from "@vincemakes/kiso-tools-node";
|
|
|
15
15
|
* it (sessions, trust, extensions, mcp config, skills). The dedicated
|
|
16
16
|
* env vars (KISO_EXTENSIONS_DIR / KISO_MCP_CONFIG / KISO_SKILLS_DIR)
|
|
17
17
|
* still override their own path; nothing hard-codes ~/.kiso anymore. */
|
|
18
|
-
export
|
|
18
|
+
export { kisoHome };
|
|
19
19
|
/** DC-49 — the workspace the coding tools may touch, and the one place it
|
|
20
20
|
* is decided. The session's own tool set and the `!` command's runner both
|
|
21
21
|
* need it, and two copies of a ruling-bearing literal is how a later DC-49
|
|
@@ -241,7 +241,7 @@ export interface LineInput {
|
|
|
241
241
|
* picked id, or null when the human leaves without picking.
|
|
242
242
|
* OPTIONAL: only the raw-mode editor has it, and the picker only
|
|
243
243
|
* ever opens on a TTY (a pipe has nobody to pick). */
|
|
244
|
-
pick?(cards: () => readonly SessionCardView[], onPick: (id: string | null) => void, here?: string): void;
|
|
244
|
+
pick?(cards: () => readonly SessionCardView[], onPick: (id: string | null) => void, here?: string, kind?: "resume" | "fork"): void;
|
|
245
245
|
/** W22: bind the pending-turn queue — the ↑ pop walks the CLI's
|
|
246
246
|
* live slots (each pop cancels the turn), esc ends the walk after
|
|
247
247
|
* one more pop. The chips are the compositor's own bindQueue. */
|
|
@@ -249,6 +249,9 @@ export interface LineInput {
|
|
|
249
249
|
/** ADR-0057: put text back into the editor — the steers a stop handed
|
|
250
250
|
* back before they landed. The pipe path has no editor; optional. */
|
|
251
251
|
restore?(text: string): void;
|
|
252
|
+
/** The follow-up: a line the person may send next, dim in the empty box, Tab takes it; null clears it. Optional: the
|
|
253
|
+
* readline input has no box to draw it in. */
|
|
254
|
+
setGhost?(text: string | null): void;
|
|
252
255
|
/** R3a: cross-session history — seed the recall buffer, register the
|
|
253
256
|
* append sink. The pipe path has no recall keys; optional. */
|
|
254
257
|
bindHistory?(seed: readonly string[], persist: (line: string) => void): void;
|
|
@@ -445,4 +448,3 @@ export declare function lastBinding(): LastBinding | null;
|
|
|
445
448
|
export declare function setAcceptDrift(v: boolean): void;
|
|
446
449
|
export declare function acceptDrift(): boolean;
|
|
447
450
|
export declare function observedRevisionFromLog(sessionId: string | undefined, full: string, kind?: "edit" | "write"): string | undefined;
|
|
448
|
-
export {};
|
package/dist/state.js
CHANGED
|
@@ -12,8 +12,9 @@ import { dirname, join, resolve } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { AT_CAP, AT_SKIP, Dock } from "@vincemakes/kiso-tui";
|
|
14
14
|
import { projectMessages } from "@vincemakes/kiso-core";
|
|
15
|
+
import { kisoHome } from "@vincemakes/kiso-runtime";
|
|
15
16
|
import { TaskManager } from "@vincemakes/kiso-runtime/internal";
|
|
16
|
-
import { processTaskBackend } from "@vincemakes/kiso-tools-node";
|
|
17
|
+
import { processTaskBackend, readShowsWholeFile } from "@vincemakes/kiso-tools-node";
|
|
17
18
|
import { collectWriter } from "@vincemakes/kiso-subagent-ext";
|
|
18
19
|
import { canonicalPath, LEGACY_SESSIONS_DIR, projectDirFor, projectLayoutActive } from "./projects.js";
|
|
19
20
|
import { taskNoticeLines, taskWhat } from "./task-notice.js";
|
|
@@ -21,9 +22,7 @@ import { taskNoticeLines, taskWhat } from "./task-notice.js";
|
|
|
21
22
|
* it (sessions, trust, extensions, mcp config, skills). The dedicated
|
|
22
23
|
* env vars (KISO_EXTENSIONS_DIR / KISO_MCP_CONFIG / KISO_SKILLS_DIR)
|
|
23
24
|
* still override their own path; nothing hard-codes ~/.kiso anymore. */
|
|
24
|
-
export
|
|
25
|
-
return process.env.KISO_HOME ?? join(homedir(), ".kiso");
|
|
26
|
-
}
|
|
25
|
+
export { kisoHome };
|
|
27
26
|
/** DC-49 — the workspace the coding tools may touch, and the one place it
|
|
28
27
|
* is decided. The session's own tool set and the `!` command's runner both
|
|
29
28
|
* need it, and two copies of a ruling-bearing literal is how a later DC-49
|
|
@@ -709,7 +708,7 @@ export function observedRevisionFromLog(sessionId, full, kind = "edit") {
|
|
|
709
708
|
if (m.role === "assistant") {
|
|
710
709
|
for (const b of m.blocks) {
|
|
711
710
|
if (b.type === "tool_use" && typeof b.input.path === "string") {
|
|
712
|
-
calls.set(b.callId, { name: b.name, full: realOrResolved(resolve(process.cwd(), b.input.path)),
|
|
711
|
+
calls.set(b.callId, { name: b.name, full: realOrResolved(resolve(process.cwd(), b.input.path)), input: b.input });
|
|
713
712
|
}
|
|
714
713
|
}
|
|
715
714
|
}
|
|
@@ -722,9 +721,11 @@ export function observedRevisionFromLog(sessionId, full, kind = "edit") {
|
|
|
722
721
|
if (hit === undefined)
|
|
723
722
|
continue;
|
|
724
723
|
if (!m.isError && (c.name === "read_file" || c.name === "edit_file" || c.name === "write_file")) {
|
|
724
|
+
const sameRevision = hit[1] === rev;
|
|
725
725
|
rev = hit[1];
|
|
726
|
+
// a later partial read of the SAME revision keeps what the model already saw whole; another revision starts over
|
|
726
727
|
if (c.name === "read_file")
|
|
727
|
-
wholeKnown =
|
|
728
|
+
wholeKnown = readShowsWholeFile(c.input, body) || (wholeKnown && sameRevision);
|
|
728
729
|
else if (c.name === "write_file")
|
|
729
730
|
wholeKnown = true;
|
|
730
731
|
}
|
package/dist/task-notice.js
CHANGED
|
@@ -90,8 +90,12 @@ export function lostReason(task) {
|
|
|
90
90
|
// an unreadable journal: the general reason below
|
|
91
91
|
}
|
|
92
92
|
const unconfirmed = records.find((r) => r.type === "stop_unconfirmed");
|
|
93
|
-
if (unconfirmed !== undefined)
|
|
94
|
-
return `its stop could not be confirmed (pids ${unconfirmed.pids.join(", ")})
|
|
93
|
+
if (unconfirmed !== undefined) {
|
|
94
|
+
return unconfirmed.pids.length > 0 ? `its stop could not be confirmed (pids ${unconfirmed.pids.join(", ")})` : "its stop could not be confirmed (what still held it could not be named)";
|
|
95
|
+
}
|
|
96
|
+
const failed = records.find((r) => r.type === "handover_failed");
|
|
97
|
+
if (failed !== undefined)
|
|
98
|
+
return `its hand-over to a task failed (${failed.reason}); the command was running and its end is not known`;
|
|
95
99
|
if (task.backend === "foreground")
|
|
96
100
|
return "the kiso that held it ended without recording its end";
|
|
97
101
|
return "its runner is gone without recording its end";
|
package/package.json
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.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",
|
|
7
7
|
"bin": {
|
|
8
8
|
"kiso": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
+
"exports": {
|
|
11
|
+
"./config": {
|
|
12
|
+
"types": "./dist/config-door.d.ts",
|
|
13
|
+
"default": "./dist/config-door.js"
|
|
14
|
+
},
|
|
15
|
+
"./policy": {
|
|
16
|
+
"types": "./dist/policy.d.ts",
|
|
17
|
+
"default": "./dist/policy.js"
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
10
21
|
"files": [
|
|
11
22
|
"dist",
|
|
12
23
|
"README.md",
|
|
@@ -18,19 +29,19 @@
|
|
|
18
29
|
"test": "vitest run"
|
|
19
30
|
},
|
|
20
31
|
"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-tools-node": "0.
|
|
32
|
-
"@vincemakes/kiso-tui": "0.
|
|
33
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
32
|
+
"@vincemakes/kiso-ask-ext": "0.53.0",
|
|
33
|
+
"@vincemakes/kiso-core": "0.53.0",
|
|
34
|
+
"@vincemakes/kiso-evals": "0.53.0",
|
|
35
|
+
"@vincemakes/kiso-mcp-ext": "0.53.0",
|
|
36
|
+
"@vincemakes/kiso-provider-anthropic": "0.53.0",
|
|
37
|
+
"@vincemakes/kiso-provider-openai": "0.53.0",
|
|
38
|
+
"@vincemakes/kiso-provider-openai-responses": "0.53.0",
|
|
39
|
+
"@vincemakes/kiso-runtime": "0.53.0",
|
|
40
|
+
"@vincemakes/kiso-skills-ext": "0.53.0",
|
|
41
|
+
"@vincemakes/kiso-subagent-ext": "0.53.0",
|
|
42
|
+
"@vincemakes/kiso-tools-node": "0.53.0",
|
|
43
|
+
"@vincemakes/kiso-tui": "0.53.0",
|
|
44
|
+
"@vincemakes/kiso-tui-cells": "0.53.0"
|
|
34
45
|
},
|
|
35
46
|
"devDependencies": {
|
|
36
47
|
"@types/node": "^26.1.2",
|