@jskit-ai/assistant-core 0.1.241 → 0.1.243

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.
@@ -8,14 +8,14 @@
8
8
  "build": "vite build"
9
9
  },
10
10
  "dependencies": {
11
- "@jskit-ai/assistant-core": "0.1.241",
12
- "@jskit-ai/http-runtime": "0.1.244",
13
- "@jskit-ai/kernel": "0.1.246",
11
+ "@jskit-ai/assistant-core": "0.1.243",
12
+ "@jskit-ai/http-runtime": "0.1.245",
13
+ "@jskit-ai/kernel": "0.1.247",
14
14
  "@mdi/js": "^7.4.47",
15
15
  "vue": "^3.5.13",
16
16
  "vuetify": "^4.0.0",
17
- "@jskit-ai/connectors-core": "0.1.63",
18
- "@jskit-ai/connectors-catalog": "0.1.65"
17
+ "@jskit-ai/connectors-core": "0.1.64",
18
+ "@jskit-ai/connectors-catalog": "0.1.66"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@vitejs/plugin-vue": "^6.0.7",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant-core",
3
- "version": "0.1.241",
3
+ "version": "0.1.243",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test --test-concurrency=1"
@@ -47,8 +47,8 @@
47
47
  "@ai-sdk/perplexity": "^4.0.43",
48
48
  "@ai-sdk/togetherai": "^3.0.49",
49
49
  "@ai-sdk/xai": "^4.0.58",
50
- "@jskit-ai/resource-core": "0.1.188",
51
- "@jskit-ai/resource-crud-core": "0.1.188",
50
+ "@jskit-ai/resource-core": "0.1.189",
51
+ "@jskit-ai/resource-crud-core": "0.1.189",
52
52
  "@mdi/js": "^7.4.47",
53
53
  "@openrouter/ai-sdk-provider": "^3.0.0",
54
54
  "ai": "^7.0.101",
@@ -57,8 +57,8 @@
57
57
  "ws": "^8.20.1"
58
58
  },
59
59
  "peerDependencies": {
60
- "@jskit-ai/http-runtime": "0.1.244",
61
- "@jskit-ai/kernel": "0.1.246",
60
+ "@jskit-ai/http-runtime": "0.1.245",
61
+ "@jskit-ai/kernel": "0.1.247",
62
62
  "vue": "^3.5.13",
63
63
  "vuetify": "^4.0.0"
64
64
  },
@@ -143,6 +143,19 @@ function claudeHistoryFrames(file) {
143
143
  return reading;
144
144
  }
145
145
 
146
+ // The offset just after the last newline between start and size.
147
+ async function completeHistoryOffset(handle, start, size) {
148
+ const block = Buffer.alloc(64 * 1024);
149
+ for (let end = size; end > start;) {
150
+ const from = Math.max(start, end - block.length);
151
+ await handle.read(block, 0, end - from, from);
152
+ const newline = block.subarray(0, end - from).lastIndexOf(10);
153
+ if (newline !== -1) return from + newline + 1;
154
+ end = from;
155
+ }
156
+ return start;
157
+ }
158
+
146
159
  async function readClaudeHistoryFrames(file) {
147
160
  const cached = historyCache.get(file.path)?.value;
148
161
  const handle = await open(file.path, "r");
@@ -154,18 +167,16 @@ async function readClaudeHistoryFrames(file) {
154
167
  reusable = check.equals(cached.check);
155
168
  }
156
169
  const start = reusable ? cached.offset : 0;
157
- const appended = Buffer.alloc(file.size - start);
158
- if (appended.length) await handle.read(appended, 0, appended.length, start);
159
170
  // Only newline-terminated frames are complete; a writer may still be
160
171
  // writing the unfinished tail, which the next read parses.
161
- const complete = appended.lastIndexOf(10) + 1;
172
+ const offset = await completeHistoryOffset(handle, start, file.size);
162
173
  const added = [];
163
- if (complete) {
164
- for await (const frame of readClaudeJsonFrames([appended.subarray(0, complete)])) {
174
+ if (offset > start) {
175
+ // Streamed, so a long conversation's history is never held whole in memory.
176
+ for await (const frame of readClaudeJsonFrames(handle.createReadStream({ start, end: offset - 1, autoClose: false }))) {
165
177
  if (!frame.isSidechain) added.push(retainedClaudeFrame(frame));
166
178
  }
167
179
  }
168
- const offset = start + complete;
169
180
  const check = Buffer.alloc(Math.min(HISTORY_CHECK_BYTES, offset));
170
181
  await handle.read(check, 0, check.length, offset - check.length);
171
182
  const value = { dev: file.dev, ino: file.ino, offset, check,
@@ -190,7 +201,6 @@ async function readClaudeHistory(options) {
190
201
  ...(options.includeFinalCarriers ? { completedResults: [], failedResults: [], structuredOutputs: [] } : {})
191
202
  };
192
203
  if (!file || file.size === 0) return { exists: false, messages: [], userIds: [], text: "", ...inspected };
193
- if (file.size > 256 * 1024 * 1024) throw new Error("Claude history exceeds the supported read limit.");
194
204
  const messages = new Map();
195
205
  const userIds = [];
196
206
  let lastUserId = "";
@@ -2712,6 +2712,14 @@ test("live Claude history parses only frames appended since the previous read",
2712
2712
  parsed = 0;
2713
2713
  assert.deepEqual((await readClaudeHistory(options)).userIds, ["u1", "u2", "u3"]);
2714
2714
  assert.equal(parsed, 0);
2715
+ // An unfinished frame longer than one look-back block is still left for the next read.
2716
+ const long = JSON.stringify({ type: "user", uuid: "u4", message: { content: "x".repeat(200 * 1024) } });
2717
+ await appendFile(file, long.slice(0, 150 * 1024));
2718
+ assert.deepEqual((await readClaudeHistory(options)).userIds, ["u1", "u2", "u3"]);
2719
+ assert.equal(parsed, 0);
2720
+ await appendFile(file, long.slice(150 * 1024) + "\n");
2721
+ assert.deepEqual((await readClaudeHistory(options)).userIds, ["u1", "u2", "u3", "u4"]);
2722
+ assert.equal(parsed, 1);
2715
2723
  // Replacement and truncation discard the parsed prefix.
2716
2724
  await write(`${file}.next`, [user("r1"), answer("ra", "r1")].join("\n") + "\n");
2717
2725
  await move(`${file}.next`, file);