@llmnesia/mcp 0.2.9 → 0.2.11

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/cli.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __llmnesiaCreateRequire } from "node:module";
3
3
  const require = __llmnesiaCreateRequire(import.meta.url);
4
4
  import {
5
5
  VERSION
6
- } from "./chunk-YT6GNKKG.js";
6
+ } from "./chunk-ZYE6VY6A.js";
7
7
 
8
8
  // src/nodeDownload.ts
9
9
  var NODE_DOWNLOAD_URLS = {
@@ -141,7 +141,7 @@ async function bootstrap() {
141
141
  await guideUnsupportedNodeVersion(process.versions.node);
142
142
  return 1;
143
143
  }
144
- const { main } = await import("./cli-KHY7ADCU.js");
144
+ const { main } = await import("./cli-RQMFEW5J.js");
145
145
  return main();
146
146
  }
147
147
  bootstrap().then((code) => {
@@ -78,9 +78,29 @@ const DEFAULT_CODEX_HOME = process.env.CODEX_HOME || join(homedir(), ".codex");
78
78
  // Codex nests rollouts as sessions/<year>/<month>/<day>/*.jsonl. Bounded so a
79
79
  // misconfigured codexHome can't turn into an unbounded filesystem walk.
80
80
  const CODEX_MAX_DEPTH = 3;
81
- // Conservative chunk size (chars). After JSON-escaping + any multibyte chars the
82
- // on-wire message stays well under native messaging's 1 MB per-message ceiling.
83
- const CHUNK_CHARS = 256 * 1024;
81
+ // Conservative chunk size. Native messaging caps a single message at 1 MB,
82
+ // and the wire size of a chunk is the JSON-ESCAPED UTF-8 length of `data`
83
+ // (an astral character is 4 raw bytes; a literal backslash doubles), so
84
+ // chunks are budgeted by measuring exactly that as they are built: a chunk
85
+ // can never push the framed message over the ceiling no matter what the
86
+ // transcript contains.
87
+ const MAX_CHUNK_ESCAPED_BYTES = 256 * 1024;
88
+ // JSON-escaped UTF-8 byte length of one UTF-16 code unit of `text` as
89
+ // JSON.stringify would emit it: quotes/backslashes and the short escapes
90
+ // double, other control characters become \u00XX (6 bytes), a lone surrogate
91
+ // becomes \udXXX, BMP non-ASCII passes through at its UTF-8 width, and a
92
+ // surrogate PAIR is one 4-byte astral character.
93
+ function escapedUnitBytes(unit, nextUnit) {
94
+ if (unit >= 0xd800 && unit <= 0xdbff) {
95
+ return nextUnit >= 0xdc00 && nextUnit <= 0xdfff ? 4 : 6; // astral char or lone high surrogate
96
+ }
97
+ if (unit >= 0xdc00 && unit <= 0xdfff) return 6; // lone low surrogate
98
+ if (unit === 0x22 || unit === 0x5c) return 2; // " and \
99
+ if (unit < 0x20) return [0x08, 0x09, 0x0a, 0x0c, 0x0d].includes(unit) ? 2 : 6;
100
+ if (unit < 0x80) return 1;
101
+ if (unit < 0x800) return 2;
102
+ return 3;
103
+ }
84
104
  // Matches companion/src/corpus.ts DIR_MODE/FILE_MODE — the corpus holds
85
105
  // plaintext conversation history, so writes here should be no more readable
86
106
  // than the browser profile it was copied out of.
@@ -690,14 +710,31 @@ async function handle(message) {
690
710
  }
691
711
  text = await readFile(match.path, "utf8");
692
712
  }
693
- const total = Math.max(1, Math.ceil(text.length / CHUNK_CHARS));
694
- for (let i = 0, seq = 0; seq < total; i += CHUNK_CHARS, seq++) {
713
+ const chunks = [];
714
+ let chunk = "";
715
+ let chunkBytes = 0;
716
+ for (let i = 0; i < text.length; i++) {
717
+ const unit = text.charCodeAt(i);
718
+ const size = escapedUnitBytes(unit, i + 1 < text.length ? text.charCodeAt(i + 1) : NaN);
719
+ if (chunk && chunkBytes + size > MAX_CHUNK_ESCAPED_BYTES) {
720
+ chunks.push(chunk);
721
+ chunk = "";
722
+ chunkBytes = 0;
723
+ }
724
+ chunk += text[i];
725
+ chunkBytes += size;
726
+ }
727
+ if (chunk) {
728
+ chunks.push(chunk);
729
+ }
730
+ const total = Math.max(1, chunks.length);
731
+ for (let seq = 0; seq < total; seq++) {
695
732
  send({
696
733
  type: "chunk",
697
734
  sessionId: message.sessionId,
698
735
  seq,
699
736
  total,
700
- data: text.slice(i, i + CHUNK_CHARS)
737
+ data: chunks[seq] ?? ""
701
738
  });
702
739
  }
703
740
  send({ type: "sessionDone", sessionId: message.sessionId });
@@ -728,7 +765,16 @@ process.stdin.on("data", (data) => {
728
765
  // Serialize handling so chunk ordering per session is preserved.
729
766
  processing = processing
730
767
  .then(() => handle(message))
731
- .catch((error) => send({ type: "error", error: String((error && error.message) || error) }));
768
+ .catch((error) =>
769
+ // Attribute the failure to the command's session when it names one, so
770
+ // the extension's fetch loop can drop a STALE error that arrives after
771
+ // it gave up on that session instead of failing the next one.
772
+ send({
773
+ type: "error",
774
+ ...(typeof message.sessionId === "string" ? { sessionId: message.sessionId } : {}),
775
+ error: String((error && error.message) || error)
776
+ })
777
+ );
732
778
  }
733
779
  });
734
780
 
Binary file