@vincemakes/kiso-code 0.15.8 → 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
@@ -171,6 +171,9 @@ function editorInput(editor) {
171
171
  onClipboardPaste(cb) {
172
172
  editor.onClipboardPaste(cb);
173
173
  },
174
+ attachments() {
175
+ return editor.attachments();
176
+ },
174
177
  question(query, cb) {
175
178
  editor.question(query, cb);
176
179
  },
package/dist/state.d.ts CHANGED
@@ -80,6 +80,9 @@ export interface LineInput {
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
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.8",
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.8",
22
- "@vincemakes/kiso-core": "0.15.8",
23
- "@vincemakes/kiso-evals": "0.15.8",
24
- "@vincemakes/kiso-mcp-ext": "0.15.8",
25
- "@vincemakes/kiso-provider-anthropic": "0.15.8",
26
- "@vincemakes/kiso-provider-openai": "0.15.8",
27
- "@vincemakes/kiso-runtime": "0.15.8",
28
- "@vincemakes/kiso-skills-ext": "0.15.8",
29
- "@vincemakes/kiso-subagent-ext": "0.15.8",
30
- "@vincemakes/kiso-task-ext": "0.15.8",
31
- "@vincemakes/kiso-tools-node": "0.15.8",
32
- "@vincemakes/kiso-tui": "0.15.8",
33
- "@vincemakes/kiso-tui-cells": "0.15.8"
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",