@vincemakes/kiso-code 0.15.7 → 0.15.9

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.
@@ -38,4 +38,4 @@ export declare function sniff(buf: Buffer): "image/png" | "image/jpeg" | "image/
38
38
  * standing in the text. Silently dropping it would tell the model about
39
39
  * a file it cannot see, which is worse than telling it nothing.
40
40
  */
41
- export declare function attachImages(text: string): string | ContentBlock[];
41
+ export declare function attachImages(text: string, files?: ReadonlyMap<number, string>): string | ContentBlock[];
@@ -83,9 +83,27 @@ function look(path) {
83
83
  * standing in the text. Silently dropping it would tell the model about
84
84
  * a file it cannot see, which is worse than telling it nothing.
85
85
  */
86
- export function attachImages(text) {
86
+ export function attachImages(text, files) {
87
87
  const found = [];
88
88
  const claimed = [];
89
+ // REL-0152-D16: the `[Image #N]` capsules first. The buffer carries a
90
+ // token and the editor carries the file, so a pasted screenshot never
91
+ // puts a path in the line — which is what sent one to the slash-command
92
+ // dispatcher. A capsule whose file has gone, or whose number nobody
93
+ // registered, stays as literal text: the same rule the path route uses,
94
+ // for the same reason.
95
+ if (files !== undefined && files.size > 0) {
96
+ for (const m of text.matchAll(/\[Image #(\d+)\]/g)) {
97
+ const path = files.get(Number(m[1]));
98
+ if (path === undefined)
99
+ continue;
100
+ const hit = look(path);
101
+ if (hit === null)
102
+ continue;
103
+ claimed.push({ start: m.index, end: m.index + m[0].length });
104
+ found.push({ start: m.index, end: m.index + m[0].length, block: { type: "image", sourceType: "base64", mediaType: hit.mediaType, data: hit.data } });
105
+ }
106
+ }
89
107
  for (const re of CANDIDATES) {
90
108
  re.lastIndex = 0;
91
109
  for (const m of text.matchAll(re)) {
@@ -23,7 +23,7 @@
23
23
  * KISO_TRACE_BYTES=/tmp/kiso-bytes.jsonl kiso chat
24
24
  *
25
25
  * One JSON object per line: {ms, dir, n, b} — the stamp relative to the
26
- * trace's start, "out" or "in", the byte count, and the bytes as base64
26
+ * trace's start, "out"/"err"/"in", the byte count, and the bytes as base64
27
27
  * so no escape, control character or partial UTF-8 sequence is altered
28
28
  * on the way to disk. Nothing is interpreted here; interpretation is
29
29
  * what the trace exists to make possible.
@@ -23,7 +23,7 @@
23
23
  * KISO_TRACE_BYTES=/tmp/kiso-bytes.jsonl kiso chat
24
24
  *
25
25
  * One JSON object per line: {ms, dir, n, b} — the stamp relative to the
26
- * trace's start, "out" or "in", the byte count, and the bytes as base64
26
+ * trace's start, "out"/"err"/"in", the byte count, and the bytes as base64
27
27
  * so no escape, control character or partial UTF-8 sequence is altered
28
28
  * on the way to disk. Nothing is interpreted here; interpretation is
29
29
  * what the trace exists to make possible.
@@ -71,11 +71,20 @@ export function armByteTrace() {
71
71
  path = null;
72
72
  return;
73
73
  }
74
- const write = process.stdout.write.bind(process.stdout);
75
- process.stdout.write = ((chunk, ...rest) => {
76
- line("out", chunk);
77
- return write(chunk, ...rest);
78
- });
74
+ // REL-0152-D17: BOTH descriptors. The first version traced stdout
75
+ // alone, and kiso's degradation notices go to stderr — so a capture
76
+ // taken to prove "the bytes leaving kiso are clean" could not see the
77
+ // bytes that were not clean. It supported that conclusion by being
78
+ // blind to the counter-evidence, which is the worst thing an
79
+ // instrument can do. `dir` distinguishes them so a reader can tell
80
+ // which descriptor carried what, and interleave them by timestamp.
81
+ for (const [dir, stream] of [["out", process.stdout], ["err", process.stderr]]) {
82
+ const write = stream.write.bind(stream);
83
+ stream.write = ((chunk, ...rest) => {
84
+ line(dir, chunk);
85
+ return write(chunk, ...rest);
86
+ });
87
+ }
79
88
  process.stdin.on("data", (chunk) => line("in", chunk));
80
89
  // The environment goes in the record FIRST, because kiso emits
81
90
  // DIFFERENT frame bytes per terminal — DEC 2026 synchronized output
package/dist/chat.js CHANGED
@@ -828,7 +828,10 @@ export async function chat(session, faux, input, autoCompact, nav) {
828
828
  // turn without one is byte-identical to before the feature.
829
829
  // Seeded turns are the product's own words and are never
830
830
  // scanned — nothing it writes to itself is an attachment.
831
- const content = seedSource !== undefined ? text : attachImages(text);
831
+ // REL-0152-D16: the capsules' files come from the editor, which
832
+ // is the only thing that knows which number stands for which
833
+ // screenshot.
834
+ const content = seedSource !== undefined ? text : attachImages(text, input.attachments?.());
832
835
  const run = seedSource !== undefined ? session.run(content, { source: seedSource }) : session.run(content);
833
836
  currentRun = run;
834
837
  turnNo += 1;
package/dist/index.js CHANGED
@@ -168,8 +168,11 @@ function editorInput(editor) {
168
168
  onRedirect(cb) {
169
169
  editor.onRedirect(cb);
170
170
  },
171
- onEmptyPaste(cb) {
172
- editor.onEmptyPaste(cb);
171
+ onClipboardPaste(cb) {
172
+ editor.onClipboardPaste(cb);
173
+ },
174
+ attachments() {
175
+ return editor.attachments();
173
176
  },
174
177
  question(query, cb) {
175
178
  editor.question(query, cb);
@@ -768,10 +771,10 @@ async function main() {
768
771
  // the signal to go and look at the clipboard. What comes back is a
769
772
  // PATH, which the turn's attachment scan then picks up exactly as it
770
773
  // would a dragged-in file — one mechanism, two ways of naming a file.
771
- input.onEmptyPaste?.(() => {
774
+ input.onClipboardPaste?.(() => {
772
775
  const shot = clipboardImage(tmpdir());
773
776
  if (shot === null) {
774
- bodyLog("[nothing on the clipboard that kiso can read as an image]");
777
+ bodyLog("[no image on the clipboard ctrl+V attaches one; a file dragged into the window works too]");
775
778
  return null;
776
779
  }
777
780
  return shot;
package/dist/state.d.ts CHANGED
@@ -79,7 +79,10 @@ export interface LineInput {
79
79
  * terminal cannot put binary in a byte stream, so pasting one sends
80
80
  * nothing. The callback returns text to insert (a path) or null.
81
81
  * Optional: the pipe path has no editor and no clipboard. */
82
- onEmptyPaste?(cb: () => string | null): void;
82
+ onClipboardPaste?(cb: () => string | null): void;
83
+ /** REL-0152-D16: which file each `[Image #N]` capsule in the line
84
+ * stands for. Optional — the pipe path has no editor. */
85
+ attachments?(): Map<number, string>;
83
86
  question(query: string, cb: (answer: string) => void): void;
84
87
  cancelQuestion(): void;
85
88
  /** W21: open the approval panel — the editor's state machine takes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-code",
3
- "version": "0.15.7",
3
+ "version": "0.15.9",
4
4
  "description": "kiso CLI \u2014 the durable coding agent that survives kill -9: kiso chat / kiso resume / kiso sessions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,19 +18,19 @@
18
18
  "test": "vitest run"
19
19
  },
20
20
  "dependencies": {
21
- "@vincemakes/kiso-ask-ext": "0.15.7",
22
- "@vincemakes/kiso-core": "0.15.7",
23
- "@vincemakes/kiso-evals": "0.15.7",
24
- "@vincemakes/kiso-mcp-ext": "0.15.7",
25
- "@vincemakes/kiso-provider-anthropic": "0.15.7",
26
- "@vincemakes/kiso-provider-openai": "0.15.7",
27
- "@vincemakes/kiso-runtime": "0.15.7",
28
- "@vincemakes/kiso-skills-ext": "0.15.7",
29
- "@vincemakes/kiso-subagent-ext": "0.15.7",
30
- "@vincemakes/kiso-task-ext": "0.15.7",
31
- "@vincemakes/kiso-tools-node": "0.15.7",
32
- "@vincemakes/kiso-tui": "0.15.7",
33
- "@vincemakes/kiso-tui-cells": "0.15.7"
21
+ "@vincemakes/kiso-ask-ext": "0.15.9",
22
+ "@vincemakes/kiso-core": "0.15.9",
23
+ "@vincemakes/kiso-evals": "0.15.9",
24
+ "@vincemakes/kiso-mcp-ext": "0.15.9",
25
+ "@vincemakes/kiso-provider-anthropic": "0.15.9",
26
+ "@vincemakes/kiso-provider-openai": "0.15.9",
27
+ "@vincemakes/kiso-runtime": "0.15.9",
28
+ "@vincemakes/kiso-skills-ext": "0.15.9",
29
+ "@vincemakes/kiso-subagent-ext": "0.15.9",
30
+ "@vincemakes/kiso-task-ext": "0.15.9",
31
+ "@vincemakes/kiso-tools-node": "0.15.9",
32
+ "@vincemakes/kiso-tui": "0.15.9",
33
+ "@vincemakes/kiso-tui-cells": "0.15.9"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^26.1.2",