chat-recall 0.7.15 → 0.7.17

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
@@ -8353,7 +8353,64 @@ var init_opencode = __esm({
8353
8353
  });
8354
8354
 
8355
8355
  // ../engine/dist/core/backends/tail-read.js
8356
- import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as statSync4 } from "node:fs";
8356
+ import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, statSync as statSync4, fstatSync } from "node:fs";
8357
+ function readAt(fd, pos, len) {
8358
+ const buf = Buffer.alloc(len);
8359
+ let got = 0;
8360
+ while (got < len) {
8361
+ const n = readSync2(fd, buf, got, len - got, pos + got);
8362
+ if (n <= 0)
8363
+ break;
8364
+ got += n;
8365
+ }
8366
+ return buf.subarray(0, got);
8367
+ }
8368
+ function linesBeforeOffset(path, offset) {
8369
+ if (offset <= 0)
8370
+ return 0;
8371
+ let fd = null;
8372
+ try {
8373
+ fd = openSync2(path, "r");
8374
+ const ino = fstatSync(fd).ino;
8375
+ const known = lineCounts.get(path);
8376
+ let pos = 0;
8377
+ let lines = 0;
8378
+ if (known && known.ino === ino && known.offset <= offset) {
8379
+ const edgeStart2 = Math.max(0, known.offset - EDGE_BYTES);
8380
+ if (readAt(fd, edgeStart2, known.offset - edgeStart2).equals(known.edge)) {
8381
+ pos = known.offset;
8382
+ lines = known.lines;
8383
+ }
8384
+ }
8385
+ const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
8386
+ while (pos < offset) {
8387
+ const n = readSync2(fd, block, 0, Math.min(SKIP_SCAN_BYTES, offset - pos), pos);
8388
+ if (n <= 0)
8389
+ return void 0;
8390
+ for (let i = block.indexOf(10, 0); i >= 0 && i < n; i = block.indexOf(10, i + 1))
8391
+ lines++;
8392
+ pos += n;
8393
+ }
8394
+ const edgeStart = Math.max(0, offset - EDGE_BYTES);
8395
+ const edge = readAt(fd, edgeStart, offset - edgeStart);
8396
+ if (lineCounts.size >= LINE_COUNT_CACHE_MAX && !lineCounts.has(path)) {
8397
+ const oldest = lineCounts.keys().next().value;
8398
+ if (oldest !== void 0)
8399
+ lineCounts.delete(oldest);
8400
+ }
8401
+ lineCounts.set(path, { offset, lines, ino, edge });
8402
+ return lines;
8403
+ } catch {
8404
+ return void 0;
8405
+ } finally {
8406
+ if (fd !== null) {
8407
+ try {
8408
+ closeSync2(fd);
8409
+ } catch {
8410
+ }
8411
+ }
8412
+ }
8413
+ }
8357
8414
  function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
8358
8415
  let size = 0;
8359
8416
  try {
@@ -8381,7 +8438,7 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
8381
8438
  const lastNl = slice.lastIndexOf(10);
8382
8439
  if (lastNl >= 0) {
8383
8440
  const text = slice.subarray(0, lastNl + 1).toString("utf-8");
8384
- return { text, newOffset: offset + lastNl + 1 };
8441
+ return { text, newOffset: offset + lastNl + 1, baseLine: linesBeforeOffset(path, offset) };
8385
8442
  }
8386
8443
  if (offset + got >= size)
8387
8444
  return { text: "", newOffset: offset };
@@ -8414,12 +8471,15 @@ function findNewline(fd, from, size) {
8414
8471
  }
8415
8472
  return -1;
8416
8473
  }
8417
- var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
8474
+ var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES, lineCounts, LINE_COUNT_CACHE_MAX, EDGE_BYTES;
8418
8475
  var init_tail_read = __esm({
8419
8476
  "../engine/dist/core/backends/tail-read.js"() {
8420
8477
  "use strict";
8421
8478
  TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
8422
8479
  SKIP_SCAN_BYTES = 1024 * 1024;
8480
+ lineCounts = /* @__PURE__ */ new Map();
8481
+ LINE_COUNT_CACHE_MAX = 2048;
8482
+ EDGE_BYTES = 64;
8423
8483
  }
8424
8484
  });
8425
8485
 
@@ -11069,18 +11129,20 @@ function updateShadow(sessionId, exp, prebuilt) {
11069
11129
  const curHash = containerSrcHash(current);
11070
11130
  if (!prior) {
11071
11131
  current.srcHash = curHash;
11132
+ current.mergeVersion = SHADOW_MERGE_VERSION;
11072
11133
  try {
11073
11134
  writeShadowContainer(tool, rawId, current);
11074
11135
  } catch {
11075
11136
  }
11076
11137
  return { status: "created", sessionId, tool, container: current, recovered: 0, path };
11077
11138
  }
11078
- if (prior.srcHash && prior.srcHash === curHash) {
11139
+ if (prior.srcHash && prior.srcHash === curHash && prior.mergeVersion === SHADOW_MERGE_VERSION) {
11079
11140
  return { status: "unchanged", sessionId, tool, container: prior, recovered: 0, path };
11080
11141
  }
11081
11142
  const merged = mergeContainer(prior, current);
11082
- if (merged.status !== "unchanged" || prior.srcHash !== curHash) {
11143
+ if (merged.status !== "unchanged" || prior.srcHash !== curHash || prior.mergeVersion !== SHADOW_MERGE_VERSION) {
11083
11144
  merged.container.srcHash = curHash;
11145
+ merged.container.mergeVersion = SHADOW_MERGE_VERSION;
11084
11146
  try {
11085
11147
  writeShadowContainer(tool, rawId, merged.container);
11086
11148
  } catch {
@@ -11173,7 +11235,7 @@ function shadowSizeOnDisk(tool, rawId) {
11173
11235
  return 0;
11174
11236
  }
11175
11237
  }
11176
- var WHOLE_FILE_TOOLS, STATE_META, lastLiveFingerprint;
11238
+ var WHOLE_FILE_TOOLS, STATE_META, SHADOW_MERGE_VERSION, lastLiveFingerprint;
11177
11239
  var init_shadow = __esm({
11178
11240
  "../engine/dist/transcript/shadow.js"() {
11179
11241
  "use strict";
@@ -11181,6 +11243,7 @@ var init_shadow = __esm({
11181
11243
  init_raw();
11182
11244
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
11183
11245
  STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
11246
+ SHADOW_MERGE_VERSION = 2;
11184
11247
  lastLiveFingerprint = /* @__PURE__ */ new Map();
11185
11248
  }
11186
11249
  });
@@ -18734,6 +18797,7 @@ function persistLedgerData(server, serverData) {
18734
18797
  const data = load();
18735
18798
  data[server] = serverData;
18736
18799
  persist(data);
18800
+ flushLedger();
18737
18801
  }
18738
18802
  function markSynced(server, rows) {
18739
18803
  if (rows.length === 0) return;
@@ -19002,7 +19066,7 @@ var init_http = __esm({
19002
19066
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19003
19067
  } catch {
19004
19068
  try {
19005
- if (true) CLI_VERSION = "0.7.15";
19069
+ if (true) CLI_VERSION = "0.7.17";
19006
19070
  } catch {
19007
19071
  }
19008
19072
  }
@@ -19500,7 +19564,7 @@ var init_telemetry = __esm({
19500
19564
  init_sync_client();
19501
19565
  cliVersion2 = () => {
19502
19566
  try {
19503
- return true ? "0.7.15" : "dev";
19567
+ return true ? "0.7.17" : "dev";
19504
19568
  } catch {
19505
19569
  return "dev";
19506
19570
  }
@@ -20987,7 +21051,8 @@ async function syncToTargetInScope(cred, opts = {}) {
20987
21051
  clearTimeout(timer);
20988
21052
  }
20989
21053
  };
20990
- const postChunkedHead = async (conv, fileSize) => {
21054
+ const postChunkedHead = async (head, fileSize) => {
21055
+ const conv = { ...head, chunk_head: true };
20991
21056
  const headers = { "content-type": "application/json" };
20992
21057
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
20993
21058
  const ac = new AbortController();
@@ -21003,9 +21068,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21003
21068
  const body2 = await res.json().catch(() => ({}));
21004
21069
  const id = conv.session_id;
21005
21070
  const guarded = (body2.shrink_guarded ?? []).find((g) => g.session_id === id);
21071
+ const staged = (body2.rebuild_staged ?? []).includes(id);
21006
21072
  const mtime = conv.mtime;
21007
21073
  try {
21008
- if (!guarded) {
21074
+ if (staged || !guarded) {
21009
21075
  markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21010
21076
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21011
21077
  markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
@@ -21738,8 +21804,13 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
21738
21804
  const backend = getBackendForId(ref.prefixedId);
21739
21805
  if (!backend?.readFromOffset) return null;
21740
21806
  const mapPath = opts.mapPath ?? ((p) => p);
21741
- const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
21807
+ const { text: tailText, newOffset, skippedBytes, baseLine } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
21742
21808
  if (newOffset <= fromOffset) return null;
21809
+ const fileSize = backend.fileSize?.(ref.prefixedId) ?? 0;
21810
+ const position = {
21811
+ ...typeof baseLine === "number" ? { base_line: baseLine } : {},
21812
+ final: fileSize > 0 && newOffset >= fileSize
21813
+ };
21743
21814
  const messages = skippedBytes ? [] : parseTailMessages(ref.toolId, tailText);
21744
21815
  const textMessages = messages.filter((m) => m.role !== "summary" && m.content?.trim());
21745
21816
  const hasContent = textMessages.length > 0 || messages.some((m) => m.toolCalls?.length);
@@ -21752,6 +21823,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
21752
21823
  append: true,
21753
21824
  base_offset: fromOffset,
21754
21825
  from_offset: newOffset,
21826
+ ...position,
21755
21827
  redacted_text: "",
21756
21828
  envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
21757
21829
  mtime: Math.floor(ref.mtime) || 0
@@ -21798,6 +21870,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
21798
21870
  // equals its stored synced-through offset.
21799
21871
  from_offset: newOffset,
21800
21872
  // where it ends → new synced-through offset.
21873
+ ...position,
21801
21874
  redacted_text: redactedText,
21802
21875
  envelope,
21803
21876
  mtime: Math.floor(ref.mtime) || 0
@@ -21881,7 +21954,7 @@ var init_sync_client = __esm({
21881
21954
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
21882
21955
  } catch {
21883
21956
  try {
21884
- if (true) ownVersion = "0.7.15";
21957
+ if (true) ownVersion = "0.7.17";
21885
21958
  } catch {
21886
21959
  }
21887
21960
  }
@@ -26043,7 +26116,7 @@ var init_intent_drain = __esm({
26043
26116
  ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
26044
26117
  } catch {
26045
26118
  try {
26046
- if (true) ownVersion2 = "0.7.15";
26119
+ if (true) ownVersion2 = "0.7.17";
26047
26120
  } catch {
26048
26121
  }
26049
26122
  }
package/dist/mcp-relay.js CHANGED
@@ -192,7 +192,7 @@ function bridge(input, output, sock, exit, opts = {}) {
192
192
  }
193
193
 
194
194
  // src/mcp-relay.ts
195
- var VERSION = true ? "0.7.15" : "0.0.0";
195
+ var VERSION = true ? "0.7.17" : "0.0.0";
196
196
  var SERVER_URL = new URL("./mcp.js", import.meta.url);
197
197
  var SERVER_PATH = fileURLToPath(SERVER_URL);
198
198
  var START_TIMEOUT_MS = 15e3;
package/dist/mcp.js CHANGED
@@ -7108,7 +7108,64 @@ var init_opencode = __esm({
7108
7108
  });
7109
7109
 
7110
7110
  // ../engine/dist/core/backends/tail-read.js
7111
- import { openSync, readSync, closeSync, statSync as statSync6 } from "node:fs";
7111
+ import { openSync, readSync, closeSync, statSync as statSync6, fstatSync } from "node:fs";
7112
+ function readAt(fd, pos, len) {
7113
+ const buf = Buffer.alloc(len);
7114
+ let got = 0;
7115
+ while (got < len) {
7116
+ const n = readSync(fd, buf, got, len - got, pos + got);
7117
+ if (n <= 0)
7118
+ break;
7119
+ got += n;
7120
+ }
7121
+ return buf.subarray(0, got);
7122
+ }
7123
+ function linesBeforeOffset(path2, offset) {
7124
+ if (offset <= 0)
7125
+ return 0;
7126
+ let fd = null;
7127
+ try {
7128
+ fd = openSync(path2, "r");
7129
+ const ino = fstatSync(fd).ino;
7130
+ const known = lineCounts.get(path2);
7131
+ let pos = 0;
7132
+ let lines = 0;
7133
+ if (known && known.ino === ino && known.offset <= offset) {
7134
+ const edgeStart2 = Math.max(0, known.offset - EDGE_BYTES);
7135
+ if (readAt(fd, edgeStart2, known.offset - edgeStart2).equals(known.edge)) {
7136
+ pos = known.offset;
7137
+ lines = known.lines;
7138
+ }
7139
+ }
7140
+ const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
7141
+ while (pos < offset) {
7142
+ const n = readSync(fd, block, 0, Math.min(SKIP_SCAN_BYTES, offset - pos), pos);
7143
+ if (n <= 0)
7144
+ return void 0;
7145
+ for (let i = block.indexOf(10, 0); i >= 0 && i < n; i = block.indexOf(10, i + 1))
7146
+ lines++;
7147
+ pos += n;
7148
+ }
7149
+ const edgeStart = Math.max(0, offset - EDGE_BYTES);
7150
+ const edge = readAt(fd, edgeStart, offset - edgeStart);
7151
+ if (lineCounts.size >= LINE_COUNT_CACHE_MAX && !lineCounts.has(path2)) {
7152
+ const oldest = lineCounts.keys().next().value;
7153
+ if (oldest !== void 0)
7154
+ lineCounts.delete(oldest);
7155
+ }
7156
+ lineCounts.set(path2, { offset, lines, ino, edge });
7157
+ return lines;
7158
+ } catch {
7159
+ return void 0;
7160
+ } finally {
7161
+ if (fd !== null) {
7162
+ try {
7163
+ closeSync(fd);
7164
+ } catch {
7165
+ }
7166
+ }
7167
+ }
7168
+ }
7112
7169
  function readTailFromOffset(path2, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7113
7170
  let size = 0;
7114
7171
  try {
@@ -7136,7 +7193,7 @@ function readTailFromOffset(path2, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7136
7193
  const lastNl = slice.lastIndexOf(10);
7137
7194
  if (lastNl >= 0) {
7138
7195
  const text = slice.subarray(0, lastNl + 1).toString("utf-8");
7139
- return { text, newOffset: offset + lastNl + 1 };
7196
+ return { text, newOffset: offset + lastNl + 1, baseLine: linesBeforeOffset(path2, offset) };
7140
7197
  }
7141
7198
  if (offset + got >= size)
7142
7199
  return { text: "", newOffset: offset };
@@ -7169,12 +7226,15 @@ function findNewline(fd, from, size) {
7169
7226
  }
7170
7227
  return -1;
7171
7228
  }
7172
- var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
7229
+ var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES, lineCounts, LINE_COUNT_CACHE_MAX, EDGE_BYTES;
7173
7230
  var init_tail_read = __esm({
7174
7231
  "../engine/dist/core/backends/tail-read.js"() {
7175
7232
  "use strict";
7176
7233
  TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
7177
7234
  SKIP_SCAN_BYTES = 1024 * 1024;
7235
+ lineCounts = /* @__PURE__ */ new Map();
7236
+ LINE_COUNT_CACHE_MAX = 2048;
7237
+ EDGE_BYTES = 64;
7178
7238
  }
7179
7239
  });
7180
7240
 
@@ -12135,18 +12195,20 @@ function updateShadow(sessionId, exp, prebuilt) {
12135
12195
  const curHash = containerSrcHash(current);
12136
12196
  if (!prior) {
12137
12197
  current.srcHash = curHash;
12198
+ current.mergeVersion = SHADOW_MERGE_VERSION;
12138
12199
  try {
12139
12200
  writeShadowContainer(tool, rawId, current);
12140
12201
  } catch {
12141
12202
  }
12142
12203
  return { status: "created", sessionId, tool, container: current, recovered: 0, path: path2 };
12143
12204
  }
12144
- if (prior.srcHash && prior.srcHash === curHash) {
12205
+ if (prior.srcHash && prior.srcHash === curHash && prior.mergeVersion === SHADOW_MERGE_VERSION) {
12145
12206
  return { status: "unchanged", sessionId, tool, container: prior, recovered: 0, path: path2 };
12146
12207
  }
12147
12208
  const merged = mergeContainer(prior, current);
12148
- if (merged.status !== "unchanged" || prior.srcHash !== curHash) {
12209
+ if (merged.status !== "unchanged" || prior.srcHash !== curHash || prior.mergeVersion !== SHADOW_MERGE_VERSION) {
12149
12210
  merged.container.srcHash = curHash;
12211
+ merged.container.mergeVersion = SHADOW_MERGE_VERSION;
12150
12212
  try {
12151
12213
  writeShadowContainer(tool, rawId, merged.container);
12152
12214
  } catch {
@@ -12184,7 +12246,7 @@ function shadowUncompressedBytes(tool, rawId) {
12184
12246
  }
12185
12247
  }
12186
12248
  }
12187
- var WHOLE_FILE_TOOLS, STATE_META;
12249
+ var WHOLE_FILE_TOOLS, STATE_META, SHADOW_MERGE_VERSION;
12188
12250
  var init_shadow = __esm({
12189
12251
  "../engine/dist/transcript/shadow.js"() {
12190
12252
  "use strict";
@@ -12192,6 +12254,7 @@ var init_shadow = __esm({
12192
12254
  init_raw();
12193
12255
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
12194
12256
  STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
12257
+ SHADOW_MERGE_VERSION = 2;
12195
12258
  }
12196
12259
  });
12197
12260
 
@@ -19648,6 +19711,7 @@ function persistLedgerData(server, serverData) {
19648
19711
  const data = load();
19649
19712
  data[server] = serverData;
19650
19713
  persist(data);
19714
+ flushLedger();
19651
19715
  }
19652
19716
  function markSynced(server, rows) {
19653
19717
  if (rows.length === 0) return;
@@ -19910,7 +19974,7 @@ var init_http = __esm({
19910
19974
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19911
19975
  } catch {
19912
19976
  try {
19913
- if (true) CLI_VERSION = "0.7.15";
19977
+ if (true) CLI_VERSION = "0.7.17";
19914
19978
  } catch {
19915
19979
  }
19916
19980
  }
@@ -20408,7 +20472,7 @@ var init_telemetry = __esm({
20408
20472
  init_sync_client();
20409
20473
  cliVersion2 = () => {
20410
20474
  try {
20411
- return true ? "0.7.15" : "dev";
20475
+ return true ? "0.7.17" : "dev";
20412
20476
  } catch {
20413
20477
  return "dev";
20414
20478
  }
@@ -21677,7 +21741,8 @@ async function syncToTargetInScope(cred, opts = {}) {
21677
21741
  clearTimeout(timer);
21678
21742
  }
21679
21743
  };
21680
- const postChunkedHead = async (conv, fileSize) => {
21744
+ const postChunkedHead = async (head, fileSize) => {
21745
+ const conv = { ...head, chunk_head: true };
21681
21746
  const headers = { "content-type": "application/json" };
21682
21747
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
21683
21748
  const ac = new AbortController();
@@ -21693,9 +21758,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21693
21758
  const body2 = await res.json().catch(() => ({}));
21694
21759
  const id = conv.session_id;
21695
21760
  const guarded = (body2.shrink_guarded ?? []).find((g) => g.session_id === id);
21761
+ const staged = (body2.rebuild_staged ?? []).includes(id);
21696
21762
  const mtime = conv.mtime;
21697
21763
  try {
21698
- if (!guarded) {
21764
+ if (staged || !guarded) {
21699
21765
  markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21700
21766
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21701
21767
  markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
@@ -22428,8 +22494,13 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
22428
22494
  const backend = getBackendForId(ref.prefixedId);
22429
22495
  if (!backend?.readFromOffset) return null;
22430
22496
  const mapPath = opts.mapPath ?? ((p) => p);
22431
- const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
22497
+ const { text: tailText, newOffset, skippedBytes, baseLine } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
22432
22498
  if (newOffset <= fromOffset) return null;
22499
+ const fileSize = backend.fileSize?.(ref.prefixedId) ?? 0;
22500
+ const position = {
22501
+ ...typeof baseLine === "number" ? { base_line: baseLine } : {},
22502
+ final: fileSize > 0 && newOffset >= fileSize
22503
+ };
22433
22504
  const messages = skippedBytes ? [] : parseTailMessages(ref.toolId, tailText);
22434
22505
  const textMessages = messages.filter((m) => m.role !== "summary" && m.content?.trim());
22435
22506
  const hasContent = textMessages.length > 0 || messages.some((m) => m.toolCalls?.length);
@@ -22442,6 +22513,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
22442
22513
  append: true,
22443
22514
  base_offset: fromOffset,
22444
22515
  from_offset: newOffset,
22516
+ ...position,
22445
22517
  redacted_text: "",
22446
22518
  envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
22447
22519
  mtime: Math.floor(ref.mtime) || 0
@@ -22488,6 +22560,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
22488
22560
  // equals its stored synced-through offset.
22489
22561
  from_offset: newOffset,
22490
22562
  // where it ends → new synced-through offset.
22563
+ ...position,
22491
22564
  redacted_text: redactedText,
22492
22565
  envelope,
22493
22566
  mtime: Math.floor(ref.mtime) || 0
@@ -22571,7 +22644,7 @@ var init_sync_client = __esm({
22571
22644
  ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
22572
22645
  } catch {
22573
22646
  try {
22574
- if (true) ownVersion = "0.7.15";
22647
+ if (true) ownVersion = "0.7.17";
22575
22648
  } catch {
22576
22649
  }
22577
22650
  }
@@ -29562,7 +29635,7 @@ function installCrashGuards() {
29562
29635
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
29563
29636
  });
29564
29637
  }
29565
- var RELAY_VERSION = true ? "0.7.15" : "0.0.0";
29638
+ var RELAY_VERSION = true ? "0.7.17" : "0.0.0";
29566
29639
  function daemonIdleSecs() {
29567
29640
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
29568
29641
  if (raw === void 0) return 900;
@@ -6897,7 +6897,64 @@ var init_opencode = __esm({
6897
6897
  });
6898
6898
 
6899
6899
  // ../engine/dist/core/backends/tail-read.js
6900
- import { openSync, readSync, closeSync, statSync as statSync3 } from "node:fs";
6900
+ import { openSync, readSync, closeSync, statSync as statSync3, fstatSync } from "node:fs";
6901
+ function readAt(fd, pos, len) {
6902
+ const buf = Buffer.alloc(len);
6903
+ let got = 0;
6904
+ while (got < len) {
6905
+ const n = readSync(fd, buf, got, len - got, pos + got);
6906
+ if (n <= 0)
6907
+ break;
6908
+ got += n;
6909
+ }
6910
+ return buf.subarray(0, got);
6911
+ }
6912
+ function linesBeforeOffset(path, offset) {
6913
+ if (offset <= 0)
6914
+ return 0;
6915
+ let fd = null;
6916
+ try {
6917
+ fd = openSync(path, "r");
6918
+ const ino = fstatSync(fd).ino;
6919
+ const known = lineCounts.get(path);
6920
+ let pos = 0;
6921
+ let lines = 0;
6922
+ if (known && known.ino === ino && known.offset <= offset) {
6923
+ const edgeStart2 = Math.max(0, known.offset - EDGE_BYTES);
6924
+ if (readAt(fd, edgeStart2, known.offset - edgeStart2).equals(known.edge)) {
6925
+ pos = known.offset;
6926
+ lines = known.lines;
6927
+ }
6928
+ }
6929
+ const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
6930
+ while (pos < offset) {
6931
+ const n = readSync(fd, block, 0, Math.min(SKIP_SCAN_BYTES, offset - pos), pos);
6932
+ if (n <= 0)
6933
+ return void 0;
6934
+ for (let i = block.indexOf(10, 0); i >= 0 && i < n; i = block.indexOf(10, i + 1))
6935
+ lines++;
6936
+ pos += n;
6937
+ }
6938
+ const edgeStart = Math.max(0, offset - EDGE_BYTES);
6939
+ const edge = readAt(fd, edgeStart, offset - edgeStart);
6940
+ if (lineCounts.size >= LINE_COUNT_CACHE_MAX && !lineCounts.has(path)) {
6941
+ const oldest = lineCounts.keys().next().value;
6942
+ if (oldest !== void 0)
6943
+ lineCounts.delete(oldest);
6944
+ }
6945
+ lineCounts.set(path, { offset, lines, ino, edge });
6946
+ return lines;
6947
+ } catch {
6948
+ return void 0;
6949
+ } finally {
6950
+ if (fd !== null) {
6951
+ try {
6952
+ closeSync(fd);
6953
+ } catch {
6954
+ }
6955
+ }
6956
+ }
6957
+ }
6901
6958
  function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
6902
6959
  let size = 0;
6903
6960
  try {
@@ -6925,7 +6982,7 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
6925
6982
  const lastNl = slice.lastIndexOf(10);
6926
6983
  if (lastNl >= 0) {
6927
6984
  const text = slice.subarray(0, lastNl + 1).toString("utf-8");
6928
- return { text, newOffset: offset + lastNl + 1 };
6985
+ return { text, newOffset: offset + lastNl + 1, baseLine: linesBeforeOffset(path, offset) };
6929
6986
  }
6930
6987
  if (offset + got >= size)
6931
6988
  return { text: "", newOffset: offset };
@@ -6958,12 +7015,15 @@ function findNewline(fd, from, size) {
6958
7015
  }
6959
7016
  return -1;
6960
7017
  }
6961
- var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
7018
+ var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES, lineCounts, LINE_COUNT_CACHE_MAX, EDGE_BYTES;
6962
7019
  var init_tail_read = __esm({
6963
7020
  "../engine/dist/core/backends/tail-read.js"() {
6964
7021
  "use strict";
6965
7022
  TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
6966
7023
  SKIP_SCAN_BYTES = 1024 * 1024;
7024
+ lineCounts = /* @__PURE__ */ new Map();
7025
+ LINE_COUNT_CACHE_MAX = 2048;
7026
+ EDGE_BYTES = 64;
6967
7027
  }
6968
7028
  });
6969
7029
 
@@ -1 +1 @@
1
- {"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../src/sync-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../src/sync-client.ts"],"names":[],"mappings":"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"}
@@ -1474,7 +1474,10 @@ async function syncToTargetInScope(cred, opts = {}) {
1474
1474
  // records that offset, so the next tick appends from where the server is.
1475
1475
  // Recording the head's end instead makes every append miss the server's
1476
1476
  // offset, answer full_resync_needed, and send the head again, forever.
1477
- const postChunkedHead = async (conv, fileSize) => {
1477
+ const postChunkedHead = async (head, fileSize) => {
1478
+ // The server stages a head it would otherwise refuse as smaller than its
1479
+ // stored copy, and the appends that follow complete the staged rebuild.
1480
+ const conv = { ...head, chunk_head: true };
1478
1481
  const headers = { 'content-type': 'application/json' };
1479
1482
  if (cred.token)
1480
1483
  headers.authorization = `Bearer ${cred.token}`;
@@ -1489,9 +1492,10 @@ async function syncToTargetInScope(cred, opts = {}) {
1489
1492
  const body = await res.json().catch(() => ({}));
1490
1493
  const id = conv.session_id;
1491
1494
  const guarded = (body.shrink_guarded ?? []).find((g) => g.session_id === id);
1495
+ const staged = (body.rebuild_staged ?? []).includes(id);
1492
1496
  const mtime = conv.mtime;
1493
1497
  try {
1494
- if (!guarded) {
1498
+ if (staged || !guarded) {
1495
1499
  markSynced(base, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
1496
1500
  }
1497
1501
  else if (typeof guarded.o === 'number' && guarded.o > 0 && guarded.o <= fileSize) {
@@ -2739,9 +2743,17 @@ export async function buildConversationTail(ref, fromOffset, opts = {}) {
2739
2743
  if (!backend?.readFromOffset)
2740
2744
  return null;
2741
2745
  const mapPath = opts.mapPath ?? ((p) => p);
2742
- const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
2746
+ const { text: tailText, newOffset, skippedBytes, baseLine } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
2743
2747
  if (newOffset <= fromOffset)
2744
2748
  return null;
2749
+ // The tail's parser numbers its lines from 1. `base_line` turns them into
2750
+ // the file's line numbers on the server; `final` says this tail ends the
2751
+ // file, which completes a rebuild the server staged.
2752
+ const fileSize = backend.fileSize?.(ref.prefixedId) ?? 0;
2753
+ const position = {
2754
+ ...(typeof baseLine === 'number' ? { base_line: baseLine } : {}),
2755
+ final: fileSize > 0 && newOffset >= fileSize,
2756
+ };
2745
2757
  // A window with bytes but nothing to show (tool noise, a skipped line longer
2746
2758
  // than a chunk) still has to move the server's synced-through offset, or the
2747
2759
  // cursor reads the same window on every tick and the rest of a chunked
@@ -2753,7 +2765,7 @@ export async function buildConversationTail(ref, fromOffset, opts = {}) {
2753
2765
  return {
2754
2766
  conv: {
2755
2767
  session_id: ref.prefixedId, tool: ref.toolId, project_path: mapPath(ref.projectPath),
2756
- append: true, base_offset: fromOffset, from_offset: newOffset, redacted_text: '',
2768
+ append: true, base_offset: fromOffset, from_offset: newOffset, ...position, redacted_text: '',
2757
2769
  envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
2758
2770
  mtime: Math.floor(ref.mtime) || 0,
2759
2771
  },
@@ -2802,6 +2814,7 @@ export async function buildConversationTail(ref, fromOffset, opts = {}) {
2802
2814
  base_offset: fromOffset, // where this tail STARTS — server validates it
2803
2815
  // equals its stored synced-through offset.
2804
2816
  from_offset: newOffset, // where it ends → new synced-through offset.
2817
+ ...position,
2805
2818
  redacted_text: redactedText,
2806
2819
  envelope,
2807
2820
  mtime: Math.floor(ref.mtime) || 0,