@vincemakes/kiso-code 0.51.1 → 0.52.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.
@@ -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 "../state.js";
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
@@ -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 "./state.js";
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
+ }
@@ -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 "./state.js";
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,7 +29,8 @@ 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 { basename, join } from "node:path";
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
36
  import { listSessionSidecars, migrateSummaries, readProfile, readSessionName, summaryMigrationPending } from "@vincemakes/kiso-runtime/internal";
@@ -1266,6 +1267,32 @@ async function main() {
1266
1267
  process.exitCode = await applyPatch(args.slice(1));
1267
1268
  return;
1268
1269
  }
1270
+ // W2 (2026-11-01): `kiso web` runs the web app, its own package (@vincemakes/kiso-web), when it is
1271
+ // installed beside kiso, and names the install command when it is not. The FIRST argument alone
1272
+ // triggers it, before any flag is read: every other argument is the web app's (`kiso web --no-open`),
1273
+ // and a session named "web" still opens with `kiso chat web`. No terminal UI is drawn on this path,
1274
+ // so the host's own ctrl+C handling decides, and its exit is this command's exit.
1275
+ if (args[0] === "web") {
1276
+ let bin;
1277
+ try {
1278
+ const manifest = createRequire(import.meta.url).resolve("@vincemakes/kiso-web/package.json");
1279
+ bin = join(dirname(manifest), JSON.parse(readFileSync(manifest, "utf8")).bin["kiso-web"] ?? "");
1280
+ }
1281
+ catch {
1282
+ process.stderr.write("kiso web: the web app is not installed — npm i -g @vincemakes/kiso-web\n");
1283
+ process.exitCode = 1;
1284
+ return;
1285
+ }
1286
+ const child = spawn(process.execPath, [bin, ...args.slice(1)], { stdio: "inherit" });
1287
+ process.on("SIGINT", () => { }); // the terminal sends it to the host too
1288
+ for (const sig of ["SIGTERM", "SIGHUP"])
1289
+ process.on(sig, () => child.kill(sig));
1290
+ process.exitCode = await new Promise((done) => {
1291
+ child.on("error", () => done(1));
1292
+ child.on("exit", (code) => done(code ?? 1));
1293
+ });
1294
+ return;
1295
+ }
1269
1296
  // XP-1: --accept-drift (a flag, never an env var) authorizes opening a
1270
1297
  // session whose recorded profile materially drifted — the
1271
1298
  // acknowledgement is recorded as a new revision by the runtime.
@@ -1864,6 +1891,7 @@ async function main() {
1864
1891
  " kiso logout <provider> remove the stored credential (or --endpoint <url>)\n" +
1865
1892
  " kiso auth list stored credentials (keys masked)\n" +
1866
1893
  " kiso update install the latest release (npm i -g @vincemakes/kiso-code@latest)\n" +
1894
+ " kiso web [--port <n>] [--no-open] the web app in the browser (its own package: npm i -g @vincemakes/kiso-web)\n" +
1867
1895
  " kiso help this help\n\n" +
1868
1896
  "flags (any position):\n" +
1869
1897
  " --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 !== current)
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
- return [...MODES.filter((m) => m === current), ...MODES.filter((m) => m !== current)].map((m) => ({
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 (current !== "plan")
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'. " +
@@ -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";
@@ -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 "./state.js";
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/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 { KisoExtension, StoreRecord } from "@vincemakes/kiso-runtime";
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 declare function kisoHome(): string;
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
@@ -445,4 +445,3 @@ export declare function lastBinding(): LastBinding | null;
445
445
  export declare function setAcceptDrift(v: boolean): void;
446
446
  export declare function acceptDrift(): boolean;
447
447
  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 function kisoHome() {
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)), ranged: b.input.offset !== undefined || b.input.limit !== undefined });
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 = !c.ranged && !body.includes("(call again with offset=") && !body.includes("[output capped at");
728
+ wholeKnown = readShowsWholeFile(c.input, body) || (wholeKnown && sameRevision);
728
729
  else if (c.name === "write_file")
729
730
  wholeKnown = true;
730
731
  }
@@ -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.51.1",
3
+ "version": "0.52.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.51.1",
22
- "@vincemakes/kiso-core": "0.51.1",
23
- "@vincemakes/kiso-evals": "0.51.1",
24
- "@vincemakes/kiso-mcp-ext": "0.51.1",
25
- "@vincemakes/kiso-provider-anthropic": "0.51.1",
26
- "@vincemakes/kiso-provider-openai": "0.51.1",
27
- "@vincemakes/kiso-provider-openai-responses": "0.51.1",
28
- "@vincemakes/kiso-runtime": "0.51.1",
29
- "@vincemakes/kiso-skills-ext": "0.51.1",
30
- "@vincemakes/kiso-subagent-ext": "0.51.1",
31
- "@vincemakes/kiso-tools-node": "0.51.1",
32
- "@vincemakes/kiso-tui": "0.51.1",
33
- "@vincemakes/kiso-tui-cells": "0.51.1"
32
+ "@vincemakes/kiso-ask-ext": "0.52.0",
33
+ "@vincemakes/kiso-core": "0.52.0",
34
+ "@vincemakes/kiso-evals": "0.52.0",
35
+ "@vincemakes/kiso-mcp-ext": "0.52.0",
36
+ "@vincemakes/kiso-provider-anthropic": "0.52.0",
37
+ "@vincemakes/kiso-provider-openai": "0.52.0",
38
+ "@vincemakes/kiso-provider-openai-responses": "0.52.0",
39
+ "@vincemakes/kiso-runtime": "0.52.0",
40
+ "@vincemakes/kiso-skills-ext": "0.52.0",
41
+ "@vincemakes/kiso-subagent-ext": "0.52.0",
42
+ "@vincemakes/kiso-tools-node": "0.52.0",
43
+ "@vincemakes/kiso-tui": "0.52.0",
44
+ "@vincemakes/kiso-tui-cells": "0.52.0"
34
45
  },
35
46
  "devDependencies": {
36
47
  "@types/node": "^26.1.2",