@vincemakes/kiso-code 0.49.0 → 0.50.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/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # @vincemakes/kiso-code
1
+ <h1 align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vincemakes/kiso/main/assets/readme/lockup-dark.png"><img src="https://raw.githubusercontent.com/vincemakes/kiso/main/assets/readme/lockup-light.png" height="46" alt="kiso"></picture></h1>
2
+
3
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vincemakes/kiso/main/assets/readme/features-dark.png"><img src="https://raw.githubusercontent.com/vincemakes/kiso/main/assets/readme/features-light.png" width="830" alt="Built in, not bolted on: it asks before it guesses; subagents in parallel; background tasks; approval modes; it resumes after a crash; yours to extend."></picture></p>
4
+
5
+ <p align="center"><img src="https://raw.githubusercontent.com/vincemakes/kiso/main/assets/readme/screenshot.png" width="830" alt="kiso in a dark terminal window: a green diff of new tests, then the test suite started as a background task, with 1 task running in the status row"></p>
2
6
 
3
7
  The coding agent that survives kill -9: durable sessions (append-only
4
8
  JSONL), pre-effect approvals, and honest recovery — an interrupted side
package/dist/builtin.js CHANGED
@@ -59,8 +59,9 @@ ask,
59
59
  * too late. Only the config knows them; nothing else can. */
60
60
  secretEnvNames = []) {
61
61
  // ADR-0058 3d: the session's task manager makes `delegate` offer
62
- // background children (explorer/reviewer; the cap and the turn budget
63
- // are the extension's defaults). 0.49.0 C1: the session's live binding
62
+ // background children (any role; a writer works in its own snapshot and
63
+ // is collected; the caps and the turn budgets are the extension's
64
+ // defaults). 0.49.0 C1: the session's live binding
64
65
  // is the model a child runs on when its task names none.
65
66
  const all = await Promise.all([createMcp({ secretEnvNames }), createSkills(), createSubagent({ tasks: tasksFor, currentBinding: bindingFor, ...joinOption() }), ...(ask === undefined ? [] : [createAsk(ask).then(offInDontAsk)])]);
66
67
  const shadowed = all.filter((b) => user.some((u) => u.name === b.name));
package/dist/chat.js CHANGED
@@ -1062,7 +1062,7 @@ onInputLanded) {
1062
1062
  if (m !== null)
1063
1063
  reason = m[1];
1064
1064
  }
1065
- body.toolResult(ev.callId, { content: text, isError: ev.isError, reason });
1065
+ body.toolResult(ev.callId, { content: text, isError: ev.isError, reason, ...((ev.tags ?? []).includes("created") ? { created: true } : {}) });
1066
1066
  break;
1067
1067
  }
1068
1068
  case "text_delta":
@@ -9,7 +9,7 @@
9
9
  */
10
10
  /** The built-in prompt. Exported for scripts/request-surface.mjs — the
11
11
  * model-side token-rent counter measures the REAL bytes, never a copy. */
12
- export declare const SYSTEM_PROMPT = "You are kiso, a coding agent. You work in a workspace\ndirectory and change code with tools. Be concise: answer in a few lines\nunless the task genuinely needs more. Never claim a file was changed\nunless a tool confirmed it.\n\nWhat you can reach:\n- The workspace: read_file, list_dir, search_text, write_file, edit_file.\n- This machine and the network: shell \u2014 builds, tests, git, package\n managers, curl for HTTP, system queries. A request one command can\n answer is answered by running it.\n- The human: ask_user, for a decision that is theirs.\n\nTool discipline:\n- READ BEFORE YOU EDIT. For any file you are about to change, read it\n first \u2014 never guess its content.\n- Use edit_file for targeted changes and write_file for new files or\n full rewrites.\n- Be careful \u2014 shell has side effects and may take time.\n- search_text and list_dir are cheap \u2014 locate first, then read ranges\n with read_file offset/limit; never read a whole large file in one call.\n- Do not re-read a file you already read unchanged, or one you changed\n yourself through a confirmed edit \u2014 rely on the earlier result and\n on the change you just made.\n- When a tool fails, read the error and adjust; do not repeat the same\n call blindly.\n\nAn authorization covers what it NAMES: when the scope is not named \u2014\nwhich files, which branches, whether to push \u2014 ask before acting.\nDelivering, sharing or handing over changes means committing locally and\nstopping; pushing, publishing or sending happens only when the human\nnames it.\n\nWorkflow: understand the request, find the relevant code, make the\nsmallest change that works, then verify with a command (tests/build).\nReport what you did in one or two lines per change.";
12
+ export declare const SYSTEM_PROMPT = "You are an expert coding assistant running on Kiso, an agent runtime. Help the user complete software engineering tasks in the current workspace using the available tools.\n\nShell can access this machine and the network, including git, package managers, and HTTP. For factual questions the user asks, use existing valid tool evidence or an available tool rather than guessing or claiming the information is unavailable.\n\nWork directly toward the user's request. Make the smallest complete change that solves it, follow existing conventions, and do not expand the task into unrelated fixes, refactors, or features.\n\nInspect enough current context to act correctly. Reuse valid observations and tool results instead of repeating work. A confirmed tool result is evidence; do not re-read or re-check solely for reassurance. If a tool reports a failure or conflict, use that new evidence to decide the next action.\n\nAfter a successful edit or write, treat the change you made and the returned revision as the file's current observed state. Do not re-read it before a follow-up edit unless you need context you have not already observed or a tool reports a conflict or failure. When a conflict or refusal already provides the current relevant text and revision, use that result directly instead of reading the file again.\n\nFor behavior-changing work, use the narrowest relevant existing verification when needed. Broaden verification only when a failure, meaningful risk, or concrete unresolved concern gives a reason to do so. Do not create ad-hoc verification scripts when existing evidence is sufficient.\n\nWhen the requested work is complete and there is no concrete unresolved issue, stop. Continue investigating or checking only when new evidence gives a reason to continue.\n\nNecessary workspace changes are within the scope of the requested task. Ask the user only for decisions that require the user. Do not commit, push, publish, send, or perform other remote or irreversible actions unless explicitly requested.\n\nRequests to share, deliver, hand over, or get work ready do not by themselves authorize a commit, push, publish, or send. Perform those actions only when the user explicitly names the action; otherwise leave the changes in the workspace.\n\nBe concise.";
13
13
  /** Hard cap for injected instructions — truncate and say so. */
14
14
  /**
15
15
  * A area: read the FIRST present instruction file (AGENTS.md preferred) and
@@ -13,41 +13,25 @@ import { readFileSync } from "node:fs";
13
13
  */
14
14
  /** The built-in prompt. Exported for scripts/request-surface.mjs — the
15
15
  * model-side token-rent counter measures the REAL bytes, never a copy. */
16
- export const SYSTEM_PROMPT = `You are kiso, a coding agent. You work in a workspace
17
- directory and change code with tools. Be concise: answer in a few lines
18
- unless the task genuinely needs more. Never claim a file was changed
19
- unless a tool confirmed it.
16
+ export const SYSTEM_PROMPT = `You are an expert coding assistant running on Kiso, an agent runtime. Help the user complete software engineering tasks in the current workspace using the available tools.
20
17
 
21
- What you can reach:
22
- - The workspace: read_file, list_dir, search_text, write_file, edit_file.
23
- - This machine and the network: shell — builds, tests, git, package
24
- managers, curl for HTTP, system queries. A request one command can
25
- answer is answered by running it.
26
- - The human: ask_user, for a decision that is theirs.
18
+ Shell can access this machine and the network, including git, package managers, and HTTP. For factual questions the user asks, use existing valid tool evidence or an available tool rather than guessing or claiming the information is unavailable.
27
19
 
28
- Tool discipline:
29
- - READ BEFORE YOU EDIT. For any file you are about to change, read it
30
- first — never guess its content.
31
- - Use edit_file for targeted changes and write_file for new files or
32
- full rewrites.
33
- - Be careful — shell has side effects and may take time.
34
- - search_text and list_dir are cheap — locate first, then read ranges
35
- with read_file offset/limit; never read a whole large file in one call.
36
- - Do not re-read a file you already read unchanged, or one you changed
37
- yourself through a confirmed edit — rely on the earlier result and
38
- on the change you just made.
39
- - When a tool fails, read the error and adjust; do not repeat the same
40
- call blindly.
20
+ Work directly toward the user's request. Make the smallest complete change that solves it, follow existing conventions, and do not expand the task into unrelated fixes, refactors, or features.
41
21
 
42
- An authorization covers what it NAMES: when the scope is not named —
43
- which files, which branches, whether to push — ask before acting.
44
- Delivering, sharing or handing over changes means committing locally and
45
- stopping; pushing, publishing or sending happens only when the human
46
- names it.
22
+ Inspect enough current context to act correctly. Reuse valid observations and tool results instead of repeating work. A confirmed tool result is evidence; do not re-read or re-check solely for reassurance. If a tool reports a failure or conflict, use that new evidence to decide the next action.
47
23
 
48
- Workflow: understand the request, find the relevant code, make the
49
- smallest change that works, then verify with a command (tests/build).
50
- Report what you did in one or two lines per change.`;
24
+ After a successful edit or write, treat the change you made and the returned revision as the file's current observed state. Do not re-read it before a follow-up edit unless you need context you have not already observed or a tool reports a conflict or failure. When a conflict or refusal already provides the current relevant text and revision, use that result directly instead of reading the file again.
25
+
26
+ For behavior-changing work, use the narrowest relevant existing verification when needed. Broaden verification only when a failure, meaningful risk, or concrete unresolved concern gives a reason to do so. Do not create ad-hoc verification scripts when existing evidence is sufficient.
27
+
28
+ When the requested work is complete and there is no concrete unresolved issue, stop. Continue investigating or checking only when new evidence gives a reason to continue.
29
+
30
+ Necessary workspace changes are within the scope of the requested task. Ask the user only for decisions that require the user. Do not commit, push, publish, send, or perform other remote or irreversible actions unless explicitly requested.
31
+
32
+ Requests to share, deliver, hand over, or get work ready do not by themselves authorize a commit, push, publish, or send. Perform those actions only when the user explicitly names the action; otherwise leave the changes in the workspace.
33
+
34
+ Be concise.`;
51
35
  /** The project-instructions file names, in priority order (A area). */
52
36
  const INSTRUCTION_FILES = ["AGENTS.md", "CLAUDE.md"];
53
37
  /** Hard cap for injected instructions — truncate and say so. */
package/dist/dispatch.js CHANGED
@@ -880,7 +880,7 @@ export function dispatch(line, ctx) {
880
880
  if (want !== wasOn)
881
881
  body.dontAskNotice(`don't ask → ${want ? "on" : "off"}`, want);
882
882
  else
883
- body.notice(`don't ask → ${want ? "on" : "off"}`);
883
+ body.dontAskNotice(`don't ask → ${want ? "on" : "off"}`, want, true);
884
884
  ctx.paintIdle();
885
885
  }
886
886
  ctx.input.prompt();
@@ -1087,6 +1087,9 @@ export function dispatch(line, ctx) {
1087
1087
  // session still names the outgoing profile
1088
1088
  const prevProfile = currentProfileName !== null ? configModels[currentProfileName] : undefined;
1089
1089
  const payeeBefore = prevProfile !== undefined ? payeeOf(prevProfile) : null;
1090
+ // the notices round (owner, 2026-10-14): the MODEL row says what it
1091
+ // switched FROM, read before the outgoing binding is replaced
1092
+ const fromName = currentProfileName ?? agentModel;
1090
1093
  ctx.session.setModelBinding(binding);
1091
1094
  setLastBinding(binding);
1092
1095
  setAgentModel(profile.model, profile.baseUrl);
@@ -1117,14 +1120,15 @@ export function dispatch(line, ctx) {
1117
1120
  // (and the owner's) read `gpt-6-astra · CH 92%`, the new
1118
1121
  // model beside the previous model's figure.
1119
1122
  ctx.modelSwitched();
1120
- body.notice(`model → ${profName} (${profile.model}${providerLabel(profile.baseUrl, profile.upstream) === "" ? "" : ` ${providerLabel(profile.baseUrl, profile.upstream)}`}${effortTok !== undefined ? ` · ${effortTok}` : ""}) — takes effect on the next turn`);
1123
+ const hostLabel = providerLabel(profile.baseUrl, profile.upstream);
1124
+ body.modelNotice(`model → ${profName} (${profile.model}${hostLabel === "" ? "" : ` ${hostLabel}`}${effortTok !== undefined ? ` · ${effortTok}` : ""}) — takes effect on the next turn`, fromName, profName, `${profile.model}${hostLabel === "" ? "" : ` ${hostLabel}`}${effortTok !== undefined ? ` \u00b7 ${effortTok}` : ""} \u00b7 takes effect on the next turn`);
1121
1125
  // MP-1 (0.40.7): when the switch changes WHO PAYS — another
1122
1126
  // endpoint, or another credential for it — say so, once,
1123
1127
  // under the switch line. A switch between two models on the
1124
1128
  // same account says nothing more.
1125
1129
  const payeeAfter = payeeOf(profile);
1126
1130
  if (payeeBefore !== null && payeeBefore !== payeeAfter)
1127
- body.notice(`paid by ${payeeAfter} from the next turn — was ${payeeBefore}`);
1131
+ body.metaNotice(`paid by ${payeeAfter} from the next turn — was ${payeeBefore}`, [{ label: "PAYER", sentence: `${payeeAfter} from the next turn \u00b7 was ${payeeBefore}` }]);
1128
1132
  }
1129
1133
  }
1130
1134
  }
package/dist/index.js CHANGED
@@ -1114,7 +1114,7 @@ async function chatLoop(first, firstId, input, autoCompact) {
1114
1114
  return r.status === 0 ? r.stdout.trim() : null;
1115
1115
  });
1116
1116
  if (tmuxHint !== null)
1117
- body.notice(tmuxHint);
1117
+ body.metaNotice(tmuxHint, [{ label: "HINT", sentence: tmuxHint }]);
1118
1118
  void announceUpdate();
1119
1119
  }
1120
1120
  else {
package/dist/replay.d.ts CHANGED
@@ -34,6 +34,7 @@ export interface ReplayBody {
34
34
  isError: boolean;
35
35
  reason?: string | null;
36
36
  untimed?: boolean;
37
+ created?: boolean;
37
38
  }): void;
38
39
  notice(text: string): void;
39
40
  metaNotice(text: string, rows: readonly {
package/dist/replay.js CHANGED
@@ -98,7 +98,8 @@ function replayTurn(body, turn, tasks) {
98
98
  if (m !== null)
99
99
  reason = m[1];
100
100
  }
101
- body.toolResult(String(e.callId), { content: text, isError: e.isError === true, reason, untimed: true });
101
+ const created = (e.tags ?? []).includes("created");
102
+ body.toolResult(String(e.callId), { content: text, isError: e.isError === true, reason, untimed: true, ...(created ? { created: true } : {}) });
102
103
  results.add(String(e.callId));
103
104
  break;
104
105
  }
package/dist/state.d.ts CHANGED
@@ -35,6 +35,7 @@ export declare function codingToolOptions(): {
35
35
  readonly secretEnvNames: readonly string[];
36
36
  readonly protectedFiles: readonly string[];
37
37
  readonly tasks: (sessionId: string | undefined) => ShellTasks | undefined;
38
+ readonly observedRevision: (sessionId: string | undefined, full: string, kind?: "edit" | "write") => string | undefined;
38
39
  readonly limits?: {
39
40
  readonly minForegroundMs: number;
40
41
  };
@@ -441,4 +442,5 @@ export declare function setLastBinding(b: LastBinding): void;
441
442
  export declare function lastBinding(): LastBinding | null;
442
443
  export declare function setAcceptDrift(v: boolean): void;
443
444
  export declare function acceptDrift(): boolean;
445
+ export declare function observedRevisionFromLog(sessionId: string | undefined, full: string, kind?: "edit" | "write"): string | undefined;
444
446
  export {};
package/dist/state.js CHANGED
@@ -8,9 +8,10 @@
8
8
  import { execFileSync } from "node:child_process";
9
9
  import { readdirSync, readFileSync, realpathSync } from "node:fs";
10
10
  import { homedir } from "node:os";
11
- import { dirname, join } from "node:path";
11
+ 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
+ import { projectMessages } from "@vincemakes/kiso-core";
14
15
  import { TaskManager } from "@vincemakes/kiso-runtime/internal";
15
16
  import { processTaskBackend } from "@vincemakes/kiso-tools-node";
16
17
  import { collectWriter } from "@vincemakes/kiso-subagent-ext";
@@ -53,6 +54,7 @@ export function codingToolOptions() {
53
54
  secretEnvNames: secretEnvNamesOf(configModels),
54
55
  protectedFiles: protectedFiles(),
55
56
  tasks: tasksFor,
57
+ observedRevision: observedRevisionFromLog,
56
58
  ...(Number.isFinite(minForeground) && minForeground >= 0 ? { limits: { minForegroundMs: minForeground } } : {}),
57
59
  };
58
60
  }
@@ -638,3 +640,87 @@ export function setAcceptDrift(v) {
638
640
  export function acceptDrift() {
639
641
  return acceptDriftValue;
640
642
  }
643
+ /** O1 — the hidden observed revision. The revision the model last
644
+ * OBSERVED for a file, rebuilt from the session's MODEL-VISIBLE history
645
+ * (`projectMessages` over the durable log, so a receipt cleared by
646
+ * microcompaction or covered by a summary no longer counts): the last
647
+ * [rev:…] of a successful read_file / edit_file / write_file on that
648
+ * file, or of an edit_file stale refusal (it carries the current text).
649
+ * Never the disk's current revision — a stale write stays refused.
650
+ * `kind: "write"` (a whole-file replace) also needs visible whole-file
651
+ * knowledge: an unranged, uncapped read or an own write, followed only
652
+ * by own mutations. The log is per session, so a child session (a
653
+ * subagent) inherits no observation. */
654
+ function realOrResolved(p) {
655
+ try {
656
+ return realpathSync(p);
657
+ }
658
+ catch {
659
+ return p;
660
+ }
661
+ }
662
+ export function observedRevisionFromLog(sessionId, full, kind = "edit") {
663
+ if (sessionId === undefined || activeStoreDir === "")
664
+ return undefined;
665
+ let text;
666
+ try {
667
+ text = readFileSync(join(activeStoreDir, `${sessionId}.jsonl`), "utf8");
668
+ }
669
+ catch {
670
+ return undefined;
671
+ }
672
+ const events = [];
673
+ for (const line of text.split("\n")) {
674
+ if (line === "")
675
+ continue;
676
+ try {
677
+ const e = JSON.parse(line).event;
678
+ if (e !== undefined)
679
+ events.push(e);
680
+ }
681
+ catch { /* a torn tail line */ }
682
+ }
683
+ let messages;
684
+ try {
685
+ messages = projectMessages(events);
686
+ }
687
+ catch {
688
+ return undefined;
689
+ }
690
+ const target = realOrResolved(full);
691
+ const calls = new Map();
692
+ let rev;
693
+ let wholeKnown = false;
694
+ for (const m of messages) {
695
+ if (m.role === "assistant") {
696
+ for (const b of m.blocks) {
697
+ if (b.type === "tool_use" && typeof b.input.path === "string") {
698
+ calls.set(b.callId, { name: b.name, full: realOrResolved(resolve(process.cwd(), b.input.path)), ranged: b.input.offset !== undefined || b.input.limit !== undefined });
699
+ }
700
+ }
701
+ }
702
+ else if (m.role === "tool") {
703
+ const c = calls.get(m.callId);
704
+ if (c === undefined || c.full !== target)
705
+ continue;
706
+ const body = typeof m.content === "string" ? m.content : m.content.map((x) => (x.type === "text" ? x.text : "")).join("");
707
+ const hit = [...body.matchAll(/\[(rev:[0-9a-f]{16})\]/g)].pop();
708
+ if (hit === undefined)
709
+ continue;
710
+ if (!m.isError && (c.name === "read_file" || c.name === "edit_file" || c.name === "write_file")) {
711
+ rev = hit[1];
712
+ if (c.name === "read_file")
713
+ wholeKnown = !c.ranged && !body.includes("(call again with offset=") && !body.includes("[output capped at");
714
+ else if (c.name === "write_file")
715
+ wholeKnown = true;
716
+ }
717
+ else if (m.isError && c.name === "edit_file" && body.includes(" changed since ")) {
718
+ rev = hit[1];
719
+ wholeKnown = false;
720
+ }
721
+ }
722
+ }
723
+ if (kind === "write" && !wholeKnown)
724
+ return undefined;
725
+ return rev;
726
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-code",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "kiso-code \u2014 the coding agent on kiso, the durable runtime for AI agents: kiso chat / kiso resume / kiso sessions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,19 +18,19 @@
18
18
  "test": "vitest run"
19
19
  },
20
20
  "dependencies": {
21
- "@vincemakes/kiso-ask-ext": "0.49.0",
22
- "@vincemakes/kiso-core": "0.49.0",
23
- "@vincemakes/kiso-evals": "0.49.0",
24
- "@vincemakes/kiso-mcp-ext": "0.49.0",
25
- "@vincemakes/kiso-provider-anthropic": "0.49.0",
26
- "@vincemakes/kiso-provider-openai": "0.49.0",
27
- "@vincemakes/kiso-provider-openai-responses": "0.49.0",
28
- "@vincemakes/kiso-runtime": "0.49.0",
29
- "@vincemakes/kiso-skills-ext": "0.49.0",
30
- "@vincemakes/kiso-subagent-ext": "0.49.0",
31
- "@vincemakes/kiso-tools-node": "0.49.0",
32
- "@vincemakes/kiso-tui": "0.49.0",
33
- "@vincemakes/kiso-tui-cells": "0.49.0"
21
+ "@vincemakes/kiso-ask-ext": "0.50.0",
22
+ "@vincemakes/kiso-core": "0.50.0",
23
+ "@vincemakes/kiso-evals": "0.50.0",
24
+ "@vincemakes/kiso-mcp-ext": "0.50.0",
25
+ "@vincemakes/kiso-provider-anthropic": "0.50.0",
26
+ "@vincemakes/kiso-provider-openai": "0.50.0",
27
+ "@vincemakes/kiso-provider-openai-responses": "0.50.0",
28
+ "@vincemakes/kiso-runtime": "0.50.0",
29
+ "@vincemakes/kiso-skills-ext": "0.50.0",
30
+ "@vincemakes/kiso-subagent-ext": "0.50.0",
31
+ "@vincemakes/kiso-tools-node": "0.50.0",
32
+ "@vincemakes/kiso-tui": "0.50.0",
33
+ "@vincemakes/kiso-tui-cells": "0.50.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^26.1.2",