@cortexkit/opencode-magic-context 0.16.2 → 0.17.0

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.
Files changed (79) hide show
  1. package/README.md +8 -8
  2. package/dist/features/magic-context/message-index-async.d.ts +12 -0
  3. package/dist/features/magic-context/message-index-async.d.ts.map +1 -0
  4. package/dist/features/magic-context/message-index.d.ts +4 -0
  5. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  6. package/dist/features/magic-context/migrations.d.ts +7 -0
  7. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  8. package/dist/features/magic-context/search.d.ts +2 -2
  9. package/dist/features/magic-context/search.d.ts.map +1 -1
  10. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  11. package/dist/features/magic-context/storage-meta-persisted.d.ts +3 -6
  12. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  13. package/dist/features/magic-context/storage-tags.d.ts +163 -1
  14. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  15. package/dist/features/magic-context/storage.d.ts +1 -1
  16. package/dist/features/magic-context/storage.d.ts.map +1 -1
  17. package/dist/features/magic-context/tagger.d.ts +52 -2
  18. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  19. package/dist/features/magic-context/tool-definition-tokens.d.ts +26 -3
  20. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  21. package/dist/features/magic-context/tool-owner-backfill.d.ts +90 -0
  22. package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -0
  23. package/dist/features/magic-context/types.d.ts +17 -0
  24. package/dist/features/magic-context/types.d.ts.map +1 -1
  25. package/dist/hooks/auto-update-checker/cache.d.ts +12 -1
  26. package/dist/hooks/auto-update-checker/cache.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +23 -0
  29. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/event-payloads.d.ts +8 -0
  32. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  34. package/dist/hooks/magic-context/hook-handlers.d.ts +6 -0
  35. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  36. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/inject-compartments.d.ts +16 -0
  38. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/live-session-state.d.ts +13 -0
  40. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  41. package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/read-session-chunk.d.ts +24 -1
  43. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/read-session-db.d.ts +1 -0
  45. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/read-session-raw.d.ts +1 -0
  47. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/strip-content.d.ts +9 -6
  49. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/tag-messages.d.ts +1 -1
  51. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/tool-drop-target.d.ts +16 -1
  53. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +0 -11
  55. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/transform.d.ts +7 -0
  57. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +2073 -768
  60. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  61. package/dist/plugin/rpc-handlers.d.ts +3 -0
  62. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  63. package/dist/shared/models-dev-cache.d.ts +3 -10
  64. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  65. package/dist/shared/native-binding.d.ts +87 -0
  66. package/dist/shared/native-binding.d.ts.map +1 -0
  67. package/dist/shared/sqlite.d.ts +0 -12
  68. package/dist/shared/sqlite.d.ts.map +1 -1
  69. package/dist/shared/tag-transcript.d.ts.map +1 -1
  70. package/package.json +2 -1
  71. package/src/shared/conflict-detector.ts +1 -1
  72. package/src/shared/models-dev-cache.test.ts +64 -57
  73. package/src/shared/models-dev-cache.ts +49 -68
  74. package/src/shared/native-binding.ts +311 -0
  75. package/src/shared/sqlite.ts +57 -14
  76. package/src/shared/tag-transcript.ts +137 -126
  77. package/src/tui/index.tsx +2 -2
  78. package/dist/hooks/magic-context/reasoning-capability.d.ts +0 -23
  79. package/dist/hooks/magic-context/reasoning-capability.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -147149,6 +147149,74 @@ var init_read_session_formatting = __esm(() => {
147149
147149
  tokenizer = new src_default(exports_claude);
147150
147150
  });
147151
147151
 
147152
+ // src/features/magic-context/tool-definition-tokens.ts
147153
+ function keyFor(providerID, modelID, agentName) {
147154
+ const agent = agentName && agentName.length > 0 ? agentName : "default";
147155
+ return `${providerID}/${modelID}/${agent}`;
147156
+ }
147157
+ function setDatabase(db) {
147158
+ persistenceDb = db;
147159
+ }
147160
+ function loadToolDefinitionMeasurements(db) {
147161
+ let rows = [];
147162
+ try {
147163
+ rows = db.prepare("SELECT provider_id, model_id, agent_name, tool_id, token_count FROM tool_definition_measurements").all();
147164
+ } catch {
147165
+ return;
147166
+ }
147167
+ for (const row of rows) {
147168
+ const key = keyFor(row.provider_id, row.model_id, row.agent_name);
147169
+ let inner = measurements.get(key);
147170
+ if (!inner) {
147171
+ inner = new Map;
147172
+ measurements.set(key, inner);
147173
+ }
147174
+ inner.set(row.tool_id, row.token_count);
147175
+ }
147176
+ }
147177
+ function recordToolDefinition(providerID, modelID, agentName, toolID, description, parameters) {
147178
+ if (!providerID || !modelID || !toolID)
147179
+ return;
147180
+ const key = keyFor(providerID, modelID, agentName);
147181
+ let paramsText = "";
147182
+ try {
147183
+ paramsText = parameters === undefined ? "" : JSON.stringify(parameters);
147184
+ } catch {
147185
+ paramsText = "";
147186
+ }
147187
+ const tokens = estimateTokens(description ?? "") + estimateTokens(paramsText);
147188
+ let inner = measurements.get(key);
147189
+ if (!inner) {
147190
+ inner = new Map;
147191
+ measurements.set(key, inner);
147192
+ }
147193
+ inner.set(toolID, tokens);
147194
+ if (persistenceDb) {
147195
+ try {
147196
+ const agent = agentName && agentName.length > 0 ? agentName : "default";
147197
+ persistenceDb.prepare(`INSERT OR REPLACE INTO tool_definition_measurements
147198
+ (provider_id, model_id, agent_name, tool_id, token_count, recorded_at)
147199
+ VALUES (?, ?, ?, ?, ?, ?)`).run(providerID, modelID, agent, toolID, tokens, Date.now());
147200
+ } catch {}
147201
+ }
147202
+ }
147203
+ function getMeasuredToolDefinitionTokens(providerID, modelID, agentName) {
147204
+ if (!providerID || !modelID)
147205
+ return;
147206
+ const inner = measurements.get(keyFor(providerID, modelID, agentName));
147207
+ if (!inner || inner.size === 0)
147208
+ return;
147209
+ let total = 0;
147210
+ for (const tokens of inner.values())
147211
+ total += tokens;
147212
+ return total;
147213
+ }
147214
+ var measurements, persistenceDb = null;
147215
+ var init_tool_definition_tokens = __esm(() => {
147216
+ init_read_session_formatting();
147217
+ measurements = new Map;
147218
+ });
147219
+
147152
147220
  // ../../node_modules/.bun/esprima@4.0.1/node_modules/esprima/dist/esprima.js
147153
147221
  var require_esprima = __commonJS((exports, module) => {
147154
147222
  (function webpackUniversalModuleDefinition(root, factory) {
@@ -155708,7 +155776,7 @@ function formatConflictShort(result) {
155708
155776
  "",
155709
155777
  ...result.reasons.map((r) => `• ${r}`),
155710
155778
  "",
155711
- "Fix: run `bunx --bun @cortexkit/opencode-magic-context@latest doctor`"
155779
+ "Fix: run `npx @cortexkit/opencode-magic-context@latest doctor`"
155712
155780
  ];
155713
155781
  return lines.join(`
155714
155782
  `);
@@ -156108,14 +156176,334 @@ function safeString(value) {
156108
156176
  }
156109
156177
  }
156110
156178
 
156179
+ // ../../node_modules/.bun/nanotar@0.3.0/node_modules/nanotar/dist/index.mjs
156180
+ function parseTar(data, opts) {
156181
+ const buffer2 = data.buffer || data;
156182
+ const files = [];
156183
+ let offset = 0;
156184
+ let nextExtendedHeader;
156185
+ let globalExtendedHeader;
156186
+ while (offset < buffer2.byteLength - 512) {
156187
+ let name2 = _readString(buffer2, offset, 100);
156188
+ if (name2.length === 0) {
156189
+ break;
156190
+ }
156191
+ if (nextExtendedHeader) {
156192
+ const longName = nextExtendedHeader.path || nextExtendedHeader.linkpath;
156193
+ if (longName) {
156194
+ name2 = longName;
156195
+ }
156196
+ }
156197
+ const mode = _readString(buffer2, offset + 100, 8).trim();
156198
+ const uid = Number.parseInt(_readString(buffer2, offset + 108, 8));
156199
+ const gid = Number.parseInt(_readString(buffer2, offset + 116, 8));
156200
+ const size = _readNumber(buffer2, offset + 124, 12);
156201
+ const seek = 512 + 512 * Math.trunc(size / 512) + (size % 512 ? 512 : 0);
156202
+ const mtime = _readNumber(buffer2, offset + 136, 12);
156203
+ const _type = _readString(buffer2, offset + 156, 1) || "0";
156204
+ const type = tarItemTypeMap[_type] || _type;
156205
+ switch (type) {
156206
+ case "extendedHeader":
156207
+ case "globalExtendedHeader": {
156208
+ const headers = _parseExtendedHeaders(new Uint8Array(buffer2, offset + 512, size));
156209
+ if (type === "extendedHeader") {
156210
+ nextExtendedHeader = headers;
156211
+ } else {
156212
+ nextExtendedHeader = undefined;
156213
+ globalExtendedHeader = {
156214
+ ...globalExtendedHeader,
156215
+ ...headers
156216
+ };
156217
+ }
156218
+ offset += seek;
156219
+ continue;
156220
+ }
156221
+ case "gnuLongFileName":
156222
+ case "gnuOldLongFileName":
156223
+ case "gnuLongLinkName": {
156224
+ nextExtendedHeader = { path: _readString(buffer2, offset + 512, size) };
156225
+ offset += seek;
156226
+ continue;
156227
+ }
156228
+ }
156229
+ const user = _readString(buffer2, offset + 265, 32);
156230
+ const group = _readString(buffer2, offset + 297, 32);
156231
+ name2 = _sanitizePath(name2);
156232
+ const meta3 = {
156233
+ name: name2,
156234
+ type,
156235
+ size,
156236
+ attrs: {
156237
+ ...globalExtendedHeader,
156238
+ ...nextExtendedHeader,
156239
+ mode,
156240
+ uid,
156241
+ gid,
156242
+ mtime,
156243
+ user,
156244
+ group
156245
+ }
156246
+ };
156247
+ nextExtendedHeader = undefined;
156248
+ if (opts?.filter && !opts.filter(meta3)) {
156249
+ offset += seek;
156250
+ continue;
156251
+ }
156252
+ if (opts?.metaOnly) {
156253
+ files.push(meta3);
156254
+ offset += seek;
156255
+ continue;
156256
+ }
156257
+ const data2 = size === 0 ? undefined : new Uint8Array(buffer2, offset + 512, size);
156258
+ files.push({
156259
+ ...meta3,
156260
+ data: data2,
156261
+ get text() {
156262
+ return new TextDecoder().decode(this.data);
156263
+ }
156264
+ });
156265
+ offset += seek;
156266
+ }
156267
+ return files;
156268
+ }
156269
+ async function parseTarGzip(data, opts = {}) {
156270
+ const stream = new ReadableStream({
156271
+ start(controller) {
156272
+ controller.enqueue(new Uint8Array(data));
156273
+ controller.close();
156274
+ }
156275
+ }).pipeThrough(new DecompressionStream(opts.compression ?? "gzip"));
156276
+ const decompressedData = await new Response(stream).arrayBuffer();
156277
+ return parseTar(decompressedData, opts);
156278
+ }
156279
+ function _sanitizePath(path3) {
156280
+ let normalized = path3.replace(/\\/g, "/");
156281
+ normalized = normalized.replace(/^[a-zA-Z]:\//, "");
156282
+ normalized = normalized.replace(/^\/+/, "");
156283
+ const hasLeadingDotSlash = normalized.startsWith("./");
156284
+ const parts = normalized.split("/");
156285
+ const resolved = [];
156286
+ for (const part of parts) {
156287
+ if (part === "..") {
156288
+ resolved.pop();
156289
+ } else if (part !== "." && part !== "") {
156290
+ resolved.push(part);
156291
+ }
156292
+ }
156293
+ let result = resolved.join("/");
156294
+ if (hasLeadingDotSlash && !result.startsWith("./")) {
156295
+ result = "./" + result;
156296
+ }
156297
+ if (path3.endsWith("/") && !result.endsWith("/")) {
156298
+ result += "/";
156299
+ }
156300
+ return result;
156301
+ }
156302
+ function _readString(buffer2, offset, size) {
156303
+ const view = new Uint8Array(buffer2, offset, size);
156304
+ const i = view.indexOf(0);
156305
+ const td = new TextDecoder;
156306
+ return td.decode(i === -1 ? view : view.slice(0, i));
156307
+ }
156308
+ function _readNumber(buffer2, offset, size) {
156309
+ const view = new Uint8Array(buffer2, offset, size);
156310
+ let str = "";
156311
+ for (let i = 0;i < size; i++) {
156312
+ str += String.fromCodePoint(view[i]);
156313
+ }
156314
+ return Number.parseInt(str, 8);
156315
+ }
156316
+ function _parseExtendedHeaders(data) {
156317
+ const dataStr = new TextDecoder().decode(data);
156318
+ const headers = {};
156319
+ for (const line of dataStr.split(`
156320
+ `)) {
156321
+ const s = line.split(" ")[1]?.split("=");
156322
+ if (s) {
156323
+ headers[s[0]] = s[1];
156324
+ }
156325
+ }
156326
+ return headers;
156327
+ }
156328
+ var tarItemTypeMap;
156329
+ var init_dist2 = __esm(() => {
156330
+ tarItemTypeMap = {
156331
+ "0": "file",
156332
+ "1": "hardLink",
156333
+ "2": "symbolicLink",
156334
+ "3": "characterDevice",
156335
+ "4": "blockDevice",
156336
+ "5": "directory",
156337
+ "6": "fifo",
156338
+ "7": "contiguousFile",
156339
+ g: "globalExtendedHeader",
156340
+ x: "extendedHeader",
156341
+ D: "gnuDirectory",
156342
+ I: "gnuInodeMetadata",
156343
+ K: "gnuLongLinkName",
156344
+ L: "gnuLongFileName",
156345
+ N: "gnuOldLongFileName",
156346
+ M: "gnuMultiVolume",
156347
+ S: "gnuSparseFile",
156348
+ E: "gnuExtendedSparse",
156349
+ A: "solarisAcl",
156350
+ V: "solarisVolumeLabel",
156351
+ X: "solarisOldExtendedHeader"
156352
+ };
156353
+ });
156354
+
156355
+ // src/shared/native-binding.ts
156356
+ var exports_native_binding = {};
156357
+ __export(exports_native_binding, {
156358
+ resolveBetterSqliteNativeBinding: () => resolveBetterSqliteNativeBinding
156359
+ });
156360
+ import { existsSync as existsSync7, mkdirSync, writeFileSync as writeFileSync3 } from "node:fs";
156361
+ import { createRequire } from "node:module";
156362
+ import * as path3 from "node:path";
156363
+ function logInfo(message) {
156364
+ log(`${PREFIX} ${message}`);
156365
+ }
156366
+ function logWarn(message) {
156367
+ log(`${PREFIX} WARN ${message}`);
156368
+ }
156369
+ function probeAbi(binaryPath) {
156370
+ const expected = process.versions.modules;
156371
+ try {
156372
+ const sandbox = { exports: {} };
156373
+ process.dlopen(sandbox, binaryPath);
156374
+ return { ok: true };
156375
+ } catch (err) {
156376
+ const message = err instanceof Error ? err.message : String(err);
156377
+ const pair = message.match(/NODE_MODULE_VERSION (\d+).*NODE_MODULE_VERSION (\d+)/s);
156378
+ if (pair?.[1] && pair[2]) {
156379
+ return { ok: false, expected: pair[2], actual: pair[1] };
156380
+ }
156381
+ const single = message.match(/NODE_MODULE_VERSION[ =:]+(\d+)/);
156382
+ if (single?.[1]) {
156383
+ return { ok: false, expected, actual: single[1] };
156384
+ }
156385
+ logWarn(`could not parse ABI from dlopen error: ${message}`);
156386
+ return { ok: false, expected, actual: null };
156387
+ }
156388
+ }
156389
+ function resolveBetterSqlite3OnDisk(requireFn) {
156390
+ try {
156391
+ const pkgJsonPath = requireFn.resolve("better-sqlite3/package.json");
156392
+ const pkgDir = path3.dirname(pkgJsonPath);
156393
+ const pkgJson = requireFn(pkgJsonPath);
156394
+ const binaryPath = path3.join(pkgDir, "build", "Release", "better_sqlite3.node");
156395
+ return { binaryPath, pkgVersion: pkgJson.version };
156396
+ } catch (err) {
156397
+ logWarn(`could not resolve better-sqlite3 in node_modules: ${err instanceof Error ? err.message : String(err)}`);
156398
+ return null;
156399
+ }
156400
+ }
156401
+ function getCachedBinaryPath(pkgVersion, abi) {
156402
+ return path3.join(getCacheDir(), "cortexkit", "native-bindings", "better-sqlite3", `v${pkgVersion}`, `electron-v${abi}-${process.platform}-${process.arch}`, "better_sqlite3.node");
156403
+ }
156404
+ async function downloadElectronPrebuild(pkgVersion, abi) {
156405
+ const filename = `better-sqlite3-v${pkgVersion}-electron-v${abi}-${process.platform}-${process.arch}.tar.gz`;
156406
+ const url2 = `https://github.com/WiseLibs/better-sqlite3/releases/download/v${pkgVersion}/${filename}`;
156407
+ logInfo(`downloading ${url2}`);
156408
+ const response = await fetch(url2, {
156409
+ redirect: "follow",
156410
+ headers: { "User-Agent": "magic-context-plugin/native-binding" }
156411
+ });
156412
+ if (!response.ok) {
156413
+ throw new Error(`failed to download Electron prebuild (HTTP ${response.status} ${response.statusText}) from ${url2}`);
156414
+ }
156415
+ const tarballBytes = new Uint8Array(await response.arrayBuffer());
156416
+ logInfo(`downloaded ${(tarballBytes.length / 1024).toFixed(1)} KB; extracting`);
156417
+ const files = await parseTarGzip(tarballBytes);
156418
+ const nodeFile = files.find((f) => f.name.endsWith("better_sqlite3.node"));
156419
+ if (!nodeFile?.data) {
156420
+ const names = files.map((f) => f.name).join(", ");
156421
+ throw new Error(`Electron prebuild tarball did not contain better_sqlite3.node; got: [${names}]`);
156422
+ }
156423
+ return nodeFile.data instanceof Uint8Array ? nodeFile.data : new Uint8Array(nodeFile.data);
156424
+ }
156425
+ async function resolveBetterSqliteNativeBinding() {
156426
+ if (!process.versions.electron) {
156427
+ return null;
156428
+ }
156429
+ if (inFlight) {
156430
+ return inFlight;
156431
+ }
156432
+ const promise2 = (async () => {
156433
+ const expected = process.versions.modules;
156434
+ logInfo(`Electron detected (v${process.versions.electron}, NODE_MODULE_VERSION ${expected}); verifying better-sqlite3 binding`);
156435
+ const requireFn = createRequire(import.meta.url);
156436
+ const resolved = resolveBetterSqlite3OnDisk(requireFn);
156437
+ if (!resolved) {
156438
+ return null;
156439
+ }
156440
+ const { binaryPath: diskPath, pkgVersion } = resolved;
156441
+ if (existsSync7(diskPath)) {
156442
+ const diskProbe = probeAbi(diskPath);
156443
+ if (diskProbe.ok) {
156444
+ logInfo(`on-disk binary already matches Electron ABI v${expected}; using default bindings() lookup`);
156445
+ return null;
156446
+ }
156447
+ logInfo(`on-disk binary ABI ${diskProbe.actual ?? "unknown"} != required ${expected}; will use Electron prebuild`);
156448
+ } else {
156449
+ logWarn(`expected better-sqlite3 binary not found at ${diskPath}; will fetch Electron prebuild anyway`);
156450
+ }
156451
+ const cachedPath = getCachedBinaryPath(pkgVersion, expected);
156452
+ if (existsSync7(cachedPath)) {
156453
+ const cachedProbe = probeAbi(cachedPath);
156454
+ if (cachedProbe.ok) {
156455
+ logInfo(`using cached Electron prebuild at ${cachedPath}`);
156456
+ return cachedPath;
156457
+ }
156458
+ logWarn(`cached binary at ${cachedPath} has wrong ABI (${cachedProbe.actual ?? "unknown"} != ${expected}); refetching`);
156459
+ }
156460
+ mkdirSync(path3.dirname(cachedPath), { recursive: true });
156461
+ const nodeFileBytes = await downloadElectronPrebuild(pkgVersion, expected);
156462
+ writeFileSync3(cachedPath, nodeFileBytes);
156463
+ logInfo(`cached Electron prebuild at ${cachedPath} (${(nodeFileBytes.length / 1024).toFixed(1)} KB)`);
156464
+ const finalProbe = probeAbi(cachedPath);
156465
+ if (!finalProbe.ok) {
156466
+ throw new Error(`downloaded Electron prebuild has wrong ABI (${finalProbe.actual ?? "unknown"} != ${expected}); refusing to use`);
156467
+ }
156468
+ return cachedPath;
156469
+ })();
156470
+ inFlight = promise2;
156471
+ try {
156472
+ return await promise2;
156473
+ } finally {
156474
+ if (inFlight === promise2) {
156475
+ inFlight = null;
156476
+ }
156477
+ }
156478
+ }
156479
+ var PREFIX = "[native-binding]", inFlight = null;
156480
+ var init_native_binding = __esm(() => {
156481
+ init_dist2();
156482
+ init_data_path();
156483
+ init_logger();
156484
+ });
156485
+
156111
156486
  // src/shared/sqlite.ts
156112
- var isBun, bunSpec, betterSpec, sqliteModule, DatabaseImpl, Database;
156487
+ var isBun, bunSpec, betterSpec, electronNativeBinding, sqliteModule, RawDatabaseImpl, DatabaseImpl, Database;
156113
156488
  var init_sqlite = __esm(async () => {
156114
156489
  isBun = typeof process !== "undefined" && typeof process.versions?.bun === "string";
156115
156490
  bunSpec = "bun:" + "sqlite";
156116
156491
  betterSpec = "better-" + "sqlite3";
156492
+ electronNativeBinding = isBun ? null : await (async () => {
156493
+ const mod = await Promise.resolve().then(() => (init_native_binding(), exports_native_binding));
156494
+ return mod.resolveBetterSqliteNativeBinding();
156495
+ })();
156117
156496
  sqliteModule = isBun ? await import(bunSpec) : await import(betterSpec);
156118
- DatabaseImpl = isBun ? sqliteModule.Database : sqliteModule.default;
156497
+ RawDatabaseImpl = isBun ? sqliteModule.Database : sqliteModule.default;
156498
+ DatabaseImpl = electronNativeBinding == null ? RawDatabaseImpl : class DatabaseWithElectronBinding extends RawDatabaseImpl {
156499
+ constructor(filename, options) {
156500
+ const fallback = electronNativeBinding;
156501
+ super(filename, {
156502
+ ...options,
156503
+ nativeBinding: options?.nativeBinding ?? fallback
156504
+ });
156505
+ }
156506
+ };
156119
156507
  Database = DatabaseImpl;
156120
156508
  });
156121
156509
 
@@ -156847,9 +157235,9 @@ __export(exports_read_session_db, {
156847
157235
  findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
156848
157236
  closeReadOnlySessionDb: () => closeReadOnlySessionDb
156849
157237
  });
156850
- import { join as join10 } from "node:path";
157238
+ import { join as join11 } from "node:path";
156851
157239
  function getOpenCodeDbPath() {
156852
- return join10(getDataDir(), "opencode", "opencode.db");
157240
+ return join11(getDataDir(), "opencode", "opencode.db");
156853
157241
  }
156854
157242
  function closeCachedReadOnlyDb() {
156855
157243
  if (!cachedReadOnlyDb) {
@@ -156908,7 +157296,8 @@ function findLastAssistantModelFromOpenCodeDb(sessionId) {
156908
157296
  try {
156909
157297
  return withReadOnlySessionDb((db) => {
156910
157298
  const row = db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
156911
- json_extract(data, '$.modelID') as modelID
157299
+ json_extract(data, '$.modelID') as modelID,
157300
+ json_extract(data, '$.agent') as agent
156912
157301
  FROM message
156913
157302
  WHERE session_id = ?
156914
157303
  AND json_extract(data, '$.role') = 'assistant'
@@ -156919,7 +157308,12 @@ function findLastAssistantModelFromOpenCodeDb(sessionId) {
156919
157308
  if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
156920
157309
  return null;
156921
157310
  }
156922
- return { providerID: row.providerID, modelID: row.modelID };
157311
+ const agent = typeof row.agent === "string" && row.agent.length > 0 ? row.agent : undefined;
157312
+ return {
157313
+ providerID: row.providerID,
157314
+ modelID: row.modelID,
157315
+ ...agent ? { agent } : {}
157316
+ };
156923
157317
  });
156924
157318
  } catch (error48) {
156925
157319
  log("[magic-context] failed to recover live model from OpenCode DB:", error48);
@@ -156951,9 +157345,9 @@ function cosineSimilarity(a, b) {
156951
157345
  }
156952
157346
 
156953
157347
  // src/features/magic-context/memory/embedding-local.ts
156954
- import { mkdirSync } from "node:fs";
157348
+ import { mkdirSync as mkdirSync2 } from "node:fs";
156955
157349
  import { open, stat, unlink, writeFile } from "node:fs/promises";
156956
- import { join as join12 } from "node:path";
157350
+ import { join as join13 } from "node:path";
156957
157351
  async function acquireModelLoadLock(lockPath) {
156958
157352
  const waitStart = Date.now();
156959
157353
  while (true) {
@@ -157087,15 +157481,15 @@ class LocalEmbeddingProvider {
157087
157481
  if (LogLevel && "ERROR" in LogLevel) {
157088
157482
  env.logLevel = LogLevel.ERROR;
157089
157483
  }
157090
- const modelCacheDir = join12(getMagicContextStorageDir(), "models");
157484
+ const modelCacheDir = join13(getMagicContextStorageDir(), "models");
157091
157485
  try {
157092
- mkdirSync(modelCacheDir, { recursive: true });
157486
+ mkdirSync2(modelCacheDir, { recursive: true });
157093
157487
  env.cacheDir = modelCacheDir;
157094
157488
  } catch {
157095
157489
  log("[magic-context] could not create model cache dir, using library default");
157096
157490
  }
157097
157491
  const createPipeline = transformersModule.pipeline;
157098
- const lockPath = join12(modelCacheDir, ".load.lock");
157492
+ const lockPath = join13(modelCacheDir, ".load.lock");
157099
157493
  const releaseLock = await acquireModelLoadLock(lockPath);
157100
157494
  const stopHeartbeat = startLockHeartbeat(lockPath);
157101
157495
  try {
@@ -157666,7 +158060,7 @@ var init_storage_memory_fts = __esm(() => {
157666
158060
  // src/features/magic-context/memory/project-identity.ts
157667
158061
  import { execSync } from "node:child_process";
157668
158062
  import { createHash as createHash2 } from "node:crypto";
157669
- import path3 from "node:path";
158063
+ import path4 from "node:path";
157670
158064
  function getRootCommitHash(directory) {
157671
158065
  try {
157672
158066
  const hash2 = execSync("git rev-list --max-parents=0 HEAD", {
@@ -157683,12 +158077,12 @@ function getRootCommitHash(directory) {
157683
158077
  }
157684
158078
  }
157685
158079
  function directoryFallback(directory) {
157686
- const canonical = path3.resolve(directory);
158080
+ const canonical = path4.resolve(directory);
157687
158081
  const hash2 = createHash2("md5").update(canonical).digest("hex").slice(0, 12);
157688
158082
  return `dir:${hash2}`;
157689
158083
  }
157690
158084
  function resolveProjectIdentity(directory) {
157691
- const resolved = path3.resolve(directory);
158085
+ const resolved = path4.resolve(directory);
157692
158086
  const cached2 = resolvedCache.get(resolved);
157693
158087
  if (cached2 !== undefined) {
157694
158088
  return cached2;
@@ -157822,6 +158216,33 @@ function readRawSessionMessagesFromDb(db, sessionId) {
157822
158216
  };
157823
158217
  });
157824
158218
  }
158219
+ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
158220
+ const row = db.prepare("SELECT id, data, time_created FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
158221
+ if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number") {
158222
+ return null;
158223
+ }
158224
+ const info = parseJsonRecord(row.data);
158225
+ if (!info || info.summary === true && info.finish === "stop") {
158226
+ return null;
158227
+ }
158228
+ const ordinalRow = db.prepare(`SELECT COUNT(*) AS ordinal FROM message
158229
+ WHERE session_id = ?
158230
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
158231
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
158232
+ AND (time_created < ? OR (time_created = ? AND id <= ?))`).get(sessionId, row.time_created, row.time_created, messageId);
158233
+ const ordinal = typeof ordinalRow?.ordinal === "number" ? ordinalRow.ordinal : 0;
158234
+ if (ordinal <= 0) {
158235
+ return null;
158236
+ }
158237
+ const partRows = db.prepare("SELECT message_id, data FROM part WHERE session_id = ? AND message_id = ? ORDER BY time_created ASC, id ASC").all(sessionId, messageId).filter(isRawPartRow);
158238
+ const role = typeof info.role === "string" ? info.role : "unknown";
158239
+ return {
158240
+ ordinal,
158241
+ id: row.id,
158242
+ role,
158243
+ parts: partRows.map((part) => parseJsonUnknown(part.data))
158244
+ };
158245
+ }
157825
158246
 
157826
158247
  // src/hooks/magic-context/tag-content-primitives.ts
157827
158248
  function byteSize(value) {
@@ -157881,86 +158302,339 @@ var init_tag_part_guards = __esm(() => {
157881
158302
  init_tag_content_primitives();
157882
158303
  });
157883
158304
 
157884
- // src/hooks/magic-context/read-session-chunk.ts
157885
- function cleanUserText(text) {
157886
- return removeSystemReminders(text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
158305
+ // src/hooks/magic-context/tool-drop-target.ts
158306
+ function isToolCallId(value) {
158307
+ return typeof value === "string" && value.length > 0;
157887
158308
  }
157888
- function withRawSessionMessageCache(fn) {
157889
- const outerCache = activeRawMessageCache;
157890
- if (!outerCache) {
157891
- activeRawMessageCache = new Map;
158309
+ function getToolContent(part) {
158310
+ if (!isRecord(part))
158311
+ return;
158312
+ if (part.type === "tool" && isRecord(part.state)) {
158313
+ return typeof part.state.output === "string" ? part.state.output : undefined;
157892
158314
  }
157893
- try {
157894
- return fn();
157895
- } finally {
157896
- if (!outerCache) {
157897
- activeRawMessageCache = null;
157898
- }
158315
+ if (part.type === "tool_result") {
158316
+ return typeof part.content === "string" ? part.content : undefined;
157899
158317
  }
158318
+ return;
157900
158319
  }
157901
- function readRawSessionMessages(sessionId) {
157902
- if (activeRawMessageCache) {
157903
- const cached2 = activeRawMessageCache.get(sessionId);
157904
- if (cached2) {
157905
- return cached2;
157906
- }
157907
- const messages = readRawSessionMessagesFromSource(sessionId);
157908
- activeRawMessageCache.set(sessionId, messages);
157909
- return messages;
158320
+ function setToolContent(part, content) {
158321
+ if (!isRecord(part))
158322
+ return;
158323
+ if (part.type === "tool" && isRecord(part.state)) {
158324
+ part.state.output = content;
158325
+ return;
157910
158326
  }
157911
- return readRawSessionMessagesFromSource(sessionId);
157912
- }
157913
- function readRawSessionMessagesFromSource(sessionId) {
157914
- const provider2 = sessionProviders.get(sessionId);
157915
- if (provider2)
157916
- return provider2.readMessages();
157917
- return withReadOnlySessionDb((db) => readRawSessionMessagesFromDb(db, sessionId));
157918
- }
157919
- function getRawSessionMessageCount(sessionId) {
157920
- const provider2 = sessionProviders.get(sessionId);
157921
- if (provider2) {
157922
- if (provider2.getMessageCount)
157923
- return provider2.getMessageCount();
157924
- return provider2.readMessages().length;
158327
+ if (part.type === "tool_result") {
158328
+ part.content = content;
157925
158329
  }
157926
- return withReadOnlySessionDb((db) => getRawSessionMessageCountFromDb(db, sessionId));
157927
158330
  }
157928
- function getRawSessionTagKeysThrough(sessionId, upToMessageIndex) {
157929
- const messages = readRawSessionMessages(sessionId);
157930
- const keys = [];
157931
- for (const message of messages) {
157932
- if (message.ordinal > upToMessageIndex)
157933
- break;
157934
- for (const [partIndex, part] of message.parts.entries()) {
157935
- if (isTextPart(part)) {
157936
- keys.push(`${message.id}:p${partIndex}`);
157937
- }
157938
- if (isFilePart(part)) {
157939
- keys.push(`${message.id}:file${partIndex}`);
157940
- }
157941
- if (isToolPartWithOutput(part)) {
157942
- keys.push(part.callID);
158331
+ function truncateToolPart(part) {
158332
+ if (!isRecord(part))
158333
+ return;
158334
+ if (part.type === "tool" && isRecord(part.state)) {
158335
+ const state = part.state;
158336
+ state.output = "[truncated]";
158337
+ if (isRecord(state.input)) {
158338
+ const inputSize = estimateInputSize(state.input);
158339
+ if (inputSize > 500) {
158340
+ truncateInputValues(state.input);
157943
158341
  }
157944
158342
  }
158343
+ return;
158344
+ }
158345
+ if (part.type === "tool_result") {
158346
+ part.content = "[truncated]";
158347
+ return;
158348
+ }
158349
+ if (part.type === "tool-invocation" && isRecord(part.args)) {
158350
+ const inputSize = estimateInputSize(part.args);
158351
+ if (inputSize > 500) {
158352
+ truncateInputValues(part.args);
158353
+ }
158354
+ return;
158355
+ }
158356
+ if (part.type === "tool_use" && isRecord(part.input)) {
158357
+ const inputSize = estimateInputSize(part.input);
158358
+ if (inputSize > 500) {
158359
+ truncateInputValues(part.input);
158360
+ }
157945
158361
  }
157946
- return keys;
157947
158362
  }
157948
- function getProtectedTailStartOrdinal(sessionId) {
157949
- const messages = readRawSessionMessages(sessionId);
157950
- const userOrdinals = messages.filter((m) => m.role === "user" && hasMeaningfulUserText(m.parts)).map((m) => m.ordinal);
157951
- if (userOrdinals.length < PROTECTED_TAIL_USER_TURNS) {
157952
- return 1;
158363
+ function estimateInputSize(input) {
158364
+ try {
158365
+ return JSON.stringify(input).length;
158366
+ } catch {
158367
+ return 0;
157953
158368
  }
157954
- return userOrdinals[userOrdinals.length - PROTECTED_TAIL_USER_TURNS];
157955
158369
  }
157956
- function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal) {
157957
- const messages = readRawSessionMessages(sessionId);
157958
- const startOrdinal = Math.max(1, offset);
157959
- const lines = [];
157960
- const lineMeta = [];
157961
- const flushedToolOnlyBlocks = [];
157962
- let totalTokens = 0;
157963
- let messagesProcessed = 0;
158370
+ function safeSlice(str, maxLen) {
158371
+ if (str.length <= maxLen)
158372
+ return str;
158373
+ const lastCharCode = str.charCodeAt(maxLen - 1);
158374
+ if (lastCharCode >= 55296 && lastCharCode <= 56319) {
158375
+ return str.slice(0, maxLen - 1);
158376
+ }
158377
+ return str.slice(0, maxLen);
158378
+ }
158379
+ function truncateInputValues(input) {
158380
+ for (const key of Object.keys(input)) {
158381
+ const value = input[key];
158382
+ if (typeof value === "string") {
158383
+ if (value.endsWith(TRUNCATION_SENTINEL) || value === "[object]" || /^\[\d+ items\]$/.test(value))
158384
+ continue;
158385
+ input[key] = value.length > 5 ? `${safeSlice(value, 5)}${TRUNCATION_SENTINEL}` : value;
158386
+ } else if (Array.isArray(value)) {
158387
+ input[key] = `[${value.length} items]`;
158388
+ } else if (value !== null && typeof value === "object") {
158389
+ input[key] = "[object]";
158390
+ }
158391
+ }
158392
+ }
158393
+ function hasMeaningfulPart(part) {
158394
+ if (!isRecord(part))
158395
+ return false;
158396
+ const type = part.type;
158397
+ if (type === "text") {
158398
+ return typeof part.text === "string" && part.text.trim().length > 0;
158399
+ }
158400
+ if (typeof type !== "string")
158401
+ return false;
158402
+ if (IGNORE_PART_TYPES.has(type))
158403
+ return false;
158404
+ return true;
158405
+ }
158406
+ function clearThinkingParts(thinkingParts) {
158407
+ for (const part of thinkingParts) {
158408
+ if (part.thinking !== undefined)
158409
+ part.thinking = "[cleared]";
158410
+ if (part.text !== undefined)
158411
+ part.text = "[cleared]";
158412
+ }
158413
+ }
158414
+ function extractToolCallObservation(part) {
158415
+ if (!isRecord(part))
158416
+ return null;
158417
+ if (part.type === "tool" && isToolCallId(part.callID)) {
158418
+ return { callId: part.callID, kind: "result" };
158419
+ }
158420
+ if (part.type === "tool-invocation" && isToolCallId(part.callID)) {
158421
+ return { callId: part.callID, kind: "invocation" };
158422
+ }
158423
+ if (part.type === "tool_use" && isToolCallId(part.id)) {
158424
+ return { callId: part.id, kind: "invocation" };
158425
+ }
158426
+ if (part.type === "tool_result" && isToolCallId(part.tool_use_id)) {
158427
+ return { callId: part.tool_use_id, kind: "result" };
158428
+ }
158429
+ return null;
158430
+ }
158431
+ function isDropContent(content) {
158432
+ return content.startsWith(DROP_PREFIX);
158433
+ }
158434
+
158435
+ class ToolMutationBatch {
158436
+ partsToRemove = new Set;
158437
+ affectedMessages = new Set;
158438
+ messages;
158439
+ constructor(messages) {
158440
+ this.messages = messages;
158441
+ }
158442
+ markForRemoval(occurrence) {
158443
+ this.partsToRemove.add(occurrence.part);
158444
+ this.affectedMessages.add(occurrence.message);
158445
+ }
158446
+ finalize() {
158447
+ if (this.partsToRemove.size === 0)
158448
+ return;
158449
+ for (const message of this.affectedMessages) {
158450
+ message.parts = message.parts.filter((p) => !this.partsToRemove.has(p));
158451
+ }
158452
+ for (let i = this.messages.length - 1;i >= 0; i -= 1) {
158453
+ if (!this.messages[i].parts.some(hasMeaningfulPart)) {
158454
+ this.messages.splice(i, 1);
158455
+ }
158456
+ }
158457
+ this.partsToRemove.clear();
158458
+ this.affectedMessages.clear();
158459
+ }
158460
+ }
158461
+ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
158462
+ const drop = () => {
158463
+ const entry = index.get(compositeKey);
158464
+ if (!entry || entry.occurrences.length === 0)
158465
+ return "absent";
158466
+ if (!entry.hasResult)
158467
+ return "incomplete";
158468
+ for (const occurrence of entry.occurrences) {
158469
+ batch.markForRemoval(occurrence);
158470
+ }
158471
+ clearThinkingParts(thinkingParts);
158472
+ index.delete(compositeKey);
158473
+ return "removed";
158474
+ };
158475
+ const truncate = () => {
158476
+ const entry = index.get(compositeKey);
158477
+ if (!entry || entry.occurrences.length === 0)
158478
+ return "absent";
158479
+ if (!entry.hasResult)
158480
+ return "incomplete";
158481
+ for (const occurrence of entry.occurrences) {
158482
+ truncateToolPart(occurrence.part);
158483
+ }
158484
+ clearThinkingParts(thinkingParts);
158485
+ return "truncated";
158486
+ };
158487
+ return {
158488
+ setContent: (content) => {
158489
+ if (isDropContent(content)) {
158490
+ drop();
158491
+ return true;
158492
+ }
158493
+ const entry = index.get(compositeKey);
158494
+ if (!entry)
158495
+ return false;
158496
+ let changed = false;
158497
+ for (const occurrence of entry.occurrences) {
158498
+ if (occurrence.kind !== "result")
158499
+ continue;
158500
+ const prevContent = getToolContent(occurrence.part);
158501
+ if (prevContent !== content) {
158502
+ setToolContent(occurrence.part, content);
158503
+ changed = true;
158504
+ }
158505
+ }
158506
+ return changed;
158507
+ },
158508
+ drop,
158509
+ truncate
158510
+ };
158511
+ }
158512
+ var DROP_PREFIX = "[dropped", IGNORE_PART_TYPES, TRUNCATION_SENTINEL = "...[truncated]";
158513
+ var init_tool_drop_target = __esm(() => {
158514
+ IGNORE_PART_TYPES = new Set([
158515
+ "thinking",
158516
+ "reasoning",
158517
+ "redacted_thinking",
158518
+ "meta",
158519
+ "step-start",
158520
+ "step-finish"
158521
+ ]);
158522
+ });
158523
+
158524
+ // src/hooks/magic-context/read-session-chunk.ts
158525
+ function cleanUserText(text) {
158526
+ return removeSystemReminders(text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
158527
+ }
158528
+ function withRawSessionMessageCache(fn) {
158529
+ const outerCache = activeRawMessageCache;
158530
+ if (!outerCache) {
158531
+ activeRawMessageCache = new Map;
158532
+ }
158533
+ try {
158534
+ return fn();
158535
+ } finally {
158536
+ if (!outerCache) {
158537
+ activeRawMessageCache = null;
158538
+ }
158539
+ }
158540
+ }
158541
+ function readRawSessionMessages(sessionId) {
158542
+ if (activeRawMessageCache) {
158543
+ const cached2 = activeRawMessageCache.get(sessionId);
158544
+ if (cached2) {
158545
+ return cached2;
158546
+ }
158547
+ const messages = readRawSessionMessagesFromSource(sessionId);
158548
+ activeRawMessageCache.set(sessionId, messages);
158549
+ return messages;
158550
+ }
158551
+ return readRawSessionMessagesFromSource(sessionId);
158552
+ }
158553
+ function readRawSessionMessageById(sessionId, messageId) {
158554
+ const provider2 = sessionProviders.get(sessionId);
158555
+ if (provider2?.readMessageById) {
158556
+ return provider2.readMessageById(messageId);
158557
+ }
158558
+ if (provider2) {
158559
+ return provider2.readMessages().find((message) => message.id === messageId) ?? null;
158560
+ }
158561
+ return withReadOnlySessionDb((db) => readRawSessionMessageByIdFromDb(db, sessionId, messageId));
158562
+ }
158563
+ function readRawSessionMessagesFromSource(sessionId) {
158564
+ const provider2 = sessionProviders.get(sessionId);
158565
+ if (provider2)
158566
+ return provider2.readMessages();
158567
+ return withReadOnlySessionDb((db) => readRawSessionMessagesFromDb(db, sessionId));
158568
+ }
158569
+ function getRawSessionMessageCount(sessionId) {
158570
+ const provider2 = sessionProviders.get(sessionId);
158571
+ if (provider2) {
158572
+ if (provider2.getMessageCount)
158573
+ return provider2.getMessageCount();
158574
+ return provider2.readMessages().length;
158575
+ }
158576
+ return withReadOnlySessionDb((db) => getRawSessionMessageCountFromDb(db, sessionId));
158577
+ }
158578
+ function getRawSessionTagKeysThrough(sessionId, upToMessageIndex) {
158579
+ const messages = readRawSessionMessages(sessionId);
158580
+ const messageFileKeys = new Set;
158581
+ const toolObservations = new Map;
158582
+ const unpairedInvocations = new Map;
158583
+ for (const message of messages) {
158584
+ if (message.ordinal > upToMessageIndex)
158585
+ break;
158586
+ for (const [partIndex, part] of message.parts.entries()) {
158587
+ if (isTextPart(part)) {
158588
+ messageFileKeys.add(`${message.id}:p${partIndex}`);
158589
+ continue;
158590
+ }
158591
+ if (isFilePart(part)) {
158592
+ messageFileKeys.add(`${message.id}:file${partIndex}`);
158593
+ continue;
158594
+ }
158595
+ const obs = extractToolCallObservation(part);
158596
+ if (!obs)
158597
+ continue;
158598
+ let ownerMsgId;
158599
+ if (obs.kind === "invocation") {
158600
+ ownerMsgId = message.id;
158601
+ const queue2 = unpairedInvocations.get(obs.callId) ?? [];
158602
+ queue2.push(message.id);
158603
+ unpairedInvocations.set(obs.callId, queue2);
158604
+ } else {
158605
+ const queue2 = unpairedInvocations.get(obs.callId);
158606
+ if (queue2 && queue2.length > 0) {
158607
+ const popped = queue2.shift();
158608
+ if (queue2.length === 0)
158609
+ unpairedInvocations.delete(obs.callId);
158610
+ ownerMsgId = popped ?? message.id;
158611
+ } else {
158612
+ ownerMsgId = message.id;
158613
+ }
158614
+ }
158615
+ const owners = toolObservations.get(obs.callId) ?? new Set;
158616
+ owners.add(ownerMsgId);
158617
+ toolObservations.set(obs.callId, owners);
158618
+ }
158619
+ }
158620
+ return { messageFileKeys, toolObservations };
158621
+ }
158622
+ function getProtectedTailStartOrdinal(sessionId) {
158623
+ const messages = readRawSessionMessages(sessionId);
158624
+ const userOrdinals = messages.filter((m) => m.role === "user" && hasMeaningfulUserText(m.parts)).map((m) => m.ordinal);
158625
+ if (userOrdinals.length < PROTECTED_TAIL_USER_TURNS) {
158626
+ return 1;
158627
+ }
158628
+ return userOrdinals[userOrdinals.length - PROTECTED_TAIL_USER_TURNS];
158629
+ }
158630
+ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal) {
158631
+ const messages = readRawSessionMessages(sessionId);
158632
+ const startOrdinal = Math.max(1, offset);
158633
+ const lines = [];
158634
+ const lineMeta = [];
158635
+ const flushedToolOnlyBlocks = [];
158636
+ let totalTokens = 0;
158637
+ let messagesProcessed = 0;
157964
158638
  let lastOrdinal = startOrdinal - 1;
157965
158639
  let lastMessageId = "";
157966
158640
  let firstMessageId = "";
@@ -158094,6 +158768,7 @@ var activeRawMessageCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS =
158094
158768
  var init_read_session_chunk = __esm(async () => {
158095
158769
  init_read_session_formatting();
158096
158770
  init_tag_part_guards();
158771
+ init_tool_drop_target();
158097
158772
  init_read_session_formatting();
158098
158773
  await init_read_session_db();
158099
158774
  sessionProviders = new Map;
@@ -158151,10 +158826,26 @@ function getCountIndexedMessageStatement(db) {
158151
158826
  }
158152
158827
  return stmt;
158153
158828
  }
158829
+ function getIndexedMessageIdStatement(db) {
158830
+ let stmt = indexedMessageIdStatements.get(db);
158831
+ if (!stmt) {
158832
+ stmt = db.prepare("SELECT message_id AS messageId FROM message_history_fts WHERE session_id = ?");
158833
+ indexedMessageIdStatements.set(db, stmt);
158834
+ }
158835
+ return stmt;
158836
+ }
158154
158837
  function getLastIndexedOrdinal(db, sessionId) {
158155
158838
  const row = getLastIndexedStatement(db).get(sessionId);
158156
158839
  return typeof row?.last_indexed_ordinal === "number" ? row.last_indexed_ordinal : 0;
158157
158840
  }
158841
+ function isMessageAlreadyIndexed(db, sessionId, messageId) {
158842
+ const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
158843
+ return (typeof row?.count === "number" ? row.count : 0) > 0;
158844
+ }
158845
+ function advanceIndexWatermark(db, sessionId, ordinal, now) {
158846
+ const current = getLastIndexedOrdinal(db, sessionId);
158847
+ getUpsertIndexStatement(db).run(sessionId, Math.max(current, ordinal), now, getHarness());
158848
+ }
158158
158849
  function deleteIndexedMessage(db, sessionId, messageId) {
158159
158850
  const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
158160
158851
  const count = typeof row?.count === "number" ? row.count : 0;
@@ -158180,36 +158871,53 @@ function getIndexableContent(role, parts) {
158180
158871
  }
158181
158872
  return "";
158182
158873
  }
158183
- function ensureMessagesIndexed(db, sessionId, readMessages) {
158184
- const messages = readMessages(sessionId);
158185
- if (messages.length === 0) {
158186
- db.transaction(() => clearIndexedMessages(db, sessionId))();
158187
- return;
158874
+ function indexSingleMessageInTransaction(db, sessionId, message, now) {
158875
+ if (message.role !== "user" && message.role !== "assistant") {
158876
+ advanceIndexWatermark(db, sessionId, message.ordinal, now);
158877
+ return false;
158188
158878
  }
158189
- let lastIndexedOrdinal = getLastIndexedOrdinal(db, sessionId);
158190
- if (lastIndexedOrdinal > messages.length) {
158191
- db.transaction(() => clearIndexedMessages(db, sessionId))();
158192
- lastIndexedOrdinal = 0;
158879
+ const content = getIndexableContent(message.role, message.parts);
158880
+ if (content.length === 0) {
158881
+ advanceIndexWatermark(db, sessionId, message.ordinal, now);
158882
+ return false;
158193
158883
  }
158194
- if (lastIndexedOrdinal >= messages.length) {
158195
- return;
158884
+ if (isMessageAlreadyIndexed(db, sessionId, message.id)) {
158885
+ advanceIndexWatermark(db, sessionId, message.ordinal, now);
158886
+ return false;
158196
158887
  }
158197
- const messagesToInsert = messages.filter((message) => message.ordinal > lastIndexedOrdinal).filter((message) => message.role === "user" || message.role === "assistant").map((message) => ({
158198
- ordinal: message.ordinal,
158199
- id: message.id,
158200
- role: message.role,
158201
- content: getIndexableContent(message.role, message.parts)
158202
- })).filter((message) => message.content.length > 0);
158888
+ getInsertMessageStatement(db).run(sessionId, message.ordinal, message.id, message.role, content);
158889
+ advanceIndexWatermark(db, sessionId, message.ordinal, now);
158890
+ return true;
158891
+ }
158892
+ function indexSingleMessage(db, sessionId, message) {
158893
+ return db.transaction(() => indexSingleMessageInTransaction(db, sessionId, message, Date.now()))();
158894
+ }
158895
+ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal, finalWatermark = messages.length) {
158203
158896
  const now = Date.now();
158897
+ let inserted = 0;
158204
158898
  db.transaction(() => {
158899
+ const existingMessageIds = new Set(getIndexedMessageIdStatement(db).all(sessionId).map((row) => row.messageId).filter((messageId) => typeof messageId === "string"));
158205
158900
  const insertMessage = getInsertMessageStatement(db);
158206
- for (const message of messagesToInsert) {
158207
- insertMessage.run(sessionId, message.ordinal, message.id, message.role, message.content);
158901
+ for (const message of messages) {
158902
+ if (message.ordinal <= lastIndexedOrdinal) {
158903
+ continue;
158904
+ }
158905
+ if (message.role !== "user" && message.role !== "assistant") {
158906
+ continue;
158907
+ }
158908
+ const content = getIndexableContent(message.role, message.parts);
158909
+ if (content.length === 0 || existingMessageIds.has(message.id)) {
158910
+ continue;
158911
+ }
158912
+ insertMessage.run(sessionId, message.ordinal, message.id, message.role, content);
158913
+ existingMessageIds.add(message.id);
158914
+ inserted++;
158208
158915
  }
158209
- getUpsertIndexStatement(db).run(sessionId, messages.length, now, getHarness());
158916
+ getUpsertIndexStatement(db).run(sessionId, finalWatermark, now, getHarness());
158210
158917
  })();
158918
+ return inserted;
158211
158919
  }
158212
- var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, deleteFtsStatements, deleteIndexStatements, countIndexedMessageStatements;
158920
+ var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, deleteFtsStatements, deleteIndexStatements, countIndexedMessageStatements, indexedMessageIdStatements;
158213
158921
  var init_message_index = __esm(async () => {
158214
158922
  init_compression_depth_storage();
158215
158923
  await init_read_session_chunk();
@@ -158219,6 +158927,7 @@ var init_message_index = __esm(async () => {
158219
158927
  deleteFtsStatements = new WeakMap;
158220
158928
  deleteIndexStatements = new WeakMap;
158221
158929
  countIndexedMessageStatements = new WeakMap;
158930
+ indexedMessageIdStatements = new WeakMap;
158222
158931
  });
158223
158932
 
158224
158933
  // src/features/magic-context/migrations.ts
@@ -158235,22 +158944,51 @@ function getCurrentVersion(db) {
158235
158944
  const row = db.prepare("SELECT MAX(version) as version FROM schema_migrations").get();
158236
158945
  return row?.version ?? 0;
158237
158946
  }
158947
+ function isSiblingMigrationConflict(error48, version2) {
158948
+ if (!(error48 instanceof Error))
158949
+ return false;
158950
+ const code = error48.code;
158951
+ if (code !== "SQLITE_CONSTRAINT_PRIMARYKEY" && code !== "SQLITE_CONSTRAINT_UNIQUE") {
158952
+ return false;
158953
+ }
158954
+ const msg = error48.message;
158955
+ if (!msg.includes("schema_migrations"))
158956
+ return false;
158957
+ if (!msg.toLowerCase().includes("version"))
158958
+ return false;
158959
+ return true;
158960
+ }
158238
158961
  function runMigrations(db) {
158239
158962
  ensureMigrationsTable(db);
158240
- const currentVersion = getCurrentVersion(db);
158241
- const pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
158963
+ let currentVersion = getCurrentVersion(db);
158964
+ let pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
158242
158965
  if (pendingMigrations.length === 0) {
158243
158966
  return;
158244
158967
  }
158245
158968
  log(`[migrations] current schema version: ${currentVersion}, applying ${pendingMigrations.length} migration(s)`);
158246
- for (const migration of pendingMigrations) {
158969
+ let migrationIndex = 0;
158970
+ while (migrationIndex < pendingMigrations.length) {
158971
+ const migration = pendingMigrations[migrationIndex];
158247
158972
  try {
158248
158973
  db.transaction(() => {
158249
158974
  migration.up(db);
158250
158975
  db.prepare("INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)").run(migration.version, migration.description, Date.now());
158251
158976
  })();
158252
158977
  log(`[migrations] applied v${migration.version}: ${migration.description}`);
158978
+ migrationIndex += 1;
158253
158979
  } catch (error48) {
158980
+ if (isSiblingMigrationConflict(error48, migration.version)) {
158981
+ log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
158982
+ const reReadVersion = getCurrentVersion(db);
158983
+ if (reReadVersion <= currentVersion) {
158984
+ log(`[migrations] FAILED v${migration.version}: sibling-conflict shape but version not advanced (${reReadVersion} <= ${currentVersion}) — failing closed`);
158985
+ throw new Error(`Migration v${migration.version} failed: sibling conflict reported but version did not advance. Database may need manual repair.`);
158986
+ }
158987
+ currentVersion = reReadVersion;
158988
+ pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
158989
+ migrationIndex = 0;
158990
+ continue;
158991
+ }
158254
158992
  log(`[migrations] FAILED v${migration.version}: ${migration.description} — ${error48 instanceof Error ? error48.message : String(error48)}`);
158255
158993
  throw new Error(`Migration v${migration.version} failed: ${error48 instanceof Error ? error48.message : String(error48)}. Database may need manual repair.`);
158256
158994
  }
@@ -158462,30 +159200,330 @@ var init_migrations = __esm(async () => {
158462
159200
  db.exec("ALTER TABLE notes ADD COLUMN harness TEXT NOT NULL DEFAULT 'opencode'");
158463
159201
  }
158464
159202
  }
159203
+ },
159204
+ {
159205
+ version: 8,
159206
+ description: "Add partial indexes on tags(session_id, tag_number) for active and dropped",
159207
+ up: (db) => {
159208
+ db.exec(`
159209
+ CREATE INDEX IF NOT EXISTS idx_tags_active_session_tag_number
159210
+ ON tags(session_id, tag_number)
159211
+ WHERE status = 'active';
159212
+
159213
+ CREATE INDEX IF NOT EXISTS idx_tags_dropped_session_tag_number
159214
+ ON tags(session_id, tag_number)
159215
+ WHERE status = 'dropped';
159216
+ `);
159217
+ db.exec("ANALYZE tags;");
159218
+ }
159219
+ },
159220
+ {
159221
+ version: 9,
159222
+ description: "Persist tool_definition_measurements across plugin restarts",
159223
+ up: (db) => {
159224
+ db.exec(`
159225
+ CREATE TABLE IF NOT EXISTS tool_definition_measurements (
159226
+ provider_id TEXT NOT NULL,
159227
+ model_id TEXT NOT NULL,
159228
+ agent_name TEXT NOT NULL,
159229
+ tool_id TEXT NOT NULL,
159230
+ token_count INTEGER NOT NULL,
159231
+ recorded_at INTEGER NOT NULL,
159232
+ PRIMARY KEY (provider_id, model_id, agent_name, tool_id)
159233
+ );
159234
+ `);
159235
+ }
159236
+ },
159237
+ {
159238
+ version: 10,
159239
+ description: "Add tool_owner_message_id column to tags + composite identity indexes",
159240
+ up: (db) => {
159241
+ const cols = db.prepare("PRAGMA table_info(tags)").all();
159242
+ if (!cols.some((c) => c.name === "tool_owner_message_id")) {
159243
+ db.exec("ALTER TABLE tags ADD COLUMN tool_owner_message_id TEXT DEFAULT NULL");
159244
+ }
159245
+ db.exec(`
159246
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_tool_composite
159247
+ ON tags(session_id, message_id, tool_owner_message_id)
159248
+ WHERE type = 'tool' AND tool_owner_message_id IS NOT NULL;
159249
+
159250
+ CREATE INDEX IF NOT EXISTS idx_tags_tool_null_owner
159251
+ ON tags(session_id, message_id)
159252
+ WHERE type = 'tool' AND tool_owner_message_id IS NULL;
159253
+ `);
159254
+ }
158465
159255
  }
158466
159256
  ];
158467
159257
  });
158468
159258
 
159259
+ // src/features/magic-context/tool-owner-backfill.ts
159260
+ import { existsSync as existsSync9 } from "node:fs";
159261
+ import { join as join14 } from "node:path";
159262
+ function resolveOpencodeDbPath() {
159263
+ return join14(getDataDir(), "opencode", "opencode.db");
159264
+ }
159265
+ function ensureBackfillStateTable(db) {
159266
+ db.exec(`
159267
+ CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
159268
+ session_id TEXT PRIMARY KEY,
159269
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'skipped')),
159270
+ started_at INTEGER,
159271
+ lease_expires_at INTEGER,
159272
+ completed_at INTEGER,
159273
+ last_error TEXT
159274
+ );
159275
+ CREATE INDEX IF NOT EXISTS idx_tool_owner_backfill_state_status
159276
+ ON tool_owner_backfill_state(status);
159277
+ `);
159278
+ }
159279
+ function runToolOwnerBackfill(db) {
159280
+ const startedAt = performance.now();
159281
+ ensureBackfillStateTable(db);
159282
+ const result = {
159283
+ sessionsProcessed: 0,
159284
+ sessionsSkippedNoOcDb: 0,
159285
+ sessionsSkippedNoMatches: 0,
159286
+ sessionsCompleted: 0,
159287
+ sessionsBlockedByLease: 0,
159288
+ sessionsErrored: 0,
159289
+ rowsUpdated: 0,
159290
+ rowsLeftNull: 0,
159291
+ durationMs: 0
159292
+ };
159293
+ if (!isToolOwnerBackfillNeeded(db)) {
159294
+ result.durationMs = performance.now() - startedAt;
159295
+ return result;
159296
+ }
159297
+ const opencodeDbPath = resolveOpencodeDbPath();
159298
+ if (!existsSync9(opencodeDbPath)) {
159299
+ log(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
159300
+ markAllUnbackfilledSessionsSkipped(db);
159301
+ result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
159302
+ result.durationMs = performance.now() - startedAt;
159303
+ return result;
159304
+ }
159305
+ db.exec(`ATTACH '${opencodeDbPath}' AS oc_backfill`);
159306
+ try {
159307
+ backfillToolOwnersInChunks(db, result);
159308
+ } finally {
159309
+ try {
159310
+ db.exec("DETACH DATABASE oc_backfill");
159311
+ } catch (error48) {
159312
+ log(`[backfill] failed to detach oc_backfill database: ${error48 instanceof Error ? error48.message : String(error48)}`);
159313
+ }
159314
+ }
159315
+ result.durationMs = performance.now() - startedAt;
159316
+ log(`[backfill] sessions=${result.sessionsProcessed} completed=${result.sessionsCompleted} skipped_no_oc=${result.sessionsSkippedNoOcDb} skipped_no_matches=${result.sessionsSkippedNoMatches} blocked_by_lease=${result.sessionsBlockedByLease} errored=${result.sessionsErrored} rows_updated=${result.rowsUpdated} rows_left_null=${result.rowsLeftNull} duration_ms=${Math.round(result.durationMs)}`);
159317
+ return result;
159318
+ }
159319
+ function isToolOwnerBackfillNeeded(db) {
159320
+ ensureBackfillStateTable(db);
159321
+ const row = db.prepare(`SELECT 1 AS hit
159322
+ FROM tags
159323
+ WHERE type = 'tool' AND tool_owner_message_id IS NULL
159324
+ AND NOT EXISTS (
159325
+ SELECT 1 FROM tool_owner_backfill_state s
159326
+ WHERE s.session_id = tags.session_id
159327
+ AND s.status IN ('completed', 'skipped')
159328
+ )
159329
+ LIMIT 1`).get();
159330
+ return row !== null && row !== undefined;
159331
+ }
159332
+ function markAllUnbackfilledSessionsSkipped(db) {
159333
+ const now = Date.now();
159334
+ db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, started_at, completed_at, last_error)
159335
+ SELECT DISTINCT session_id, 'skipped', NULL, ?, NULL
159336
+ FROM tags
159337
+ WHERE type = 'tool' AND tool_owner_message_id IS NULL
159338
+ ON CONFLICT(session_id) DO UPDATE SET
159339
+ status = 'skipped',
159340
+ completed_at = excluded.completed_at,
159341
+ last_error = NULL
159342
+ WHERE tool_owner_backfill_state.status NOT IN ('completed', 'running')`).run(now);
159343
+ }
159344
+ function countSessionsByStatus(db, status) {
159345
+ const row = db.prepare("SELECT COUNT(*) AS c FROM tool_owner_backfill_state WHERE status = ?").get(status);
159346
+ return row.c;
159347
+ }
159348
+ function acquireSessionLease(db, sessionId, now) {
159349
+ const expiresAt = now + LEASE_DURATION_MS2;
159350
+ const result = db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, started_at, lease_expires_at)
159351
+ VALUES (?, 'running', ?, ?)
159352
+ ON CONFLICT(session_id) DO UPDATE SET
159353
+ status = 'running',
159354
+ started_at = excluded.started_at,
159355
+ lease_expires_at = excluded.lease_expires_at,
159356
+ last_error = NULL
159357
+ WHERE tool_owner_backfill_state.status IN ('pending', 'skipped')
159358
+ OR (tool_owner_backfill_state.status = 'running'
159359
+ AND tool_owner_backfill_state.lease_expires_at < ?)`).run(sessionId, now, expiresAt, now);
159360
+ return (result.changes ?? 0) === 1;
159361
+ }
159362
+ function renewSessionLease(db, sessionId, now) {
159363
+ const expiresAt = now + LEASE_DURATION_MS2;
159364
+ db.prepare(`UPDATE tool_owner_backfill_state
159365
+ SET lease_expires_at = ?
159366
+ WHERE session_id = ? AND status = 'running'`).run(expiresAt, sessionId);
159367
+ }
159368
+ function markSessionCompleted(db, sessionId, now) {
159369
+ db.prepare(`UPDATE tool_owner_backfill_state
159370
+ SET status = 'completed', completed_at = ?, lease_expires_at = NULL, last_error = NULL
159371
+ WHERE session_id = ?`).run(now, sessionId);
159372
+ }
159373
+ function markSessionSkipped(db, sessionId, now, reason) {
159374
+ db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, completed_at, last_error)
159375
+ VALUES (?, 'skipped', ?, ?)
159376
+ ON CONFLICT(session_id) DO UPDATE SET
159377
+ status = 'skipped',
159378
+ completed_at = excluded.completed_at,
159379
+ last_error = excluded.last_error,
159380
+ lease_expires_at = NULL`).run(sessionId, now, reason);
159381
+ }
159382
+ function markSessionErrored(db, sessionId, error48) {
159383
+ const message = error48 instanceof Error ? error48.message : String(error48);
159384
+ db.prepare(`UPDATE tool_owner_backfill_state
159385
+ SET last_error = ?, lease_expires_at = NULL
159386
+ WHERE session_id = ?`).run(message, sessionId);
159387
+ }
159388
+ function getSessionsNeedingBackfill(db) {
159389
+ const rows = db.prepare(`SELECT DISTINCT t.session_id
159390
+ FROM tags t
159391
+ LEFT JOIN tool_owner_backfill_state s ON s.session_id = t.session_id
159392
+ WHERE t.type = 'tool' AND t.tool_owner_message_id IS NULL
159393
+ AND (s.status IS NULL OR s.status NOT IN ('completed', 'skipped'))
159394
+ ORDER BY t.session_id ASC`).all();
159395
+ return rows.map((r) => r.session_id);
159396
+ }
159397
+ function buildSessionOwnerMap(db, sessionId) {
159398
+ const rows = db.prepare(`SELECT
159399
+ COALESCE(
159400
+ CASE WHEN json_extract(p.data, '$.type') = 'tool_use'
159401
+ THEN json_extract(p.data, '$.id')
159402
+ END,
159403
+ json_extract(p.data, '$.callID')
159404
+ ) AS callid,
159405
+ m.id AS owner_id,
159406
+ m.time_created AS owner_t_created,
159407
+ p.id AS part_id,
159408
+ p.time_created AS part_t_created
159409
+ FROM oc_backfill.message m
159410
+ INNER JOIN oc_backfill.part p ON p.message_id = m.id
159411
+ WHERE m.session_id = ?
159412
+ AND json_extract(m.data, '$.role') = 'assistant'
159413
+ AND (
159414
+ (json_extract(p.data, '$.type') IN ('tool', 'tool-invocation')
159415
+ AND json_extract(p.data, '$.callID') IS NOT NULL)
159416
+ OR (json_extract(p.data, '$.type') = 'tool_use'
159417
+ AND json_extract(p.data, '$.id') IS NOT NULL)
159418
+ )
159419
+ ORDER BY
159420
+ m.time_created ASC,
159421
+ m.id ASC,
159422
+ p.time_created ASC,
159423
+ p.id ASC`).all(sessionId);
159424
+ const oldestByCallId = new Map;
159425
+ for (const r of rows) {
159426
+ if (typeof r.callid !== "string" || r.callid.length === 0)
159427
+ continue;
159428
+ if (!oldestByCallId.has(r.callid)) {
159429
+ oldestByCallId.set(r.callid, r.owner_id);
159430
+ }
159431
+ }
159432
+ return oldestByCallId;
159433
+ }
159434
+ function applyOwnersForSession(db, sessionId, ownersByCallId) {
159435
+ if (ownersByCallId.size === 0) {
159436
+ const leftNull = db.prepare(`SELECT COUNT(*) AS c FROM tags
159437
+ WHERE session_id = ? AND type = 'tool'
159438
+ AND tool_owner_message_id IS NULL`).get(sessionId).c;
159439
+ return { rowsUpdated: 0, rowsLeftNull: leftNull };
159440
+ }
159441
+ const findOrphanStmt = db.prepare(`SELECT id FROM tags
159442
+ WHERE session_id = ? AND message_id = ? AND type = 'tool'
159443
+ AND tool_owner_message_id IS NULL
159444
+ ORDER BY tag_number ASC
159445
+ LIMIT 1`);
159446
+ const updateRowStmt = db.prepare(`UPDATE tags
159447
+ SET tool_owner_message_id = ?
159448
+ WHERE id = ? AND tool_owner_message_id IS NULL`);
159449
+ let rowsUpdated = 0;
159450
+ db.transaction(() => {
159451
+ for (const [callId, ownerId] of ownersByCallId) {
159452
+ const orphan = findOrphanStmt.get(sessionId, callId);
159453
+ if (!orphan)
159454
+ continue;
159455
+ const result = updateRowStmt.run(ownerId, orphan.id);
159456
+ rowsUpdated += result.changes ?? 0;
159457
+ }
159458
+ })();
159459
+ const rowsLeftNull = db.prepare(`SELECT COUNT(*) AS c FROM tags
159460
+ WHERE session_id = ? AND type = 'tool'
159461
+ AND tool_owner_message_id IS NULL`).get(sessionId).c;
159462
+ return { rowsUpdated, rowsLeftNull };
159463
+ }
159464
+ function backfillToolOwnersInChunks(db, result) {
159465
+ const sessionIds = getSessionsNeedingBackfill(db);
159466
+ let lastRenewedAt = Date.now();
159467
+ for (const sessionId of sessionIds) {
159468
+ const now = Date.now();
159469
+ result.sessionsProcessed += 1;
159470
+ const acquired = acquireSessionLease(db, sessionId, now);
159471
+ if (!acquired) {
159472
+ result.sessionsBlockedByLease += 1;
159473
+ continue;
159474
+ }
159475
+ try {
159476
+ const owners = buildSessionOwnerMap(db, sessionId);
159477
+ const { rowsUpdated, rowsLeftNull } = applyOwnersForSession(db, sessionId, owners);
159478
+ result.rowsUpdated += rowsUpdated;
159479
+ result.rowsLeftNull += rowsLeftNull;
159480
+ if (owners.size === 0) {
159481
+ markSessionSkipped(db, sessionId, Date.now(), "no_oc_matches");
159482
+ result.sessionsSkippedNoMatches += 1;
159483
+ } else {
159484
+ markSessionCompleted(db, sessionId, Date.now());
159485
+ result.sessionsCompleted += 1;
159486
+ }
159487
+ } catch (error48) {
159488
+ log(`[backfill] session=${sessionId} errored: ${error48 instanceof Error ? error48.message : String(error48)}`);
159489
+ markSessionErrored(db, sessionId, error48);
159490
+ result.sessionsErrored += 1;
159491
+ }
159492
+ const sinceRenew = Date.now() - lastRenewedAt;
159493
+ if (sinceRenew > LEASE_RENEWAL_MS) {
159494
+ renewSessionLease(db, sessionId, Date.now());
159495
+ lastRenewedAt = Date.now();
159496
+ }
159497
+ }
159498
+ }
159499
+ var LEASE_DURATION_MS2, LEASE_RENEWAL_MS;
159500
+ var init_tool_owner_backfill = __esm(() => {
159501
+ init_data_path();
159502
+ init_logger();
159503
+ LEASE_DURATION_MS2 = 5 * 60 * 1000;
159504
+ LEASE_RENEWAL_MS = 60 * 1000;
159505
+ });
159506
+
158469
159507
  // src/features/magic-context/storage-db.ts
158470
- import { copyFileSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "node:fs";
158471
- import { join as join13 } from "node:path";
159508
+ import { copyFileSync, cpSync, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "node:fs";
159509
+ import { join as join15 } from "node:path";
158472
159510
  function resolveDatabasePath() {
158473
159511
  const dbDir = getMagicContextStorageDir();
158474
- return { dbDir, dbPath: join13(dbDir, "context.db") };
159512
+ return { dbDir, dbPath: join15(dbDir, "context.db") };
158475
159513
  }
158476
159514
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158477
- if (existsSync8(targetDbPath))
159515
+ if (existsSync10(targetDbPath))
158478
159516
  return;
158479
159517
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
158480
- const legacyDbPath = join13(legacyDir, "context.db");
158481
- if (!existsSync8(legacyDbPath))
159518
+ const legacyDbPath = join15(legacyDir, "context.db");
159519
+ if (!existsSync10(legacyDbPath))
158482
159520
  return;
158483
159521
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
158484
- mkdirSync2(targetDbDir, { recursive: true });
159522
+ mkdirSync3(targetDbDir, { recursive: true });
158485
159523
  for (const suffix of ["", "-wal", "-shm"]) {
158486
159524
  const src = `${legacyDbPath}${suffix}`;
158487
- const dst = join13(targetDbDir, `context.db${suffix}`);
158488
- if (existsSync8(src)) {
159525
+ const dst = join15(targetDbDir, `context.db${suffix}`);
159526
+ if (existsSync10(src)) {
158489
159527
  try {
158490
159528
  copyFileSync(src, dst);
158491
159529
  } catch (error48) {
@@ -158493,9 +159531,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158493
159531
  }
158494
159532
  }
158495
159533
  }
158496
- const legacyModelsDir = join13(legacyDir, "models");
158497
- const targetModelsDir = join13(targetDbDir, "models");
158498
- if (existsSync8(legacyModelsDir) && !existsSync8(targetModelsDir)) {
159534
+ const legacyModelsDir = join15(legacyDir, "models");
159535
+ const targetModelsDir = join15(targetDbDir, "models");
159536
+ if (existsSync10(legacyModelsDir) && !existsSync10(targetModelsDir)) {
158499
159537
  try {
158500
159538
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
158501
159539
  } catch (error48) {
@@ -158781,6 +159819,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158781
159819
  ensureColumn(db, "tags", "tool_name", "TEXT");
158782
159820
  ensureColumn(db, "tags", "input_byte_size", "INTEGER DEFAULT 0");
158783
159821
  ensureColumn(db, "tags", "caveman_depth", "INTEGER DEFAULT 0");
159822
+ ensureColumn(db, "tags", "tool_owner_message_id", "TEXT DEFAULT NULL");
158784
159823
  ensureColumn(db, "session_meta", "system_prompt_tokens", "INTEGER DEFAULT 0");
158785
159824
  ensureColumn(db, "session_meta", "compaction_marker_state", "TEXT DEFAULT ''");
158786
159825
  ensureColumn(db, "session_meta", "key_files", "TEXT DEFAULT ''");
@@ -158875,10 +159914,17 @@ function openDatabase() {
158875
159914
  }
158876
159915
  try {
158877
159916
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
158878
- mkdirSync2(dbDir, { recursive: true });
159917
+ mkdirSync3(dbDir, { recursive: true });
158879
159918
  const db = new Database(dbPath);
158880
159919
  initializeDatabase(db);
158881
159920
  runMigrations(db);
159921
+ try {
159922
+ runToolOwnerBackfill(db);
159923
+ } catch (error48) {
159924
+ log(`[magic-context] tool-owner backfill failed (continuing with lazy adoption fallback): ${getErrorMessage(error48)}`);
159925
+ }
159926
+ setDatabase(db);
159927
+ loadToolDefinitionMeasurements(db);
158882
159928
  databases.set(dbPath, db);
158883
159929
  persistenceByDatabase.set(db, true);
158884
159930
  persistenceErrorByDatabase.delete(db);
@@ -158899,6 +159945,8 @@ var databases, persistenceByDatabase, persistenceErrorByDatabase;
158899
159945
  var init_storage_db = __esm(async () => {
158900
159946
  init_data_path();
158901
159947
  init_logger();
159948
+ init_tool_definition_tokens();
159949
+ init_tool_owner_backfill();
158902
159950
  await __promiseAll([
158903
159951
  init_sqlite(),
158904
159952
  init_migrations()
@@ -159469,7 +160517,7 @@ var init_storage_source = () => {};
159469
160517
  function getInsertTagStatement(db) {
159470
160518
  let stmt = insertTagStatements.get(db);
159471
160519
  if (!stmt) {
159472
- stmt = db.prepare("INSERT INTO tags (session_id, message_id, type, byte_size, reasoning_byte_size, tag_number, tool_name, input_byte_size, harness) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
160520
+ stmt = db.prepare("INSERT INTO tags (session_id, message_id, type, byte_size, reasoning_byte_size, tag_number, tool_name, input_byte_size, harness, tool_owner_message_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
159473
160521
  insertTagStatements.set(db, stmt);
159474
160522
  }
159475
160523
  return stmt;
@@ -159550,7 +160598,8 @@ function toTagEntry(row) {
159550
160598
  byteSize: row.byte_size,
159551
160599
  reasoningByteSize: row.reasoning_byte_size ?? 0,
159552
160600
  sessionId: row.session_id,
159553
- cavemanDepth: typeof row.caveman_depth === "number" && Number.isFinite(row.caveman_depth) ? row.caveman_depth : 0
160601
+ cavemanDepth: typeof row.caveman_depth === "number" && Number.isFinite(row.caveman_depth) ? row.caveman_depth : 0,
160602
+ toolOwnerMessageId: typeof row.tool_owner_message_id === "string" ? row.tool_owner_message_id : null
159554
160603
  };
159555
160604
  }
159556
160605
  function isTagNumberRow(row) {
@@ -159568,8 +160617,8 @@ function isMaxTagNumberRow(row) {
159568
160617
  function escapeLikePattern(value) {
159569
160618
  return value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
159570
160619
  }
159571
- function insertTag(db, sessionId, messageId, type, byteSize2, tagNumber, reasoningByteSize = 0, toolName = null, inputByteSize = 0) {
159572
- getInsertTagStatement(db).run(sessionId, messageId, type, byteSize2, reasoningByteSize, tagNumber, toolName, inputByteSize, getHarness());
160620
+ function insertTag(db, sessionId, messageId, type, byteSize2, tagNumber, reasoningByteSize = 0, toolName = null, inputByteSize = 0, toolOwnerMessageId = null) {
160621
+ getInsertTagStatement(db).run(sessionId, messageId, type, byteSize2, reasoningByteSize, tagNumber, toolName, inputByteSize, getHarness(), toolOwnerMessageId);
159573
160622
  return tagNumber;
159574
160623
  }
159575
160624
  function updateTagStatus(db, sessionId, tagId, status) {
@@ -159588,12 +160637,27 @@ function deleteTagsByMessageId(db, sessionId, messageId) {
159588
160637
  const escapedMessageId = escapeLikePattern(messageId);
159589
160638
  const textPartPattern = `${escapedMessageId}:p%`;
159590
160639
  const filePartPattern = `${escapedMessageId}:file%`;
159591
- const tagNumbers = getTagNumbersByMessageIdStatement(db).all(sessionId, messageId, textPartPattern, filePartPattern).filter(isTagNumberRow).map((row) => row.tag_number);
159592
- if (tagNumbers.length === 0) {
160640
+ const messageScopedTags = getTagNumbersByMessageIdStatement(db).all(sessionId, messageId, textPartPattern, filePartPattern).filter(isTagNumberRow).map((row) => row.tag_number);
160641
+ const ownerScopedTagNumbers = getOwnerScopedToolTagNumbers(db, sessionId, messageId);
160642
+ if (messageScopedTags.length === 0 && ownerScopedTagNumbers.length === 0) {
159593
160643
  return [];
159594
160644
  }
159595
- getDeleteTagsByMessageIdStatement(db).run(sessionId, messageId, textPartPattern, filePartPattern);
159596
- return tagNumbers;
160645
+ if (messageScopedTags.length > 0) {
160646
+ getDeleteTagsByMessageIdStatement(db).run(sessionId, messageId, textPartPattern, filePartPattern);
160647
+ }
160648
+ if (ownerScopedTagNumbers.length > 0) {
160649
+ deleteToolTagsByOwner(db, sessionId, messageId);
160650
+ }
160651
+ const merged = new Set([...messageScopedTags, ...ownerScopedTagNumbers]);
160652
+ return Array.from(merged).sort((a, b) => a - b);
160653
+ }
160654
+ function getOwnerScopedToolTagNumbers(db, sessionId, ownerMsgId) {
160655
+ let stmt = getOwnerScopedToolTagNumbersStatements.get(db);
160656
+ if (!stmt) {
160657
+ stmt = db.prepare("SELECT tag_number FROM tags WHERE session_id = ? AND type = 'tool' AND tool_owner_message_id = ? ORDER BY tag_number ASC");
160658
+ getOwnerScopedToolTagNumbersStatements.set(db, stmt);
160659
+ }
160660
+ return stmt.all(sessionId, ownerMsgId).filter(isTagNumberRow).map((row) => row.tag_number);
159597
160661
  }
159598
160662
  function getMaxTagNumberBySession(db, sessionId) {
159599
160663
  const row = getMaxTagNumberBySessionStatement(db).get(sessionId);
@@ -159604,17 +160668,151 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
159604
160668
  return isTagNumberRow(row) ? row.tag_number : null;
159605
160669
  }
159606
160670
  function getTagsBySession(db, sessionId) {
159607
- const rows = db.prepare("SELECT id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC").all(sessionId).filter(isTagRow);
160671
+ const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
160672
+ return rows.map(toTagEntry);
160673
+ }
160674
+ function getActiveTagsBySessionStatement(db) {
160675
+ let stmt = getActiveTagsBySessionStatements.get(db);
160676
+ if (!stmt) {
160677
+ stmt = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND status = 'active' ORDER BY tag_number ASC, id ASC`);
160678
+ getActiveTagsBySessionStatements.set(db, stmt);
160679
+ }
160680
+ return stmt;
160681
+ }
160682
+ function getMaxDroppedTagNumberStatement(db) {
160683
+ let stmt = getMaxDroppedTagNumberStatements.get(db);
160684
+ if (!stmt) {
160685
+ stmt = db.prepare("SELECT COALESCE(MAX(tag_number), 0) AS max_tag_number FROM tags WHERE session_id = ? AND status = 'dropped'");
160686
+ getMaxDroppedTagNumberStatements.set(db, stmt);
160687
+ }
160688
+ return stmt;
160689
+ }
160690
+ function getActiveTagsBySession(db, sessionId) {
160691
+ const rows = getActiveTagsBySessionStatement(db).all(sessionId).filter(isTagRow);
160692
+ return rows.map(toTagEntry);
160693
+ }
160694
+ function getTagsByNumbers(db, sessionId, tagNumbers) {
160695
+ if (tagNumbers.length === 0)
160696
+ return [];
160697
+ if (tagNumbers.length > 900) {
160698
+ const all = [];
160699
+ for (let i = 0;i < tagNumbers.length; i += 900) {
160700
+ all.push(...getTagsByNumbers(db, sessionId, tagNumbers.slice(i, i + 900)));
160701
+ }
160702
+ return all;
160703
+ }
160704
+ const placeholders = tagNumbers.map(() => "?").join(",");
160705
+ const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${placeholders}) ORDER BY tag_number ASC, id ASC`).all(sessionId, ...tagNumbers).filter(isTagRow);
159608
160706
  return rows.map(toTagEntry);
159609
160707
  }
160708
+ function getMaxDroppedTagNumber(db, sessionId) {
160709
+ const row = getMaxDroppedTagNumberStatement(db).get(sessionId);
160710
+ return isMaxTagNumberRow(row) ? row.max_tag_number : 0;
160711
+ }
159610
160712
  function getTopNBySize(db, sessionId, n) {
159611
160713
  if (n <= 0) {
159612
160714
  return [];
159613
160715
  }
159614
- const rows = db.prepare("SELECT id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth FROM tags WHERE session_id = ? AND status = 'active' ORDER BY byte_size DESC, tag_number ASC LIMIT ?").all(sessionId, n).filter(isTagRow);
160716
+ const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND status = 'active' ORDER BY byte_size DESC, tag_number ASC LIMIT ?`).all(sessionId, n).filter(isTagRow);
159615
160717
  return rows.map(toTagEntry);
159616
160718
  }
159617
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements;
160719
+ function getGetToolTagNumberByOwnerStatement(db) {
160720
+ let stmt = getToolTagNumberByOwnerStatements.get(db);
160721
+ if (!stmt) {
160722
+ stmt = db.prepare(`SELECT tag_number FROM tags
160723
+ WHERE session_id = ? AND message_id = ?
160724
+ AND type = 'tool' AND tool_owner_message_id = ?
160725
+ LIMIT 1`);
160726
+ getToolTagNumberByOwnerStatements.set(db, stmt);
160727
+ }
160728
+ return stmt;
160729
+ }
160730
+ function getToolTagNumberByOwner(db, sessionId, callId, ownerMsgId) {
160731
+ const row = getGetToolTagNumberByOwnerStatement(db).get(sessionId, callId, ownerMsgId);
160732
+ return isTagNumberRow(row) ? row.tag_number : null;
160733
+ }
160734
+ function isNullOwnerToolTagRow(row) {
160735
+ if (row === null || typeof row !== "object")
160736
+ return false;
160737
+ const r = row;
160738
+ return typeof r.id === "number" && typeof r.tag_number === "number";
160739
+ }
160740
+ function getGetNullOwnerToolTagStatement(db) {
160741
+ let stmt = getNullOwnerToolTagStatements.get(db);
160742
+ if (!stmt) {
160743
+ stmt = db.prepare(`SELECT id, tag_number FROM tags
160744
+ WHERE session_id = ? AND message_id = ?
160745
+ AND type = 'tool' AND tool_owner_message_id IS NULL
160746
+ ORDER BY tag_number ASC
160747
+ LIMIT 1`);
160748
+ getNullOwnerToolTagStatements.set(db, stmt);
160749
+ }
160750
+ return stmt;
160751
+ }
160752
+ function getNullOwnerToolTag(db, sessionId, callId) {
160753
+ const row = getGetNullOwnerToolTagStatement(db).get(sessionId, callId);
160754
+ if (!isNullOwnerToolTagRow(row))
160755
+ return null;
160756
+ return { id: row.id, tagNumber: row.tag_number };
160757
+ }
160758
+ function getAdoptNullOwnerToolTagStatement(db) {
160759
+ let stmt = adoptNullOwnerToolTagStatements.get(db);
160760
+ if (!stmt) {
160761
+ stmt = db.prepare(`UPDATE tags
160762
+ SET tool_owner_message_id = ?
160763
+ WHERE id = ? AND tool_owner_message_id IS NULL`);
160764
+ adoptNullOwnerToolTagStatements.set(db, stmt);
160765
+ }
160766
+ return stmt;
160767
+ }
160768
+ function adoptNullOwnerToolTag(db, rowId, ownerMsgId) {
160769
+ const result = getAdoptNullOwnerToolTagStatement(db).run(ownerMsgId, rowId);
160770
+ return (result.changes ?? 0) === 1;
160771
+ }
160772
+ function getCandidateToolOwners(db, sessionId, callId) {
160773
+ const rows = db.prepare(`SELECT DISTINCT tool_owner_message_id
160774
+ FROM tags
160775
+ WHERE session_id = ?
160776
+ AND message_id = ?
160777
+ AND type = 'tool'
160778
+ AND tool_owner_message_id IS NOT NULL`).all(sessionId, callId);
160779
+ return rows.map((r) => r.tool_owner_message_id);
160780
+ }
160781
+ function pickNearestPriorOwner(candidates, currentMessageId, times) {
160782
+ const currentTime = times.get(currentMessageId);
160783
+ if (typeof currentTime !== "number")
160784
+ return null;
160785
+ let best = null;
160786
+ for (const id of candidates) {
160787
+ const t = times.get(id);
160788
+ if (typeof t !== "number")
160789
+ continue;
160790
+ if (t > currentTime)
160791
+ continue;
160792
+ if (t === currentTime && id >= currentMessageId)
160793
+ continue;
160794
+ if (best === null || t > best.time || t === best.time && id > best.id) {
160795
+ best = { id, time: t };
160796
+ }
160797
+ }
160798
+ return best?.id ?? null;
160799
+ }
160800
+ function getDeleteToolTagsByOwnerStatement(db) {
160801
+ let stmt = deleteToolTagsByOwnerStatements.get(db);
160802
+ if (!stmt) {
160803
+ stmt = db.prepare(`DELETE FROM tags
160804
+ WHERE session_id = ?
160805
+ AND type = 'tool'
160806
+ AND tool_owner_message_id = ?`);
160807
+ deleteToolTagsByOwnerStatements.set(db, stmt);
160808
+ }
160809
+ return stmt;
160810
+ }
160811
+ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
160812
+ const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
160813
+ return result.changes ?? 0;
160814
+ }
160815
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, getOwnerScopedToolTagNumbersStatements, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
159618
160816
  var init_storage_tags = __esm(() => {
159619
160817
  insertTagStatements = new WeakMap;
159620
160818
  updateTagStatusStatements = new WeakMap;
@@ -159626,6 +160824,13 @@ var init_storage_tags = __esm(() => {
159626
160824
  getTagNumberByMessageIdStatements = new WeakMap;
159627
160825
  updateTagByteSizeStatements = new WeakMap;
159628
160826
  updateTagInputByteSizeStatements = new WeakMap;
160827
+ getOwnerScopedToolTagNumbersStatements = new WeakMap;
160828
+ getActiveTagsBySessionStatements = new WeakMap;
160829
+ getMaxDroppedTagNumberStatements = new WeakMap;
160830
+ getToolTagNumberByOwnerStatements = new WeakMap;
160831
+ getNullOwnerToolTagStatements = new WeakMap;
160832
+ adoptNullOwnerToolTagStatements = new WeakMap;
160833
+ deleteToolTagsByOwnerStatements = new WeakMap;
159629
160834
  });
159630
160835
 
159631
160836
  // src/features/magic-context/storage.ts
@@ -159645,9 +160850,9 @@ var init_storage = __esm(async () => {
159645
160850
 
159646
160851
  // src/shared/models-dev-cache.ts
159647
160852
  import { createHash as createHash3 } from "node:crypto";
159648
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
160853
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
159649
160854
  import { homedir as homedir8, platform as platform3 } from "node:os";
159650
- import { join as join14 } from "node:path";
160855
+ import { join as join16 } from "node:path";
159651
160856
  function hashFast(input) {
159652
160857
  return createHash3("sha1").update(input).digest("hex");
159653
160858
  }
@@ -159658,16 +160863,16 @@ function getModelsJsonPath() {
159658
160863
  const cacheBase = getCacheDir();
159659
160864
  const source = process.env.OPENCODE_MODELS_URL?.trim();
159660
160865
  const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
159661
- return join14(cacheBase, "opencode", filename);
160866
+ return join16(cacheBase, "opencode", filename);
159662
160867
  }
159663
160868
  function getOpencodeConfigPath() {
159664
160869
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
159665
- const configDir = envDir ? envDir : platform3() === "win32" ? join14(homedir8(), ".config", "opencode") : join14(process.env.XDG_CONFIG_HOME || join14(homedir8(), ".config"), "opencode");
159666
- const jsonc = join14(configDir, "opencode.jsonc");
159667
- if (existsSync9(jsonc))
160870
+ const configDir = envDir ? envDir : platform3() === "win32" ? join16(homedir8(), ".config", "opencode") : join16(process.env.XDG_CONFIG_HOME || join16(homedir8(), ".config"), "opencode");
160871
+ const jsonc = join16(configDir, "opencode.jsonc");
160872
+ if (existsSync11(jsonc))
159668
160873
  return jsonc;
159669
- const json2 = join14(configDir, "opencode.json");
159670
- if (existsSync9(json2))
160874
+ const json2 = join16(configDir, "opencode.json");
160875
+ if (existsSync11(json2))
159671
160876
  return json2;
159672
160877
  return null;
159673
160878
  }
@@ -159680,23 +160885,12 @@ function resolveLimit(limit) {
159680
160885
  return limit.context;
159681
160886
  return;
159682
160887
  }
159683
- function resolveInterleavedField(interleaved) {
159684
- if (interleaved && typeof interleaved === "object" && typeof interleaved.field === "string" && interleaved.field.length > 0) {
159685
- return interleaved.field;
159686
- }
159687
- return;
159688
- }
159689
160888
  function setCachedModelMetadata(cache, key, model) {
159690
160889
  const limit = resolveLimit(model?.limit);
159691
- const interleavedField = resolveInterleavedField(model?.capabilities?.interleaved) ?? resolveInterleavedField(model?.interleaved);
159692
- if (limit === undefined && interleavedField === undefined) {
160890
+ if (limit === undefined) {
159693
160891
  return;
159694
160892
  }
159695
- const value = {};
159696
- if (limit !== undefined)
159697
- value.limit = limit;
159698
- if (interleavedField !== undefined)
159699
- value.interleavedField = interleavedField;
160893
+ const value = { limit };
159700
160894
  cache.set(key, value);
159701
160895
  const modes = model?.experimental?.modes;
159702
160896
  if (modes && typeof modes === "object") {
@@ -159710,7 +160904,7 @@ function loadModelsDevMetadataFromFile() {
159710
160904
  const modelsJsonPath = getModelsJsonPath();
159711
160905
  let fileFound = false;
159712
160906
  try {
159713
- if (existsSync9(modelsJsonPath)) {
160907
+ if (existsSync11(modelsJsonPath)) {
159714
160908
  fileFound = true;
159715
160909
  const raw = readFileSync7(modelsJsonPath, "utf-8");
159716
160910
  const data = JSON.parse(raw);
@@ -159727,7 +160921,7 @@ function loadModelsDevMetadataFromFile() {
159727
160921
  }
159728
160922
  try {
159729
160923
  const configPath = getOpencodeConfigPath();
159730
- if (configPath && existsSync9(configPath)) {
160924
+ if (configPath && existsSync11(configPath)) {
159731
160925
  let raw = readFileSync7(configPath, "utf-8");
159732
160926
  raw = raw.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/gm, (match) => match.startsWith('"') ? match : "");
159733
160927
  const config2 = JSON.parse(raw);
@@ -159768,8 +160962,18 @@ async function refreshModelLimitsFromApi(client) {
159768
160962
  const previousSize = apiCache?.size ?? null;
159769
160963
  apiCache = map2;
159770
160964
  apiLoadedAt = Date.now();
159771
- if (previousSize === null || previousSize !== map2.size) {
159772
- sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries${previousSize !== null ? ` (was ${previousSize})` : ""}`);
160965
+ if (previousSize === null) {
160966
+ recentlySeenApiSizes.add(map2.size);
160967
+ sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries`);
160968
+ } else if (previousSize !== map2.size) {
160969
+ const sizeAlreadySeen = recentlySeenApiSizes.has(map2.size);
160970
+ recentlySeenApiSizes.add(map2.size);
160971
+ if (!sizeAlreadySeen) {
160972
+ sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries (was ${previousSize})`);
160973
+ } else if (!oscillationLogged) {
160974
+ oscillationLogged = true;
160975
+ sessionLog("global", `models-dev-cache: API count oscillating between ${[...recentlySeenApiSizes].sort((a, b) => a - b).join(" ↔ ")} — likely upstream provider plugin returning slightly different model sets between calls (e.g. github-copilot's /models endpoint toggling model_picker_enabled). Suppressing further size-change logs.`);
160976
+ }
159773
160977
  }
159774
160978
  } catch (error48) {
159775
160979
  sessionLog("global", "models-dev-cache: API refresh failed:", error48 instanceof Error ? error48.message : String(error48));
@@ -159789,27 +160993,12 @@ function getModelsDevContextLimit(providerID, modelID) {
159789
160993
  }
159790
160994
  return fileCache.get(key)?.limit;
159791
160995
  }
159792
- function getModelsDevInterleavedField(providerID, modelID) {
159793
- const key = `${providerID}/${modelID}`;
159794
- if (apiCache) {
159795
- const fromApi = apiCache.get(key)?.interleavedField;
159796
- if (typeof fromApi === "string" && fromApi.length > 0) {
159797
- return fromApi;
159798
- }
159799
- }
159800
- const now = Date.now();
159801
- if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
159802
- fileLastAttempt = now;
159803
- fileCache = loadModelsDevMetadataFromFile();
159804
- }
159805
- const fromFile = fileCache.get(key)?.interleavedField;
159806
- return typeof fromFile === "string" && fromFile.length > 0 ? fromFile : undefined;
159807
- }
159808
- var RELOAD_INTERVAL_MS, apiCache = null, apiLoadedAt = 0, fileCache = null, fileLastAttempt = 0;
160996
+ var RELOAD_INTERVAL_MS, apiCache = null, apiLoadedAt = 0, recentlySeenApiSizes, oscillationLogged = false, fileCache = null, fileLastAttempt = 0;
159809
160997
  var init_models_dev_cache = __esm(() => {
159810
160998
  init_data_path();
159811
160999
  init_logger();
159812
161000
  RELOAD_INTERVAL_MS = 5 * 60 * 1000;
161001
+ recentlySeenApiSizes = new Set;
159813
161002
  });
159814
161003
 
159815
161004
  // src/shared/rpc-notifications.ts
@@ -160059,9 +161248,9 @@ var init_compartment_runner_validation = __esm(async () => {
160059
161248
  });
160060
161249
 
160061
161250
  // src/hooks/magic-context/compartment-runner-historian.ts
160062
- import { mkdirSync as mkdirSync3, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
161251
+ import { mkdirSync as mkdirSync4, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
160063
161252
  import { tmpdir as tmpdir2 } from "node:os";
160064
- import { join as join15 } from "node:path";
161253
+ import { join as join17 } from "node:path";
160065
161254
  async function runValidatedHistorianPass(args) {
160066
161255
  const firstRun = await runHistorianPrompt({
160067
161256
  ...args,
@@ -160301,11 +161490,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
160301
161490
  }
160302
161491
  function dumpHistorianResponse(sessionId, label, text) {
160303
161492
  try {
160304
- mkdirSync3(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
161493
+ mkdirSync4(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
160305
161494
  const safeSessionId = sanitizeDumpName(sessionId);
160306
161495
  const safeLabel = sanitizeDumpName(label);
160307
- const dumpPath = join15(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
160308
- writeFileSync3(dumpPath, text, "utf8");
161496
+ const dumpPath = join17(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
161497
+ writeFileSync4(dumpPath, text, "utf8");
160309
161498
  sessionLog(sessionId, "compartment agent: historian response dumped", {
160310
161499
  label,
160311
161500
  dumpPath
@@ -160329,7 +161518,7 @@ var init_compartment_runner_historian = __esm(async () => {
160329
161518
  init_assistant_message_extractor();
160330
161519
  init_compartment_prompt();
160331
161520
  await init_compartment_runner_validation();
160332
- HISTORIAN_RESPONSE_DUMP_DIR = join15(tmpdir2(), "magic-context-historian");
161521
+ HISTORIAN_RESPONSE_DUMP_DIR = join17(tmpdir2(), "magic-context-historian");
160333
161522
  });
160334
161523
 
160335
161524
  // src/hooks/magic-context/compartment-runner-state-xml.ts
@@ -161308,7 +162497,7 @@ var init_derive_budgets = __esm(() => {
161308
162497
  });
161309
162498
 
161310
162499
  // src/features/magic-context/compaction-marker.ts
161311
- import { join as join16 } from "node:path";
162500
+ import { join as join18 } from "node:path";
161312
162501
  function randomBase62(length) {
161313
162502
  const chars = [];
161314
162503
  for (let i = 0;i < length; i++) {
@@ -161328,7 +162517,7 @@ function generatePartId(timestampMs, counter = 0n) {
161328
162517
  return generateId("prt", timestampMs, counter);
161329
162518
  }
161330
162519
  function getOpenCodeDbPath3() {
161331
- return join16(getDataDir(), "opencode", "opencode.db");
162520
+ return join18(getDataDir(), "opencode", "opencode.db");
161332
162521
  }
161333
162522
  function isOpenCodeSchemaCompatible(db, dbPath) {
161334
162523
  if (cachedSchemaCompatible?.path === dbPath) {
@@ -161465,7 +162654,7 @@ var init_compaction_marker = __esm(async () => {
161465
162654
  });
161466
162655
 
161467
162656
  // src/hooks/magic-context/compaction-marker-manager.ts
161468
- import { join as join17 } from "node:path";
162657
+ import { join as join19 } from "node:path";
161469
162658
  function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
161470
162659
  const existing = getPersistedCompactionMarkerState(db, sessionId);
161471
162660
  if (existing) {
@@ -161508,7 +162697,7 @@ function removeCompactionMarkerForSession(db, sessionId) {
161508
162697
  }
161509
162698
  }
161510
162699
  function checkCompactionMarkerConsistency(db) {
161511
- const opencodeDbPath = join17(getDataDir(), "opencode", "opencode.db");
162700
+ const opencodeDbPath = join19(getDataDir(), "opencode", "opencode.db");
161512
162701
  let opencodeDb;
161513
162702
  try {
161514
162703
  opencodeDb = new Database(opencodeDbPath, { readonly: true });
@@ -161895,31 +163084,31 @@ var init_caveman = __esm(() => {
161895
163084
  });
161896
163085
 
161897
163086
  // src/hooks/magic-context/historian-state-file.ts
161898
- import { mkdirSync as mkdirSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
163087
+ import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
161899
163088
  import { tmpdir as tmpdir3 } from "node:os";
161900
- import { join as join18 } from "node:path";
163089
+ import { join as join20 } from "node:path";
161901
163090
  function maybeWriteHistorianStateFile(sessionId, existingState) {
161902
163091
  if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
161903
163092
  return;
161904
163093
  try {
161905
- mkdirSync4(HISTORIAN_STATE_DIR, { recursive: true });
161906
- const path4 = join18(HISTORIAN_STATE_DIR, `state-${sessionId}-${Date.now()}.xml`);
161907
- writeFileSync4(path4, existingState, "utf8");
161908
- return path4;
163094
+ mkdirSync5(HISTORIAN_STATE_DIR, { recursive: true });
163095
+ const path5 = join20(HISTORIAN_STATE_DIR, `state-${sessionId}-${Date.now()}.xml`);
163096
+ writeFileSync5(path5, existingState, "utf8");
163097
+ return path5;
161909
163098
  } catch {
161910
163099
  return;
161911
163100
  }
161912
163101
  }
161913
- function cleanupHistorianStateFile(path4) {
161914
- if (!path4)
163102
+ function cleanupHistorianStateFile(path5) {
163103
+ if (!path5)
161915
163104
  return;
161916
163105
  try {
161917
- unlinkSync2(path4);
163106
+ unlinkSync2(path5);
161918
163107
  } catch {}
161919
163108
  }
161920
163109
  var HISTORIAN_STATE_INLINE_THRESHOLD = 30000, HISTORIAN_STATE_DIR;
161921
163110
  var init_historian_state_file = __esm(() => {
161922
- HISTORIAN_STATE_DIR = join18(tmpdir3(), "magic-context-historian");
163111
+ HISTORIAN_STATE_DIR = join20(tmpdir3(), "magic-context-historian");
161923
163112
  });
161924
163113
 
161925
163114
  // src/features/magic-context/memory/embedding-backfill.ts
@@ -162424,12 +163613,24 @@ var init_compartment_runner_compressor = __esm(async () => {
162424
163613
  // src/hooks/magic-context/compartment-runner-drop-queue.ts
162425
163614
  function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex) {
162426
163615
  const tags = getTagsBySession(db, sessionId);
162427
- const rawTagKeys = new Set(getRawSessionTagKeysThrough(sessionId, upToMessageIndex));
163616
+ const { messageFileKeys, toolObservations } = getRawSessionTagKeysThrough(sessionId, upToMessageIndex);
162428
163617
  let dropsQueued = 0;
162429
163618
  for (const tag of tags) {
162430
163619
  if (tag.status !== "active")
162431
163620
  continue;
162432
- if (rawTagKeys.has(tag.messageId)) {
163621
+ if (tag.type === "tool") {
163622
+ const observedOwners = toolObservations.get(tag.messageId);
163623
+ if (!observedOwners)
163624
+ continue;
163625
+ if (tag.toolOwnerMessageId !== null) {
163626
+ if (!observedOwners.has(tag.toolOwnerMessageId))
163627
+ continue;
163628
+ }
163629
+ queuePendingOp(db, sessionId, tag.tagNumber, "drop");
163630
+ dropsQueued += 1;
163631
+ continue;
163632
+ }
163633
+ if (messageFileKeys.has(tag.messageId)) {
162433
163634
  queuePendingOp(db, sessionId, tag.tagNumber, "drop");
162434
163635
  dropsQueued += 1;
162435
163636
  }
@@ -162989,8 +164190,8 @@ var exports_tui_config = {};
162989
164190
  __export(exports_tui_config, {
162990
164191
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
162991
164192
  });
162992
- import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
162993
- import { dirname as dirname5, join as join21 } from "node:path";
164193
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
164194
+ import { dirname as dirname6, join as join23 } from "node:path";
162994
164195
  function isMagicContextEntry(entry) {
162995
164196
  if (!entry)
162996
164197
  return false;
@@ -163004,11 +164205,11 @@ function isMagicContextEntry(entry) {
163004
164205
  }
163005
164206
  function resolveTuiConfigPath() {
163006
164207
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
163007
- const jsoncPath = join21(configDir, "tui.jsonc");
163008
- const jsonPath = join21(configDir, "tui.json");
163009
- if (existsSync11(jsoncPath))
164208
+ const jsoncPath = join23(configDir, "tui.jsonc");
164209
+ const jsonPath = join23(configDir, "tui.json");
164210
+ if (existsSync13(jsoncPath))
163010
164211
  return jsoncPath;
163011
- if (existsSync11(jsonPath))
164212
+ if (existsSync13(jsonPath))
163012
164213
  return jsonPath;
163013
164214
  return jsonPath;
163014
164215
  }
@@ -163016,7 +164217,7 @@ function ensureTuiPluginEntry() {
163016
164217
  try {
163017
164218
  const configPath = resolveTuiConfigPath();
163018
164219
  let config2 = {};
163019
- if (existsSync11(configPath)) {
164220
+ if (existsSync13(configPath)) {
163020
164221
  const raw = readFileSync9(configPath, "utf-8");
163021
164222
  config2 = import_comment_json3.parse(raw) ?? {};
163022
164223
  }
@@ -163036,8 +164237,8 @@ function ensureTuiPluginEntry() {
163036
164237
  plugins.push(PLUGIN_ENTRY);
163037
164238
  }
163038
164239
  config2.plugin = plugins;
163039
- mkdirSync6(dirname5(configPath), { recursive: true });
163040
- writeFileSync6(configPath, `${import_comment_json3.stringify(config2, null, 2)}
164240
+ mkdirSync7(dirname6(configPath), { recursive: true });
164241
+ writeFileSync7(configPath, `${import_comment_json3.stringify(config2, null, 2)}
163041
164242
  `);
163042
164243
  log(`[magic-context] updated TUI plugin entry in ${configPath}`);
163043
164244
  return true;
@@ -163813,42 +165014,8 @@ async function runSidekick(deps) {
163813
165014
  }
163814
165015
  }
163815
165016
 
163816
- // src/features/magic-context/tool-definition-tokens.ts
163817
- init_read_session_formatting();
163818
- var measurements = new Map;
163819
- function keyFor(providerID, modelID, agentName) {
163820
- const agent = agentName && agentName.length > 0 ? agentName : "default";
163821
- return `${providerID}/${modelID}/${agent}`;
163822
- }
163823
- function recordToolDefinition(providerID, modelID, agentName, toolID, description, parameters) {
163824
- if (!providerID || !modelID || !toolID)
163825
- return;
163826
- const key = keyFor(providerID, modelID, agentName);
163827
- let paramsText = "";
163828
- try {
163829
- paramsText = parameters === undefined ? "" : JSON.stringify(parameters);
163830
- } catch {
163831
- paramsText = "";
163832
- }
163833
- const tokens = estimateTokens(description ?? "") + estimateTokens(paramsText);
163834
- let inner = measurements.get(key);
163835
- if (!inner) {
163836
- inner = new Map;
163837
- measurements.set(key, inner);
163838
- }
163839
- inner.set(toolID, tokens);
163840
- }
163841
- function getMeasuredToolDefinitionTokens(providerID, modelID, agentName) {
163842
- if (!providerID || !modelID)
163843
- return;
163844
- const inner = measurements.get(keyFor(providerID, modelID, agentName));
163845
- if (!inner || inner.size === 0)
163846
- return;
163847
- let total = 0;
163848
- for (const tokens of inner.values())
163849
- total += tokens;
163850
- return total;
163851
- }
165017
+ // src/index.ts
165018
+ init_tool_definition_tokens();
163852
165019
 
163853
165020
  // src/hooks/auto-update-checker/index.ts
163854
165021
  init_logger();
@@ -164130,24 +165297,27 @@ function stripPackageNameFromPath(pathValue, packageName) {
164130
165297
  }
164131
165298
  return current;
164132
165299
  }
164133
- function removeFromBunLock(installDir, packageName) {
164134
- const lockPath = join5(installDir, "bun.lock");
165300
+ function removeFromPackageLock(installDir, packageName) {
165301
+ const lockPath = join5(installDir, "package-lock.json");
164135
165302
  if (!existsSync5(lockPath))
164136
165303
  return false;
164137
165304
  try {
164138
165305
  const lock = import_comment_json2.parse(readFileSync5(lockPath, "utf-8"));
164139
165306
  let modified = false;
164140
- if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
164141
- delete lock.workspaces[""].dependencies[packageName];
164142
- modified = true;
165307
+ if (lock.packages) {
165308
+ const key = `node_modules/${packageName}`;
165309
+ if (lock.packages[key] !== undefined) {
165310
+ delete lock.packages[key];
165311
+ modified = true;
165312
+ }
164143
165313
  }
164144
- if (lock.packages?.[packageName]) {
164145
- delete lock.packages[packageName];
165314
+ if (lock.dependencies?.[packageName]) {
165315
+ delete lock.dependencies[packageName];
164146
165316
  modified = true;
164147
165317
  }
164148
165318
  if (modified) {
164149
165319
  writeFileSync2(lockPath, JSON.stringify(lock, null, 2));
164150
- log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
165320
+ log(`[auto-update-checker] Removed from package-lock.json: ${packageName}`);
164151
165321
  }
164152
165322
  return modified;
164153
165323
  } catch {
@@ -164212,7 +165382,7 @@ function preparePackageUpdate(version2, packageName = PACKAGE_NAME, runtimePacka
164212
165382
  if (!ensureDependencyVersion(installContext.packageJsonPath, packageName, version2))
164213
165383
  return null;
164214
165384
  const packageRemoved = removeInstalledPackage(installContext.installDir, packageName);
164215
- const lockRemoved = removeFromBunLock(installContext.installDir, packageName);
165385
+ const lockRemoved = removeFromPackageLock(installContext.installDir, packageName);
164216
165386
  if (!packageRemoved && !lockRemoved) {
164217
165387
  log(`[auto-update-checker] No cached package artifacts removed for ${packageName}; continuing with updated dependency spec`);
164218
165388
  }
@@ -164222,12 +165392,12 @@ function preparePackageUpdate(version2, packageName = PACKAGE_NAME, runtimePacka
164222
165392
  return null;
164223
165393
  }
164224
165394
  }
164225
- async function runBunInstallSafe(installDir, options = {}) {
165395
+ async function runNpmInstallSafe(installDir, options = {}) {
164226
165396
  let timeout = null;
164227
165397
  try {
164228
165398
  if (options.signal?.aborted)
164229
165399
  return false;
164230
- const proc = spawn("bun", ["install"], {
165400
+ const proc = spawn("npm", ["install", "--no-audit", "--no-fund", "--no-progress"], {
164231
165401
  cwd: installDir,
164232
165402
  stdio: "pipe"
164233
165403
  });
@@ -164252,7 +165422,7 @@ async function runBunInstallSafe(installDir, options = {}) {
164252
165422
  }
164253
165423
  return result;
164254
165424
  } catch (err) {
164255
- warn2(`[auto-update-checker] bun install error: ${String(err)}`);
165425
+ warn2(`[auto-update-checker] npm install error: ${String(err)}`);
164256
165426
  return false;
164257
165427
  } finally {
164258
165428
  if (timeout)
@@ -164370,7 +165540,7 @@ async function runBackgroundUpdateCheck(ctx, options) {
164370
165540
  warn3("[auto-update-checker] Failed to prepare install root for auto-update");
164371
165541
  return;
164372
165542
  }
164373
- const installSuccess = await runBunInstallSafe(installDir, { signal: options.signal });
165543
+ const installSuccess = await runNpmInstallSafe(installDir, { signal: options.signal });
164374
165544
  if (installSuccess) {
164375
165545
  showToast(ctx, "Magic Context Updated!", `v${currentVersion} → v${latestVersion}
164376
165546
  Restart OpenCode to apply.`, "success", 8000);
@@ -164378,7 +165548,7 @@ Restart OpenCode to apply.`, "success", 8000);
164378
165548
  return;
164379
165549
  }
164380
165550
  showToast(ctx, `Magic Context ${latestVersion}`, `v${latestVersion} available, but auto-update failed to install it. Check logs or retry manually.`, "error", 8000);
164381
- warn3("[auto-update-checker] bun install failed; update not installed");
165551
+ warn3("[auto-update-checker] npm install failed; update not installed");
164382
165552
  }
164383
165553
  function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
164384
165554
  ctx.client.tui.showToast({ body: { title, message, variant, duration: duration3 } }).catch(() => {});
@@ -164395,7 +165565,8 @@ function createLiveSessionState() {
164395
165565
  agentBySession: new Map,
164396
165566
  historyRefreshSessions: new Set,
164397
165567
  systemPromptRefreshSessions: new Set,
164398
- pendingMaterializationSessions: new Set
165568
+ pendingMaterializationSessions: new Set,
165569
+ sessionDirectoryBySession: new Map
164399
165570
  };
164400
165571
  }
164401
165572
 
@@ -164562,8 +165733,8 @@ init_assistant_message_extractor();
164562
165733
  init_data_path();
164563
165734
  init_logger();
164564
165735
  await init_sqlite();
164565
- import { existsSync as existsSync7 } from "node:fs";
164566
- import { join as join11 } from "node:path";
165736
+ import { existsSync as existsSync8 } from "node:fs";
165737
+ import { join as join12 } from "node:path";
164567
165738
 
164568
165739
  // src/features/magic-context/key-files/identify-key-files.ts
164569
165740
  init_logger();
@@ -164954,11 +166125,11 @@ function countNewIds(beforeIds, afterIds) {
164954
166125
  return count;
164955
166126
  }
164956
166127
  function getOpenCodeDbPath2() {
164957
- return join11(getDataDir(), "opencode", "opencode.db");
166128
+ return join12(getDataDir(), "opencode", "opencode.db");
164958
166129
  }
164959
166130
  function openOpenCodeDb() {
164960
166131
  const dbPath = getOpenCodeDbPath2();
164961
- if (!existsSync7(dbPath)) {
166132
+ if (!existsSync8(dbPath)) {
164962
166133
  log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
164963
166134
  return null;
164964
166135
  }
@@ -165208,8 +166379,8 @@ async function runDream(args) {
165208
166379
  try {
165209
166380
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
165210
166381
  const existingDocs = taskName === "maintain-docs" ? {
165211
- architecture: existsSync7(join11(docsDir, "ARCHITECTURE.md")),
165212
- structure: existsSync7(join11(docsDir, "STRUCTURE.md"))
166382
+ architecture: existsSync8(join12(docsDir, "ARCHITECTURE.md")),
166383
+ structure: existsSync8(join12(docsDir, "STRUCTURE.md"))
165213
166384
  } : undefined;
165214
166385
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
165215
166386
  id: um.id,
@@ -166535,14 +167706,46 @@ function createScheduler(config2) {
166535
167706
 
166536
167707
  // src/features/magic-context/tagger.ts
166537
167708
  init_storage_tags();
167709
+ var TOOL_COMPOSITE_KEY_SEP = "\x00";
167710
+ function makeToolCompositeKey(ownerMsgId, callId) {
167711
+ return `${ownerMsgId}${TOOL_COMPOSITE_KEY_SEP}${callId}`;
167712
+ }
166538
167713
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
166539
- var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
167714
+ var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
167715
+ var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
167716
+ var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
167717
+ var probeDataVersionStatements = new WeakMap;
167718
+ var probeTotalChangesStatements = new WeakMap;
167719
+ function getProbeDataVersionStatement(db) {
167720
+ let stmt = probeDataVersionStatements.get(db);
167721
+ if (!stmt) {
167722
+ stmt = db.prepare(PROBE_DATA_VERSION_SQL);
167723
+ probeDataVersionStatements.set(db, stmt);
167724
+ }
167725
+ return stmt;
167726
+ }
167727
+ function getProbeTotalChangesStatement(db) {
167728
+ let stmt = probeTotalChangesStatements.get(db);
167729
+ if (!stmt) {
167730
+ stmt = db.prepare(PROBE_TOTAL_CHANGES_SQL);
167731
+ probeTotalChangesStatements.set(db, stmt);
167732
+ }
167733
+ return stmt;
167734
+ }
166540
167735
  function isAssignmentRow(row) {
166541
167736
  if (row === null || typeof row !== "object") {
166542
167737
  return false;
166543
167738
  }
166544
167739
  const candidate = row;
166545
- return typeof candidate.message_id === "string" && typeof candidate.tag_number === "number";
167740
+ if (typeof candidate.message_id !== "string")
167741
+ return false;
167742
+ if (typeof candidate.tag_number !== "number")
167743
+ return false;
167744
+ if (candidate.type !== "message" && candidate.type !== "tool" && candidate.type !== "file")
167745
+ return false;
167746
+ if (candidate.tool_owner_message_id !== null && typeof candidate.tool_owner_message_id !== "string")
167747
+ return false;
167748
+ return true;
166546
167749
  }
166547
167750
  var UPSERT_COUNTER_SQL = `
166548
167751
  INSERT INTO session_meta (session_id, counter, harness)
@@ -166576,6 +167779,7 @@ var MAX_TAG_ALLOC_RETRIES = 5;
166576
167779
  function createTagger() {
166577
167780
  const counters = new Map;
166578
167781
  const assignments = new Map;
167782
+ const loadSignatures = new Map;
166579
167783
  function getSessionAssignments(sessionId) {
166580
167784
  let map2 = assignments.get(sessionId);
166581
167785
  if (!map2) {
@@ -166594,15 +167798,15 @@ function createTagger() {
166594
167798
  counters.set(sessionId, next);
166595
167799
  getUpsertCounterStatement(db).run(sessionId, next, getHarness());
166596
167800
  }
166597
- function assignTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize = 0, toolName = null, inputByteSize = 0) {
167801
+ function allocateTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize, toolName, inputByteSize, toolOwnerMessageId, mapKey, dbExistingLookup) {
166598
167802
  const sessionAssignments = getSessionAssignments(sessionId);
166599
- const existing = sessionAssignments.get(messageId);
167803
+ const existing = sessionAssignments.get(mapKey);
166600
167804
  if (existing !== undefined) {
166601
167805
  return existing;
166602
167806
  }
166603
- const dbExisting = getTagNumberByMessageId(db, sessionId, messageId);
167807
+ const dbExisting = dbExistingLookup();
166604
167808
  if (dbExisting !== null) {
166605
- sessionAssignments.set(messageId, dbExisting);
167809
+ sessionAssignments.set(mapKey, dbExisting);
166606
167810
  syncCounterAtLeast(sessionId, db, dbExisting);
166607
167811
  return dbExisting;
166608
167812
  }
@@ -166612,16 +167816,16 @@ function createTagger() {
166612
167816
  const next = Math.max(memCounter, dbMax) + 1;
166613
167817
  try {
166614
167818
  db.transaction(() => {
166615
- insertTag(db, sessionId, messageId, type, byteSize2, next, reasoningByteSize, toolName, inputByteSize);
167819
+ insertTag(db, sessionId, messageId, type, byteSize2, next, reasoningByteSize, toolName, inputByteSize, toolOwnerMessageId);
166616
167820
  getUpsertCounterStatement(db).run(sessionId, next, getHarness());
166617
167821
  })();
166618
167822
  } catch (error48) {
166619
167823
  if (!isUniqueConstraintError(error48)) {
166620
167824
  throw error48;
166621
167825
  }
166622
- const racedRow = getTagNumberByMessageId(db, sessionId, messageId);
167826
+ const racedRow = dbExistingLookup();
166623
167827
  if (racedRow !== null) {
166624
- sessionAssignments.set(messageId, racedRow);
167828
+ sessionAssignments.set(mapKey, racedRow);
166625
167829
  syncCounterAtLeast(sessionId, db, racedRow);
166626
167830
  return racedRow;
166627
167831
  }
@@ -166630,51 +167834,124 @@ function createTagger() {
166630
167834
  continue;
166631
167835
  }
166632
167836
  counters.set(sessionId, next);
166633
- sessionAssignments.set(messageId, next);
167837
+ sessionAssignments.set(mapKey, next);
166634
167838
  return next;
166635
167839
  }
166636
- throw new Error(`tagger.assignTag: failed to allocate tag for session=${sessionId} message=${messageId} after ${MAX_TAG_ALLOC_RETRIES} retries`);
167840
+ throw new Error(`tagger.allocateTag: failed to allocate tag for session=${sessionId} key=${mapKey} after ${MAX_TAG_ALLOC_RETRIES} retries`);
167841
+ }
167842
+ function assignTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize = 0, toolName = null, inputByteSize = 0) {
167843
+ if (type === "tool") {
167844
+ throw new Error("tagger.assignTag: type='tool' is forbidden — use assignToolTag(sessionId, callId, ownerMsgId, ...)");
167845
+ }
167846
+ return allocateTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize, toolName, inputByteSize, null, messageId, () => getTagNumberByMessageId(db, sessionId, messageId));
167847
+ }
167848
+ function assignToolTag(sessionId, callId, ownerMsgId, byteSize2, db, reasoningByteSize = 0, toolName = null, inputByteSize = 0) {
167849
+ const compositeKey = makeToolCompositeKey(ownerMsgId, callId);
167850
+ const sessionAssignments = getSessionAssignments(sessionId);
167851
+ const existing = sessionAssignments.get(compositeKey);
167852
+ if (existing !== undefined) {
167853
+ return existing;
167854
+ }
167855
+ const dbHit = getToolTagNumberByOwner(db, sessionId, callId, ownerMsgId);
167856
+ if (dbHit !== null) {
167857
+ sessionAssignments.set(compositeKey, dbHit);
167858
+ syncCounterAtLeast(sessionId, db, dbHit);
167859
+ return dbHit;
167860
+ }
167861
+ for (let attempt = 0;attempt < MAX_TAG_ALLOC_RETRIES; attempt += 1) {
167862
+ const orphan = getNullOwnerToolTag(db, sessionId, callId);
167863
+ if (orphan === null)
167864
+ break;
167865
+ const claimed = adoptNullOwnerToolTag(db, orphan.id, ownerMsgId);
167866
+ if (claimed) {
167867
+ sessionAssignments.set(compositeKey, orphan.tagNumber);
167868
+ syncCounterAtLeast(sessionId, db, orphan.tagNumber);
167869
+ return orphan.tagNumber;
167870
+ }
167871
+ const recheck = getToolTagNumberByOwner(db, sessionId, callId, ownerMsgId);
167872
+ if (recheck !== null) {
167873
+ sessionAssignments.set(compositeKey, recheck);
167874
+ syncCounterAtLeast(sessionId, db, recheck);
167875
+ return recheck;
167876
+ }
167877
+ }
167878
+ return allocateTag(sessionId, callId, "tool", byteSize2, db, reasoningByteSize, toolName, inputByteSize, ownerMsgId, compositeKey, () => getToolTagNumberByOwner(db, sessionId, callId, ownerMsgId));
166637
167879
  }
166638
- function getTag(sessionId, messageId) {
167880
+ function getTag(sessionId, messageId, _type) {
166639
167881
  return assignments.get(sessionId)?.get(messageId);
166640
167882
  }
167883
+ function getToolTag(sessionId, callId, ownerMsgId) {
167884
+ return assignments.get(sessionId)?.get(makeToolCompositeKey(ownerMsgId, callId));
167885
+ }
166641
167886
  function bindTag(sessionId, messageId, tagNumber) {
166642
167887
  getSessionAssignments(sessionId).set(messageId, tagNumber);
166643
167888
  }
167889
+ function bindToolTag(sessionId, callId, ownerMsgId, tagNumber) {
167890
+ getSessionAssignments(sessionId).set(makeToolCompositeKey(ownerMsgId, callId), tagNumber);
167891
+ }
166644
167892
  function getAssignments(sessionId) {
166645
167893
  return getSessionAssignments(sessionId);
166646
167894
  }
166647
167895
  function resetCounter(sessionId, db) {
166648
167896
  counters.set(sessionId, 0);
166649
167897
  assignments.delete(sessionId);
167898
+ loadSignatures.delete(sessionId);
166650
167899
  getResetCounterStatement(db).run(sessionId, getHarness());
166651
167900
  }
166652
167901
  function getCounter(sessionId) {
166653
167902
  return counters.get(sessionId) ?? 0;
166654
167903
  }
167904
+ function probeSignature(db) {
167905
+ const dvRow = getProbeDataVersionStatement(db).get();
167906
+ const tcRow = getProbeTotalChangesStatement(db).get();
167907
+ return {
167908
+ dataVersion: dvRow?.data_version ?? 0,
167909
+ totalChanges: tcRow?.tc ?? 0
167910
+ };
167911
+ }
166655
167912
  function initFromDb(sessionId, db) {
167913
+ const probe = probeSignature(db);
167914
+ const cached2 = loadSignatures.get(sessionId);
167915
+ if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.totalChanges === probe.totalChanges) {
167916
+ return;
167917
+ }
166656
167918
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
166657
167919
  const assignmentRows = db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId).filter(isAssignmentRow);
166658
167920
  const sessionAssignments = getSessionAssignments(sessionId);
166659
167921
  sessionAssignments.clear();
166660
167922
  let maxTagNumber = 0;
166661
167923
  for (const assignment of assignmentRows) {
166662
- sessionAssignments.set(assignment.message_id, assignment.tag_number);
167924
+ if (assignment.type === "tool") {
167925
+ if (assignment.tool_owner_message_id !== null) {
167926
+ sessionAssignments.set(makeToolCompositeKey(assignment.tool_owner_message_id, assignment.message_id), assignment.tag_number);
167927
+ }
167928
+ } else {
167929
+ sessionAssignments.set(assignment.message_id, assignment.tag_number);
167930
+ }
166663
167931
  if (assignment.tag_number > maxTagNumber) {
166664
167932
  maxTagNumber = assignment.tag_number;
166665
167933
  }
166666
167934
  }
166667
167935
  const counter = Math.max(row?.counter ?? 0, maxTagNumber, counters.get(sessionId) ?? 0);
166668
167936
  counters.set(sessionId, counter);
167937
+ loadSignatures.set(sessionId, {
167938
+ db,
167939
+ dataVersion: probe.dataVersion,
167940
+ totalChanges: probe.totalChanges
167941
+ });
166669
167942
  }
166670
167943
  function cleanup(sessionId) {
166671
167944
  counters.delete(sessionId);
166672
167945
  assignments.delete(sessionId);
167946
+ loadSignatures.delete(sessionId);
166673
167947
  }
166674
167948
  return {
166675
167949
  assignTag,
167950
+ assignToolTag,
166676
167951
  getTag,
167952
+ getToolTag,
166677
167953
  bindTag,
167954
+ bindToolTag,
166678
167955
  getAssignments,
166679
167956
  resetCounter,
166680
167957
  getCounter,
@@ -167318,21 +168595,146 @@ ${snap.error}`;
167318
168595
  // src/hooks/magic-context/hook.ts
167319
168596
  init_derive_budgets();
167320
168597
 
167321
- // src/features/magic-context/overflow-detection.ts
167322
- var OVERFLOW_PATTERNS = [
167323
- /prompt is too long/i,
167324
- /input is too long for requested model/i,
167325
- /exceeds the context window/i,
167326
- /input token count.*exceeds the maximum/i,
167327
- /maximum prompt length is \d+/i,
167328
- /reduce the length of the messages/i,
167329
- /maximum context length is \d+ tokens/i,
167330
- /exceeds the limit of \d+/i,
167331
- /exceeds the available context size/i,
167332
- /greater than the context length/i,
167333
- /context window exceeds limit/i,
167334
- /exceeded model token limit/i,
167335
- /context[_ ]length[_ ]exceeded/i,
168598
+ // src/features/magic-context/message-index-async.ts
168599
+ init_logger();
168600
+ await init_message_index();
168601
+ var INCREMENTAL_DEBOUNCE_MS = 100;
168602
+ var reconciledSessions = new Set;
168603
+ var reconciliationScheduledSessions = new Set;
168604
+ var sessionLocks = new Map;
168605
+ var incrementalTimers = new Map;
168606
+ var pendingIncrementalKeys = new Set;
168607
+ function defer(fn) {
168608
+ const immediate = globalThis.setImmediate;
168609
+ if (typeof immediate === "function") {
168610
+ immediate(fn);
168611
+ return;
168612
+ }
168613
+ setTimeout(fn, 0);
168614
+ }
168615
+ function runWithSessionLock(sessionId, operation) {
168616
+ const previous = sessionLocks.get(sessionId) ?? Promise.resolve();
168617
+ const run = previous.catch(() => {
168618
+ return;
168619
+ }).then(async () => {
168620
+ await operation();
168621
+ });
168622
+ sessionLocks.set(sessionId, run);
168623
+ run.finally(() => {
168624
+ if (sessionLocks.get(sessionId) === run) {
168625
+ sessionLocks.delete(sessionId);
168626
+ }
168627
+ }).catch(() => {
168628
+ return;
168629
+ });
168630
+ return run;
168631
+ }
168632
+ function logIndexingError(sessionId, action, error48) {
168633
+ sessionLog(sessionId, `message FTS async ${action} failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
168634
+ log(`[message-index-async] ${action} failed for ${sessionId}:`, error48);
168635
+ }
168636
+ async function reconcileSessionIndex(db, sessionId, readMessages) {
168637
+ await runWithSessionLock(sessionId, () => {
168638
+ if (reconciledSessions.has(sessionId)) {
168639
+ return;
168640
+ }
168641
+ const messages = readMessages(sessionId);
168642
+ if (messages.length === 0) {
168643
+ clearIndexedMessages(db, sessionId);
168644
+ reconciledSessions.add(sessionId);
168645
+ return;
168646
+ }
168647
+ let lastIndexedOrdinal = getLastIndexedOrdinal(db, sessionId);
168648
+ if (lastIndexedOrdinal > messages.length) {
168649
+ clearIndexedMessages(db, sessionId);
168650
+ lastIndexedOrdinal = 0;
168651
+ }
168652
+ const watermark = Math.min(lastIndexedOrdinal, messages.length);
168653
+ indexMessagesAfterOrdinal(db, sessionId, messages, watermark, messages.length);
168654
+ reconciledSessions.add(sessionId);
168655
+ });
168656
+ }
168657
+ function scheduleReconciliation(db, sessionId, readMessages) {
168658
+ if (reconciledSessions.has(sessionId) || reconciliationScheduledSessions.has(sessionId)) {
168659
+ return;
168660
+ }
168661
+ reconciliationScheduledSessions.add(sessionId);
168662
+ defer(() => {
168663
+ reconcileSessionIndex(db, sessionId, readMessages).catch((error48) => {
168664
+ reconciliationScheduledSessions.delete(sessionId);
168665
+ logIndexingError(sessionId, "reconciliation", error48);
168666
+ });
168667
+ });
168668
+ }
168669
+ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
168670
+ const key = `${sessionId}\x00${messageId}`;
168671
+ if (incrementalTimers.has(key) || pendingIncrementalKeys.has(key)) {
168672
+ return;
168673
+ }
168674
+ const timer = setTimeout(() => {
168675
+ incrementalTimers.delete(key);
168676
+ pendingIncrementalKeys.add(key);
168677
+ runWithSessionLock(sessionId, () => {
168678
+ const message = readSingleMessage(sessionId, messageId);
168679
+ if (!message) {
168680
+ return;
168681
+ }
168682
+ indexSingleMessage(db, sessionId, message);
168683
+ }).catch((error48) => {
168684
+ logIndexingError(sessionId, `incremental index for ${messageId}`, error48);
168685
+ }).finally(() => {
168686
+ pendingIncrementalKeys.delete(key);
168687
+ });
168688
+ }, INCREMENTAL_DEBOUNCE_MS);
168689
+ incrementalTimers.set(key, timer);
168690
+ }
168691
+ function scheduleClearAndReindex(db, sessionId, readMessages) {
168692
+ reconciledSessions.delete(sessionId);
168693
+ reconciliationScheduledSessions.delete(sessionId);
168694
+ defer(() => {
168695
+ runWithSessionLock(sessionId, () => {
168696
+ clearIndexedMessages(db, sessionId);
168697
+ const messages = readMessages(sessionId);
168698
+ indexMessagesAfterOrdinal(db, sessionId, messages, 0, messages.length);
168699
+ reconciledSessions.add(sessionId);
168700
+ }).catch((error48) => {
168701
+ logIndexingError(sessionId, "clear and reindex", error48);
168702
+ });
168703
+ });
168704
+ }
168705
+ function clearSessionTracking(sessionId) {
168706
+ reconciledSessions.delete(sessionId);
168707
+ reconciliationScheduledSessions.delete(sessionId);
168708
+ sessionLocks.delete(sessionId);
168709
+ const prefix = `${sessionId}\x00`;
168710
+ for (const [key, timer] of incrementalTimers) {
168711
+ if (key.startsWith(prefix)) {
168712
+ clearTimeout(timer);
168713
+ incrementalTimers.delete(key);
168714
+ }
168715
+ }
168716
+ for (const key of pendingIncrementalKeys) {
168717
+ if (key.startsWith(prefix)) {
168718
+ pendingIncrementalKeys.delete(key);
168719
+ }
168720
+ }
168721
+ }
168722
+
168723
+ // src/features/magic-context/overflow-detection.ts
168724
+ var OVERFLOW_PATTERNS = [
168725
+ /prompt is too long/i,
168726
+ /input is too long for requested model/i,
168727
+ /exceeds the context window/i,
168728
+ /input token count.*exceeds the maximum/i,
168729
+ /maximum prompt length is \d+/i,
168730
+ /reduce the length of the messages/i,
168731
+ /maximum context length is \d+ tokens/i,
168732
+ /exceeds the limit of \d+/i,
168733
+ /exceeds the available context size/i,
168734
+ /greater than the context length/i,
168735
+ /context window exceeds limit/i,
168736
+ /exceeded model token limit/i,
168737
+ /context[_ ]length[_ ]exceeded/i,
167336
168738
  /request entity too large/i,
167337
168739
  /context length is only \d+ tokens/i,
167338
168740
  /input length.*exceeds.*context length/i,
@@ -167488,6 +168890,24 @@ function getMessageUpdatedAssistantInfo(properties) {
167488
168890
  error: info.error !== undefined ? info.error : undefined
167489
168891
  };
167490
168892
  }
168893
+ function getMessageUpdatedInfo(properties) {
168894
+ const eventProps = getSessionProperties(properties);
168895
+ if (!eventProps || !isRecord(eventProps.info)) {
168896
+ return null;
168897
+ }
168898
+ const info = eventProps.info;
168899
+ if (typeof info.role !== "string" || typeof info.sessionID !== "string") {
168900
+ return null;
168901
+ }
168902
+ const time3 = isRecord(info.time) ? info.time : undefined;
168903
+ return {
168904
+ role: info.role,
168905
+ sessionID: info.sessionID,
168906
+ messageID: typeof info.id === "string" ? info.id : undefined,
168907
+ finish: typeof info.finish === "string" ? info.finish : undefined,
168908
+ completedAt: typeof time3?.completed === "number" ? time3.completed : undefined
168909
+ };
168910
+ }
167491
168911
  function getSessionErrorInfo(properties) {
167492
168912
  if (!isRecord(properties))
167493
168913
  return null;
@@ -167511,6 +168931,7 @@ function getMessageRemovedInfo(properties) {
167511
168931
 
167512
168932
  // src/hooks/magic-context/event-handler.ts
167513
168933
  init_note_nudger();
168934
+ await init_read_session_chunk();
167514
168935
 
167515
168936
  // src/hooks/magic-context/transform.ts
167516
168937
  init_compartment_storage();
@@ -167786,23 +169207,11 @@ function readUint32BE(b, offset) {
167786
169207
  // src/hooks/magic-context/transform.ts
167787
169208
  init_note_nudger();
167788
169209
  init_read_session_formatting();
169210
+ init_send_session_notification();
167789
169211
  await __promiseAll([
167790
169212
  init_inject_compartments(),
167791
169213
  init_read_session_chunk()
167792
169214
  ]);
167793
-
167794
- // src/hooks/magic-context/reasoning-capability.ts
167795
- init_models_dev_cache();
167796
- function modelRequiresInterleavedReasoning(model) {
167797
- if (!model?.providerID || !model?.modelID) {
167798
- return false;
167799
- }
167800
- const field = getModelsDevInterleavedField(model.providerID, model.modelID);
167801
- return typeof field === "string" && field.length > 0;
167802
- }
167803
-
167804
- // src/hooks/magic-context/transform.ts
167805
- init_send_session_notification();
167806
169215
  // src/hooks/magic-context/sentinel.ts
167807
169216
  function makeSentinel(originalPart) {
167808
169217
  const sentinel = {
@@ -167979,9 +169388,7 @@ function stripDroppedPlaceholderMessages(messages) {
167979
169388
  }
167980
169389
  return { stripped, sentineledIds };
167981
169390
  }
167982
- function replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedWatermark, skipTypedReasoningCleanup = false) {
167983
- if (skipTypedReasoningCleanup)
167984
- return 0;
169391
+ function replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedWatermark) {
167985
169392
  if (persistedWatermark <= 0)
167986
169393
  return 0;
167987
169394
  let cleared = 0;
@@ -168027,9 +169434,7 @@ function replayStrippedInlineThinking(messages, messageTagNumbers, persistedWate
168027
169434
  }
168028
169435
  return stripped;
168029
169436
  }
168030
- function clearOldReasoning(messages, reasoningByMessage, messageTagNumbers, clearReasoningAge, skipTypedReasoningCleanup = false) {
168031
- if (skipTypedReasoningCleanup)
168032
- return 0;
169437
+ function clearOldReasoning(messages, reasoningByMessage, messageTagNumbers, clearReasoningAge) {
168033
169438
  const maxTag = findMaxTag(messageTagNumbers);
168034
169439
  if (maxTag === 0)
168035
169440
  return 0;
@@ -168064,9 +169469,7 @@ function findMaxTag(messageTagNumbers) {
168064
169469
  return max;
168065
169470
  }
168066
169471
  var CLEARED_REASONING_TYPES = new Set(["thinking", "reasoning"]);
168067
- function stripClearedReasoning(messages, skipTypedReasoningCleanup = false) {
168068
- if (skipTypedReasoningCleanup)
168069
- return 0;
169472
+ function stripClearedReasoning(messages) {
168070
169473
  let stripped = 0;
168071
169474
  for (const message of messages) {
168072
169475
  if (message.info.role !== "assistant")
@@ -168588,374 +169991,189 @@ function isStructuralNoisePart(part) {
168588
169991
  if (!isRecord(part) || typeof part.type !== "string") {
168589
169992
  return false;
168590
169993
  }
168591
- if (!STRUCTURAL_PART_TYPES.has(part.type)) {
168592
- return false;
168593
- }
168594
- if (part.type === "reasoning" && typeof part.text === "string" && part.text !== "[cleared]") {
168595
- return false;
168596
- }
168597
- return true;
168598
- }
168599
- function stripStructuralNoise(messages) {
168600
- let strippedParts = 0;
168601
- for (const message of messages) {
168602
- if (!Array.isArray(message.parts)) {
168603
- continue;
168604
- }
168605
- for (let i = 0;i < message.parts.length; i++) {
168606
- const part = message.parts[i];
168607
- if (isSentinel(part))
168608
- continue;
168609
- if (!isStructuralNoisePart(part))
168610
- continue;
168611
- message.parts[i] = makeSentinel(part);
168612
- strippedParts++;
168613
- }
168614
- }
168615
- return strippedParts;
168616
- }
168617
- // src/hooks/magic-context/tag-messages.ts
168618
- await init_storage();
168619
- // src/hooks/magic-context/drop-stale-reduce-calls.ts
168620
- var STALE_TOOL_NAMES = new Set(["ctx_reduce"]);
168621
- function isReduceToolPart(part) {
168622
- if (!isRecord(part))
168623
- return false;
168624
- if (part.type === "tool" && typeof part.tool === "string" && STALE_TOOL_NAMES.has(part.tool))
168625
- return true;
168626
- if (part.type === "tool-invocation" && typeof part.toolName === "string" && STALE_TOOL_NAMES.has(part.toolName))
168627
- return true;
168628
- if (part.type === "tool_use" && typeof part.name === "string" && STALE_TOOL_NAMES.has(part.name))
168629
- return true;
168630
- return false;
168631
- }
168632
- function hasAnyMeaningfulPart(parts) {
168633
- for (const part of parts) {
168634
- if (!isRecord(part))
168635
- continue;
168636
- if (part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0)
168637
- return true;
168638
- if (part.type === "thinking" || part.type === "reasoning" || part.type === "redacted_thinking")
168639
- continue;
168640
- if (part.type === "meta" || part.type === "step-start" || part.type === "step-finish")
168641
- continue;
168642
- if (part.type !== "tool" || !isReduceToolPart(part))
168643
- return true;
168644
- }
168645
- return false;
168646
- }
168647
- function dropStaleReduceCalls(messages, protectedCount = 0) {
168648
- let didDrop = false;
168649
- const protectedStart = messages.length - protectedCount;
168650
- for (let i = 0;i < messages.length; i++) {
168651
- if (i >= protectedStart)
168652
- break;
168653
- const message = messages[i];
168654
- let touched = false;
168655
- for (let j = 0;j < message.parts.length; j++) {
168656
- const part = message.parts[j];
168657
- if (isSentinel(part))
168658
- continue;
168659
- if (isReduceToolPart(part)) {
168660
- message.parts[j] = makeSentinel(part);
168661
- touched = true;
168662
- }
168663
- }
168664
- if (touched) {
168665
- didDrop = true;
168666
- if (!hasAnyMeaningfulPart(message.parts)) {
168667
- message.parts.length = 0;
168668
- message.parts.push(makeSentinel(undefined));
168669
- }
168670
- }
168671
- }
168672
- return didDrop;
168673
- }
168674
-
168675
- // src/hooks/magic-context/tag-messages.ts
168676
- init_tag_content_primitives();
168677
-
168678
- // src/hooks/magic-context/tag-id-fallback.ts
168679
- await init_storage();
168680
- function parseScopedContentId(contentId) {
168681
- const match = /^(.*):(p|file)(\d+)$/.exec(contentId);
168682
- if (!match)
168683
- return null;
168684
- return {
168685
- messageId: match[1],
168686
- type: match[2] === "file" ? "file" : "message",
168687
- partIndex: Number.parseInt(match[3], 10)
168688
- };
168689
- }
168690
- function createScopedAssignments(assignments) {
168691
- const scoped = new Map;
168692
- for (const [contentId, tagNumber] of assignments) {
168693
- const parsed = parseScopedContentId(contentId);
168694
- if (!parsed)
168695
- continue;
168696
- const entry = scoped.get(parsed.messageId) ?? { message: [], file: [] };
168697
- entry[parsed.type].push({ tagNumber, contentId, partIndex: parsed.partIndex });
168698
- scoped.set(parsed.messageId, entry);
168699
- }
168700
- for (const entry of scoped.values()) {
168701
- entry.message.sort((left, right) => left.partIndex - right.partIndex);
168702
- entry.file.sort((left, right) => left.partIndex - right.partIndex);
168703
- }
168704
- return scoped;
168705
- }
168706
- function createExistingTagResolver(sessionId, tagger, db) {
168707
- const assignments = tagger.getAssignments(sessionId);
168708
- let cachedAssignmentSize = -1;
168709
- let cachedScopedAssignments = null;
168710
- const usedTagNumbers = new Set;
168711
- function getScopedAssignments() {
168712
- if (!cachedScopedAssignments || cachedAssignmentSize !== assignments.size) {
168713
- cachedScopedAssignments = createScopedAssignments(assignments);
168714
- cachedAssignmentSize = assignments.size;
168715
- }
168716
- return cachedScopedAssignments;
168717
- }
168718
- return {
168719
- resolve(messageId, type, currentContentId, ordinal) {
168720
- const exactTagId = assignments.get(currentContentId);
168721
- if (exactTagId !== undefined) {
168722
- usedTagNumbers.add(exactTagId);
168723
- return exactTagId;
168724
- }
168725
- const fallback = getScopedAssignments().get(messageId)?.[type][ordinal];
168726
- if (!fallback || usedTagNumbers.has(fallback.tagNumber)) {
168727
- return;
168728
- }
168729
- updateTagMessageId(db, sessionId, fallback.tagNumber, currentContentId);
168730
- tagger.bindTag(sessionId, currentContentId, fallback.tagNumber);
168731
- usedTagNumbers.add(fallback.tagNumber);
168732
- return fallback.tagNumber;
168733
- }
168734
- };
168735
- }
168736
-
168737
- // src/hooks/magic-context/tag-messages.ts
168738
- init_tag_part_guards();
168739
-
168740
- // src/hooks/magic-context/tool-drop-target.ts
168741
- var DROP_PREFIX = "[dropped";
168742
- var IGNORE_PART_TYPES = new Set([
168743
- "thinking",
168744
- "reasoning",
168745
- "redacted_thinking",
168746
- "meta",
168747
- "step-start",
168748
- "step-finish"
168749
- ]);
168750
- function isToolCallId(value) {
168751
- return typeof value === "string" && value.length > 0;
168752
- }
168753
- function getToolContent(part) {
168754
- if (!isRecord(part))
168755
- return;
168756
- if (part.type === "tool" && isRecord(part.state)) {
168757
- return typeof part.state.output === "string" ? part.state.output : undefined;
168758
- }
168759
- if (part.type === "tool_result") {
168760
- return typeof part.content === "string" ? part.content : undefined;
168761
- }
168762
- return;
168763
- }
168764
- function setToolContent(part, content) {
168765
- if (!isRecord(part))
168766
- return;
168767
- if (part.type === "tool" && isRecord(part.state)) {
168768
- part.state.output = content;
168769
- return;
168770
- }
168771
- if (part.type === "tool_result") {
168772
- part.content = content;
168773
- }
168774
- }
168775
- function truncateToolPart(part) {
168776
- if (!isRecord(part))
168777
- return;
168778
- if (part.type === "tool" && isRecord(part.state)) {
168779
- const state = part.state;
168780
- state.output = "[truncated]";
168781
- if (isRecord(state.input)) {
168782
- const inputSize = estimateInputSize(state.input);
168783
- if (inputSize > 500) {
168784
- truncateInputValues(state.input);
168785
- }
168786
- }
168787
- return;
168788
- }
168789
- if (part.type === "tool_result") {
168790
- part.content = "[truncated]";
168791
- return;
168792
- }
168793
- if (part.type === "tool-invocation" && isRecord(part.args)) {
168794
- const inputSize = estimateInputSize(part.args);
168795
- if (inputSize > 500) {
168796
- truncateInputValues(part.args);
168797
- }
168798
- return;
168799
- }
168800
- if (part.type === "tool_use" && isRecord(part.input)) {
168801
- const inputSize = estimateInputSize(part.input);
168802
- if (inputSize > 500) {
168803
- truncateInputValues(part.input);
168804
- }
168805
- }
168806
- }
168807
- function estimateInputSize(input) {
168808
- try {
168809
- return JSON.stringify(input).length;
168810
- } catch {
168811
- return 0;
168812
- }
168813
- }
168814
- var TRUNCATION_SENTINEL = "...[truncated]";
168815
- function safeSlice(str, maxLen) {
168816
- if (str.length <= maxLen)
168817
- return str;
168818
- const lastCharCode = str.charCodeAt(maxLen - 1);
168819
- if (lastCharCode >= 55296 && lastCharCode <= 56319) {
168820
- return str.slice(0, maxLen - 1);
168821
- }
168822
- return str.slice(0, maxLen);
168823
- }
168824
- function truncateInputValues(input) {
168825
- for (const key of Object.keys(input)) {
168826
- const value = input[key];
168827
- if (typeof value === "string") {
168828
- if (value.endsWith(TRUNCATION_SENTINEL) || value === "[object]" || /^\[\d+ items\]$/.test(value))
168829
- continue;
168830
- input[key] = value.length > 5 ? `${safeSlice(value, 5)}${TRUNCATION_SENTINEL}` : value;
168831
- } else if (Array.isArray(value)) {
168832
- input[key] = `[${value.length} items]`;
168833
- } else if (value !== null && typeof value === "object") {
168834
- input[key] = "[object]";
168835
- }
168836
- }
168837
- }
168838
- function hasMeaningfulPart(part) {
168839
- if (!isRecord(part))
168840
- return false;
168841
- const type = part.type;
168842
- if (type === "text") {
168843
- return typeof part.text === "string" && part.text.trim().length > 0;
168844
- }
168845
- if (typeof type !== "string")
168846
- return false;
168847
- if (IGNORE_PART_TYPES.has(type))
168848
- return false;
168849
- return true;
168850
- }
168851
- function clearThinkingParts(thinkingParts) {
168852
- for (const part of thinkingParts) {
168853
- if (part.thinking !== undefined)
168854
- part.thinking = "[cleared]";
168855
- if (part.text !== undefined)
168856
- part.text = "[cleared]";
168857
- }
168858
- }
168859
- function extractToolCallObservation(part) {
168860
- if (!isRecord(part))
168861
- return null;
168862
- if (part.type === "tool" && isToolCallId(part.callID)) {
168863
- return { callId: part.callID, kind: "result" };
168864
- }
168865
- if (part.type === "tool-invocation" && isToolCallId(part.callID)) {
168866
- return { callId: part.callID, kind: "invocation" };
168867
- }
168868
- if (part.type === "tool_use" && isToolCallId(part.id)) {
168869
- return { callId: part.id, kind: "invocation" };
169994
+ if (!STRUCTURAL_PART_TYPES.has(part.type)) {
169995
+ return false;
168870
169996
  }
168871
- if (part.type === "tool_result" && isToolCallId(part.tool_use_id)) {
168872
- return { callId: part.tool_use_id, kind: "result" };
169997
+ if (part.type === "reasoning" && typeof part.text === "string" && part.text !== "[cleared]") {
169998
+ return false;
168873
169999
  }
168874
- return null;
168875
- }
168876
- function isDropContent(content) {
168877
- return content.startsWith(DROP_PREFIX);
170000
+ return true;
168878
170001
  }
168879
-
168880
- class ToolMutationBatch {
168881
- partsToRemove = new Set;
168882
- affectedMessages = new Set;
168883
- messages;
168884
- constructor(messages) {
168885
- this.messages = messages;
170002
+ function stripStructuralNoise(messages) {
170003
+ let strippedParts = 0;
170004
+ for (const message of messages) {
170005
+ if (!Array.isArray(message.parts)) {
170006
+ continue;
170007
+ }
170008
+ for (let i = 0;i < message.parts.length; i++) {
170009
+ const part = message.parts[i];
170010
+ if (isSentinel(part))
170011
+ continue;
170012
+ if (!isStructuralNoisePart(part))
170013
+ continue;
170014
+ message.parts[i] = makeSentinel(part);
170015
+ strippedParts++;
170016
+ }
168886
170017
  }
168887
- markForRemoval(occurrence) {
168888
- this.partsToRemove.add(occurrence.part);
168889
- this.affectedMessages.add(occurrence.message);
170018
+ return strippedParts;
170019
+ }
170020
+ // src/hooks/magic-context/tag-messages.ts
170021
+ init_storage_tags();
170022
+ await init_storage();
170023
+ // src/hooks/magic-context/drop-stale-reduce-calls.ts
170024
+ var STALE_TOOL_NAMES = new Set(["ctx_reduce"]);
170025
+ function isReduceToolPart(part) {
170026
+ if (!isRecord(part))
170027
+ return false;
170028
+ if (part.type === "tool" && typeof part.tool === "string" && STALE_TOOL_NAMES.has(part.tool))
170029
+ return true;
170030
+ if (part.type === "tool-invocation" && typeof part.toolName === "string" && STALE_TOOL_NAMES.has(part.toolName))
170031
+ return true;
170032
+ if (part.type === "tool_use" && typeof part.name === "string" && STALE_TOOL_NAMES.has(part.name))
170033
+ return true;
170034
+ return false;
170035
+ }
170036
+ function hasAnyMeaningfulPart(parts) {
170037
+ for (const part of parts) {
170038
+ if (!isRecord(part))
170039
+ continue;
170040
+ if (part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0)
170041
+ return true;
170042
+ if (part.type === "thinking" || part.type === "reasoning" || part.type === "redacted_thinking")
170043
+ continue;
170044
+ if (part.type === "meta" || part.type === "step-start" || part.type === "step-finish")
170045
+ continue;
170046
+ if (part.type !== "tool" || !isReduceToolPart(part))
170047
+ return true;
168890
170048
  }
168891
- finalize() {
168892
- if (this.partsToRemove.size === 0)
168893
- return;
168894
- for (const message of this.affectedMessages) {
168895
- message.parts = message.parts.filter((p) => !this.partsToRemove.has(p));
170049
+ return false;
170050
+ }
170051
+ function dropStaleReduceCalls(messages, protectedCount = 0) {
170052
+ let didDrop = false;
170053
+ const protectedStart = messages.length - protectedCount;
170054
+ for (let i = 0;i < messages.length; i++) {
170055
+ if (i >= protectedStart)
170056
+ break;
170057
+ const message = messages[i];
170058
+ let touched = false;
170059
+ for (let j = 0;j < message.parts.length; j++) {
170060
+ const part = message.parts[j];
170061
+ if (isSentinel(part))
170062
+ continue;
170063
+ if (isReduceToolPart(part)) {
170064
+ message.parts[j] = makeSentinel(part);
170065
+ touched = true;
170066
+ }
168896
170067
  }
168897
- for (let i = this.messages.length - 1;i >= 0; i -= 1) {
168898
- if (!this.messages[i].parts.some(hasMeaningfulPart)) {
168899
- this.messages.splice(i, 1);
170068
+ if (touched) {
170069
+ didDrop = true;
170070
+ if (!hasAnyMeaningfulPart(message.parts)) {
170071
+ message.parts.length = 0;
170072
+ message.parts.push(makeSentinel(undefined));
168900
170073
  }
168901
170074
  }
168902
- this.partsToRemove.clear();
168903
- this.affectedMessages.clear();
168904
170075
  }
170076
+ return didDrop;
168905
170077
  }
168906
- function createToolDropTarget(callId, thinkingParts, index, batch) {
168907
- const drop = () => {
168908
- const entry = index.get(callId);
168909
- if (!entry || entry.occurrences.length === 0)
168910
- return "absent";
168911
- if (!entry.hasResult)
168912
- return "incomplete";
168913
- for (const occurrence of entry.occurrences) {
168914
- batch.markForRemoval(occurrence);
168915
- }
168916
- clearThinkingParts(thinkingParts);
168917
- index.delete(callId);
168918
- return "removed";
170078
+
170079
+ // src/hooks/magic-context/tag-messages.ts
170080
+ init_tag_content_primitives();
170081
+ await init_read_session_db();
170082
+
170083
+ // src/hooks/magic-context/tag-id-fallback.ts
170084
+ await init_storage();
170085
+ function parseScopedContentId(contentId) {
170086
+ const match = /^(.*):(p|file)(\d+)$/.exec(contentId);
170087
+ if (!match)
170088
+ return null;
170089
+ return {
170090
+ messageId: match[1],
170091
+ type: match[2] === "file" ? "file" : "message",
170092
+ partIndex: Number.parseInt(match[3], 10)
168919
170093
  };
168920
- const truncate = () => {
168921
- const entry = index.get(callId);
168922
- if (!entry || entry.occurrences.length === 0)
168923
- return "absent";
168924
- if (!entry.hasResult)
168925
- return "incomplete";
168926
- for (const occurrence of entry.occurrences) {
168927
- truncateToolPart(occurrence.part);
170094
+ }
170095
+ function createScopedAssignments(assignments) {
170096
+ const scoped = new Map;
170097
+ for (const [contentId, tagNumber] of assignments) {
170098
+ const parsed = parseScopedContentId(contentId);
170099
+ if (!parsed)
170100
+ continue;
170101
+ const entry = scoped.get(parsed.messageId) ?? { message: [], file: [] };
170102
+ entry[parsed.type].push({ tagNumber, contentId, partIndex: parsed.partIndex });
170103
+ scoped.set(parsed.messageId, entry);
170104
+ }
170105
+ for (const entry of scoped.values()) {
170106
+ entry.message.sort((left, right) => left.partIndex - right.partIndex);
170107
+ entry.file.sort((left, right) => left.partIndex - right.partIndex);
170108
+ }
170109
+ return scoped;
170110
+ }
170111
+ function createExistingTagResolver(sessionId, tagger, db) {
170112
+ const assignments = tagger.getAssignments(sessionId);
170113
+ let cachedAssignmentSize = -1;
170114
+ let cachedScopedAssignments = null;
170115
+ const usedTagNumbers = new Set;
170116
+ function getScopedAssignments() {
170117
+ if (!cachedScopedAssignments || cachedAssignmentSize !== assignments.size) {
170118
+ cachedScopedAssignments = createScopedAssignments(assignments);
170119
+ cachedAssignmentSize = assignments.size;
168928
170120
  }
168929
- clearThinkingParts(thinkingParts);
168930
- return "truncated";
168931
- };
170121
+ return cachedScopedAssignments;
170122
+ }
168932
170123
  return {
168933
- setContent: (content) => {
168934
- if (isDropContent(content)) {
168935
- drop();
168936
- return true;
170124
+ resolve(messageId, type, currentContentId, ordinal) {
170125
+ const exactTagId = assignments.get(currentContentId);
170126
+ if (exactTagId !== undefined) {
170127
+ usedTagNumbers.add(exactTagId);
170128
+ return exactTagId;
168937
170129
  }
168938
- const entry = index.get(callId);
168939
- if (!entry)
168940
- return false;
168941
- let changed = false;
168942
- for (const occurrence of entry.occurrences) {
168943
- if (occurrence.kind !== "result")
168944
- continue;
168945
- const prevContent = getToolContent(occurrence.part);
168946
- if (prevContent !== content) {
168947
- setToolContent(occurrence.part, content);
168948
- changed = true;
168949
- }
170130
+ const fallback = getScopedAssignments().get(messageId)?.[type][ordinal];
170131
+ if (!fallback || usedTagNumbers.has(fallback.tagNumber)) {
170132
+ return;
168950
170133
  }
168951
- return changed;
168952
- },
168953
- drop,
168954
- truncate
170134
+ updateTagMessageId(db, sessionId, fallback.tagNumber, currentContentId);
170135
+ tagger.bindTag(sessionId, currentContentId, fallback.tagNumber);
170136
+ usedTagNumbers.add(fallback.tagNumber);
170137
+ return fallback.tagNumber;
170138
+ }
168955
170139
  };
168956
170140
  }
168957
170141
 
168958
170142
  // src/hooks/magic-context/tag-messages.ts
170143
+ init_tag_part_guards();
170144
+ init_tool_drop_target();
170145
+ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
170146
+ const messageId = typeof message.info.id === "string" ? message.info.id : "";
170147
+ if (obs.kind === "invocation") {
170148
+ if (messageId) {
170149
+ const queue4 = unpaired.get(obs.callId) ?? [];
170150
+ queue4.push(messageId);
170151
+ unpaired.set(obs.callId, queue4);
170152
+ return messageId;
170153
+ }
170154
+ return obs.callId;
170155
+ }
170156
+ const queue3 = unpaired.get(obs.callId);
170157
+ if (queue3 && queue3.length > 0) {
170158
+ const popped = queue3.shift();
170159
+ if (queue3.length === 0)
170160
+ unpaired.delete(obs.callId);
170161
+ if (popped !== undefined)
170162
+ return popped;
170163
+ }
170164
+ if (messageId) {
170165
+ const candidates = getCandidateToolOwners(db, sessionId, obs.callId);
170166
+ if (candidates.length > 0) {
170167
+ const ids = [...candidates, messageId];
170168
+ const times = getMessageTimesFromOpenCodeDb(sessionId, ids);
170169
+ const persisted = pickNearestPriorOwner(candidates, messageId, times);
170170
+ if (persisted !== null)
170171
+ return persisted;
170172
+ }
170173
+ return messageId;
170174
+ }
170175
+ return obs.callId;
170176
+ }
168959
170177
  function collectRelevantSourceTagIds(messages, assignments) {
168960
170178
  const currentMessageIds = new Set(messages.flatMap((message) => typeof message.info.id === "string" ? [message.info.id] : []));
168961
170179
  const relevantTagIds = new Set;
@@ -169006,6 +170224,8 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
169006
170224
  const toolTagByCallId = new Map;
169007
170225
  const toolThinkingByCallId = new Map;
169008
170226
  const toolCallIndex = new Map;
170227
+ const unpairedInvocations = new Map;
170228
+ const ownerByPartKey = new Map;
169009
170229
  const batch = new ToolMutationBatch(messages);
169010
170230
  const assignments = tagger.getAssignments(sessionId);
169011
170231
  const resolver = createExistingTagResolver(sessionId, tagger, db);
@@ -169036,22 +170256,37 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
169036
170256
  }
169037
170257
  const toolObservation = extractToolCallObservation(part);
169038
170258
  if (toolObservation) {
169039
- const entry = toolCallIndex.get(toolObservation.callId) ?? {
170259
+ const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations);
170260
+ const compositeKey = makeToolCompositeKey(ownerMsgId, toolObservation.callId);
170261
+ const entry = toolCallIndex.get(compositeKey) ?? {
169040
170262
  occurrences: [],
169041
170263
  hasResult: false
169042
170264
  };
169043
170265
  entry.occurrences.push({ message, part, kind: toolObservation.kind });
169044
170266
  if (toolObservation.kind === "result")
169045
170267
  entry.hasResult = true;
169046
- toolCallIndex.set(toolObservation.callId, entry);
169047
- const existingTagId = tagger.getTag(sessionId, toolObservation.callId);
170268
+ toolCallIndex.set(compositeKey, entry);
170269
+ let existingTagId = tagger.getToolTag(sessionId, toolObservation.callId, ownerMsgId);
170270
+ if (existingTagId === undefined) {
170271
+ const orphan = getNullOwnerToolTag(db, sessionId, toolObservation.callId);
170272
+ if (orphan !== null) {
170273
+ const claimed = adoptNullOwnerToolTag(db, orphan.id, ownerMsgId);
170274
+ if (claimed) {
170275
+ tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, orphan.tagNumber);
170276
+ existingTagId = orphan.tagNumber;
170277
+ } else {
170278
+ existingTagId = tagger.getToolTag(sessionId, toolObservation.callId, ownerMsgId);
170279
+ }
170280
+ }
170281
+ }
169048
170282
  if (existingTagId !== undefined) {
169049
- toolTagByCallId.set(toolObservation.callId, existingTagId);
170283
+ toolTagByCallId.set(compositeKey, existingTagId);
169050
170284
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, existingTagId));
169051
- if (message.info.role === "tool" && precedingThinkingParts.length > 0 && !toolThinkingByCallId.has(toolObservation.callId)) {
169052
- toolThinkingByCallId.set(toolObservation.callId, precedingThinkingParts);
170285
+ if (message.info.role === "tool" && precedingThinkingParts.length > 0 && !toolThinkingByCallId.has(compositeKey)) {
170286
+ toolThinkingByCallId.set(compositeKey, precedingThinkingParts);
169053
170287
  }
169054
170288
  }
170289
+ ownerByPartKey.set(part, { ownerMsgId, callId: toolObservation.callId });
169055
170290
  }
169056
170291
  if (messageId && isTextPart(part)) {
169057
170292
  const textPart = part;
@@ -169097,14 +170332,17 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
169097
170332
  const thinkingParts = precedingThinkingParts;
169098
170333
  const reasoningBytes = getReasoningByteSize(thinkingParts);
169099
170334
  const { toolName, inputByteSize } = extractToolTagMetadata(toolPart);
169100
- const tagId = tagger.assignTag(sessionId, toolPart.callID, "tool", byteSize(toolPart.state.output), db, reasoningBytes, toolName, inputByteSize);
170335
+ const memo = ownerByPartKey.get(part);
170336
+ const ownerMsgId = memo?.ownerMsgId ?? messageId ?? toolPart.callID;
170337
+ const compositeKey = makeToolCompositeKey(ownerMsgId, toolPart.callID);
170338
+ const tagId = tagger.assignToolTag(sessionId, toolPart.callID, ownerMsgId, byteSize(toolPart.state.output), db, reasoningBytes, toolName, inputByteSize);
169101
170339
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
169102
170340
  if (!skipPrefixInjection) {
169103
170341
  toolPart.state.output = prependTag(tagId, toolPart.state.output);
169104
170342
  }
169105
- toolTagByCallId.set(toolPart.callID, tagId);
169106
- if (thinkingParts.length > 0 && !toolThinkingByCallId.has(toolPart.callID)) {
169107
- toolThinkingByCallId.set(toolPart.callID, thinkingParts);
170343
+ toolTagByCallId.set(compositeKey, tagId);
170344
+ if (thinkingParts.length > 0 && !toolThinkingByCallId.has(compositeKey)) {
170345
+ toolThinkingByCallId.set(compositeKey, thinkingParts);
169108
170346
  }
169109
170347
  }
169110
170348
  if (messageId && isFilePart(part)) {
@@ -169152,9 +170390,9 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
169152
170390
  }
169153
170391
  }
169154
170392
  }
169155
- for (const [callId, tagId] of toolTagByCallId) {
169156
- const thinkingParts = toolThinkingByCallId.get(callId) ?? [];
169157
- targets.set(tagId, createToolDropTarget(callId, thinkingParts, toolCallIndex, batch));
170393
+ for (const [compositeKey, tagId] of toolTagByCallId) {
170394
+ const thinkingParts = toolThinkingByCallId.get(compositeKey) ?? [];
170395
+ targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch));
169158
170396
  }
169159
170397
  const hasRecentReduceCall = lastReduceMessageIndex >= 0 && messages.length - lastReduceMessageIndex <= RECENT_REDUCE_LOOKBACK;
169160
170398
  return {
@@ -169315,11 +170553,9 @@ function applyContextNudge(messages, nudge, nudgePlacements, sessionId) {
169315
170553
 
169316
170554
  // src/features/magic-context/search.ts
169317
170555
  init_logger();
169318
- await init_read_session_chunk();
169319
170556
  init_memory();
169320
170557
  init_embedding();
169321
170558
  init_storage_memory_fts();
169322
- await init_message_index();
169323
170559
  var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
169324
170560
  var FTS_SEMANTIC_CANDIDATE_LIMIT = 50;
169325
170561
  var SEMANTIC_WEIGHT = 0.7;
@@ -169369,11 +170605,7 @@ function getMessageOrdinal(value) {
169369
170605
  }
169370
170606
  async function getSemanticScores(args) {
169371
170607
  const semanticScores = new Map;
169372
- if (!args.embeddingEnabled || !args.isEmbeddingRuntimeEnabled() || args.memories.length === 0) {
169373
- return semanticScores;
169374
- }
169375
- const queryEmbedding = await args.embedQuery(args.query, args.signal);
169376
- if (!queryEmbedding) {
170608
+ if (!args.queryEmbedding || args.memories.length === 0) {
169377
170609
  return semanticScores;
169378
170610
  }
169379
170611
  const cachedEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
@@ -169387,7 +170619,7 @@ async function getSemanticScores(args) {
169387
170619
  if (!memoryEmbedding) {
169388
170620
  continue;
169389
170621
  }
169390
- semanticScores.set(memory.id, normalizeCosineScore(cosineSimilarity(queryEmbedding, memoryEmbedding)));
170622
+ semanticScores.set(memory.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding)));
169391
170623
  }
169392
170624
  return semanticScores;
169393
170625
  }
@@ -169483,12 +170715,8 @@ async function searchMemories(args) {
169483
170715
  const semanticScores = await getSemanticScores({
169484
170716
  db: args.db,
169485
170717
  projectPath: args.projectPath,
169486
- query: args.query,
169487
170718
  memories: semanticCandidates,
169488
- embeddingEnabled: args.embeddingEnabled,
169489
- embedQuery: args.embedQuery,
169490
- isEmbeddingRuntimeEnabled: args.isEmbeddingRuntimeEnabled,
169491
- signal: args.signal
170719
+ queryEmbedding: args.queryEmbedding
169492
170720
  });
169493
170721
  return mergeMemoryResults({
169494
170722
  memories,
@@ -169504,7 +170732,6 @@ function linearDecayScore(rank, total) {
169504
170732
  return Math.max(0, 1 - rank / total);
169505
170733
  }
169506
170734
  function searchMessages(args) {
169507
- ensureMessagesIndexed(args.db, args.sessionId, args.readMessages);
169508
170735
  const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
169509
170736
  if (sanitizedQuery.length === 0) {
169510
170737
  return [];
@@ -169575,20 +170802,12 @@ function toGitCommitResult(hit) {
169575
170802
  matchType: hit.matchType
169576
170803
  };
169577
170804
  }
169578
- async function searchGitCommitsAsync(args) {
170805
+ function searchGitCommits(args) {
169579
170806
  if (args.limit <= 0)
169580
170807
  return [];
169581
- let queryEmbedding = null;
169582
- if (args.embeddingEnabled && args.isEmbeddingRuntimeEnabled()) {
169583
- try {
169584
- queryEmbedding = await args.embedQuery(args.query, args.signal);
169585
- } catch (error48) {
169586
- log(`[search] git commit query embedding failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
169587
- }
169588
- }
169589
170808
  const hits = searchGitCommitsSync(args.db, args.projectPath, args.query, {
169590
170809
  limit: args.limit,
169591
- queryEmbedding
170810
+ queryEmbedding: args.queryEmbedding
169592
170811
  });
169593
170812
  return hits.map(toGitCommitResult);
169594
170813
  }
@@ -169619,37 +170838,37 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
169619
170838
  const runMemory = activeSources.has("memory") && (options.memoryEnabled ?? true);
169620
170839
  const runMessages = activeSources.has("message");
169621
170840
  const runGitCommits = activeSources.has("git_commit") && gitCommitsEnabled;
169622
- const [memoryResults, messageResults, gitCommitResults] = await Promise.all([
170841
+ const needsEmbedding = (runMemory || runGitCommits) && embeddingEnabled && isEmbeddingRuntimeEnabled();
170842
+ const queryEmbeddingPromise = needsEmbedding ? embedQuery(trimmedQuery, options.signal).catch((error48) => {
170843
+ log(`[search] query embedding failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
170844
+ return null;
170845
+ }) : Promise.resolve(null);
170846
+ await Promise.resolve();
170847
+ const messageResults = runMessages ? searchMessages({
170848
+ db,
170849
+ sessionId,
170850
+ query: trimmedQuery,
170851
+ limit: tierLimit,
170852
+ maxOrdinal: options.maxMessageOrdinal
170853
+ }) : [];
170854
+ const queryEmbedding = await queryEmbeddingPromise;
170855
+ const [memoryResults, gitCommitResults] = await Promise.all([
169623
170856
  runMemory ? searchMemories({
169624
170857
  db,
169625
170858
  projectPath,
169626
170859
  query: trimmedQuery,
169627
170860
  limit: tierLimit,
169628
170861
  memoryEnabled: true,
169629
- embeddingEnabled,
169630
- embedQuery,
169631
- isEmbeddingRuntimeEnabled,
169632
- visibleMemoryIds: options.visibleMemoryIds,
169633
- signal: options.signal
170862
+ queryEmbedding,
170863
+ visibleMemoryIds: options.visibleMemoryIds
169634
170864
  }) : Promise.resolve([]),
169635
- runMessages ? Promise.resolve(searchMessages({
169636
- db,
169637
- sessionId,
169638
- query: trimmedQuery,
169639
- limit: tierLimit,
169640
- readMessages: options.readMessages ?? readRawSessionMessages,
169641
- maxOrdinal: options.maxMessageOrdinal
169642
- })) : Promise.resolve([]),
169643
- runGitCommits ? searchGitCommitsAsync({
170865
+ runGitCommits ? Promise.resolve(searchGitCommits({
169644
170866
  db,
169645
170867
  projectPath,
169646
170868
  query: trimmedQuery,
169647
170869
  limit: tierLimit,
169648
- embeddingEnabled,
169649
- embedQuery,
169650
- isEmbeddingRuntimeEnabled,
169651
- signal: options.signal
169652
- }) : Promise.resolve([])
170870
+ queryEmbedding
170871
+ })) : Promise.resolve([])
169653
170872
  ]);
169654
170873
  const results = [...memoryResults, ...messageResults, ...gitCommitResults].sort(compareUnifiedResults).slice(0, limit);
169655
170874
  const countRetrievals = options.countRetrievals ?? true;
@@ -169787,8 +171006,31 @@ function collectUserPromptParts(message) {
169787
171006
  function hasStackedAugmentation(rawText) {
169788
171007
  return rawText.includes("<sidekick-augmentation>") || rawText.includes("<ctx-search-hint>") || rawText.includes("<ctx-search-auto>");
169789
171008
  }
171009
+ function stripNestedSystemReminders(text) {
171010
+ const OPEN = "<system-reminder>";
171011
+ const CLOSE = "</system-reminder>";
171012
+ let result = "";
171013
+ let depth = 0;
171014
+ let i = 0;
171015
+ while (i < text.length) {
171016
+ if (text.startsWith(OPEN, i)) {
171017
+ depth += 1;
171018
+ i += OPEN.length;
171019
+ } else if (text.startsWith(CLOSE, i)) {
171020
+ if (depth > 0)
171021
+ depth -= 1;
171022
+ i += CLOSE.length;
171023
+ } else if (depth === 0) {
171024
+ result += text[i];
171025
+ i += 1;
171026
+ } else {
171027
+ i += 1;
171028
+ }
171029
+ }
171030
+ return result;
171031
+ }
169790
171032
  function extractUserPromptText(message) {
169791
- return collectUserPromptParts(message).replace(/§\d+§\s*/g, "").replace(/<!--\s*\+[\d\s.hmdw]+\s*-->/g, "").replace(/<!--\s*OMO_INTERNAL_INITIATOR[\s\S]*?-->/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").replace(/<ctx-search-hint>[\s\S]*?<\/ctx-search-hint>/g, "").replace(/<ctx-search-auto>[\s\S]*?<\/ctx-search-auto>/g, "").replace(/<instruction[^>]*>[\s\S]*?<\/instruction>/g, "").replace(/<sidekick-augmentation>[\s\S]*?<\/sidekick-augmentation>/g, "").replace(/[ \t]+\n/g, `
171033
+ return stripNestedSystemReminders(collectUserPromptParts(message)).replace(/§\d+§\s*/g, "").replace(/<!--\s*\+[\d\s.hmdw]+\s*-->/g, "").replace(/<!--\s*OMO_INTERNAL_INITIATOR[\s\S]*?-->/g, "").replace(/<!--\s*ALFONSO_INTERNAL_INITIATOR[\s\S]*?-->/g, "").replace(/<ctx-search-hint>[\s\S]*?<\/ctx-search-hint>/g, "").replace(/<ctx-search-auto>[\s\S]*?<\/ctx-search-auto>/g, "").replace(/<instruction[^>]*>[\s\S]*?<\/instruction>/g, "").replace(/<sidekick-augmentation>[\s\S]*?<\/sidekick-augmentation>/g, "").replace(/[ \t]+\n/g, `
169792
171034
  `).replace(/\n{3,}/g, `
169793
171035
 
169794
171036
  `).trim();
@@ -169944,8 +171186,8 @@ var DEDUP_SAFE_TOOLS = new Set([
169944
171186
  "mcp_lsp_prepare_rename"
169945
171187
  ]);
169946
171188
  function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config2, preloadedTags) {
169947
- const tags = preloadedTags ?? getTagsBySession(db, sessionId);
169948
- const maxTag = tags.reduce((max, t) => Math.max(max, t.tagNumber), 0);
171189
+ const tags = preloadedTags ?? getActiveTagsBySession(db, sessionId);
171190
+ const maxTag = getMaxTagNumberBySession(db, sessionId);
169949
171191
  const toolAgeCutoff = maxTag - config2.autoDropToolAge;
169950
171192
  const protectedCutoff = maxTag - config2.protectedTags;
169951
171193
  let droppedTools = 0;
@@ -170010,15 +171252,16 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
170010
171252
  const allMessages = Array.from(messageTagNumbers.keys());
170011
171253
  const toolFingerprints = buildToolFingerprints(allMessages);
170012
171254
  if (toolFingerprints.size > 0) {
170013
- const tagsByMessageId = new Map;
171255
+ const tagsByCompositeKey = new Map;
170014
171256
  for (const tag of tags) {
170015
171257
  if (tag.type === "tool" && tag.status === "active" && tag.messageId) {
170016
- tagsByMessageId.set(tag.messageId, tag);
171258
+ const key = tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId;
171259
+ tagsByCompositeKey.set(key, tag);
170017
171260
  }
170018
171261
  }
170019
171262
  const fingerprintGroups = new Map;
170020
- for (const [messageId, fingerprint] of toolFingerprints) {
170021
- const tag = tagsByMessageId.get(messageId);
171263
+ for (const [compositeKey, fingerprint] of toolFingerprints) {
171264
+ const tag = tagsByCompositeKey.get(compositeKey);
170022
171265
  if (!tag || tag.tagNumber > protectedCutoff)
170023
171266
  continue;
170024
171267
  const group = fingerprintGroups.get(fingerprint) ?? [];
@@ -170075,6 +171318,9 @@ function buildToolFingerprints(messages) {
170075
171318
  for (const message of messages) {
170076
171319
  if (message.info.role !== "assistant")
170077
171320
  continue;
171321
+ const ownerMsgId = typeof message.info.id === "string" ? message.info.id : null;
171322
+ if (!ownerMsgId)
171323
+ continue;
170078
171324
  for (const part of message.parts) {
170079
171325
  const record2 = part;
170080
171326
  const info = extractToolInfo(record2);
@@ -170084,8 +171330,9 @@ function buildToolFingerprints(messages) {
170084
171330
  if (!callId)
170085
171331
  continue;
170086
171332
  try {
170087
- const fingerprint = `${info.toolName}:${JSON.stringify(info.args)}`;
170088
- fingerprints.set(callId, fingerprint);
171333
+ const fingerprint = `${ownerMsgId}:${info.toolName}:${JSON.stringify(info.args)}`;
171334
+ const compositeKey = `${ownerMsgId}\x00${callId}`;
171335
+ fingerprints.set(compositeKey, fingerprint);
170089
171336
  } catch {}
170090
171337
  }
170091
171338
  }
@@ -170191,7 +171438,7 @@ async function runPostTransformPhase(args) {
170191
171438
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
170192
171439
  const pendingCountBefore = pendingOps.length;
170193
171440
  const tApply = performance.now();
170194
- didMutateFromPendingOperations = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, args.tags, pendingOps);
171441
+ didMutateFromPendingOperations = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
170195
171442
  const pendingCountAfter = getPendingOps(args.db, args.sessionId).length;
170196
171443
  if (pendingCountBefore > 0 && pendingCountAfter === 0) {
170197
171444
  clearPersistedStickyTurnReminder(args.db, args.sessionId);
@@ -170222,8 +171469,8 @@ async function runPostTransformPhase(args) {
170222
171469
  logTransformTiming(args.sessionId, "watermarkCleanup", t6);
170223
171470
  }
170224
171471
  const t7 = performance.now();
170225
- const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge, args.skipTypedReasoningCleanup);
170226
- stripClearedReasoning(args.messages, args.skipTypedReasoningCleanup);
171472
+ const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
171473
+ stripClearedReasoning(args.messages);
170227
171474
  const strippedInline = stripInlineThinking(args.messages, args.messageTagNumbers, args.clearReasoningAge);
170228
171475
  if (clearedReasoning > 0 || strippedInline > 0) {
170229
171476
  let maxTag = 0;
@@ -170493,6 +171740,9 @@ function createTransform(deps) {
170493
171740
  const resolvedSessionId = sessionId;
170494
171741
  logTransformTiming(sessionId, "findSessionId", startTime, `messages=${messages.length}`);
170495
171742
  const db = deps.db;
171743
+ if (deps.client !== undefined) {
171744
+ scheduleReconciliation(db, sessionId, readRawSessionMessages);
171745
+ }
170496
171746
  const tUserMsg = performance.now();
170497
171747
  const currentTurnId = findLastUserMessageId(messages);
170498
171748
  logTransformTiming(sessionId, "findLastUserMessageId", tUserMsg);
@@ -170508,12 +171758,16 @@ function createTransform(deps) {
170508
171758
  const reducedMode = sessionMeta.isSubagent;
170509
171759
  const fullFeatureMode = !reducedMode;
170510
171760
  let sessionDirectory = deps.directory ?? "";
170511
- if (deps.client !== undefined) {
171761
+ const cachedDirectory = deps.sessionDirectoryBySession?.get(sessionId);
171762
+ if (cachedDirectory && cachedDirectory.length > 0) {
171763
+ sessionDirectory = cachedDirectory;
171764
+ } else if (deps.client !== undefined) {
170512
171765
  try {
170513
171766
  const sessionResponse = await deps.client.session.get({ path: { id: sessionId } }).catch(() => null);
170514
171767
  const sessionInfo = sessionResponse?.data;
170515
171768
  if (sessionInfo && typeof sessionInfo.directory === "string" && sessionInfo.directory.length > 0) {
170516
171769
  sessionDirectory = sessionInfo.directory;
171770
+ deps.sessionDirectoryBySession?.set(sessionId, sessionDirectory);
170517
171771
  }
170518
171772
  } catch {}
170519
171773
  }
@@ -170710,13 +171964,17 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
170710
171964
  }
170711
171965
  }
170712
171966
  const t1 = performance.now();
170713
- const tags = getTagsBySession(db, sessionId);
170714
- logTransformTiming(sessionId, "getTagsBySession", t1, `count=${tags.length}`);
171967
+ const activeTags = getActiveTagsBySession(db, sessionId);
171968
+ logTransformTiming(sessionId, "getActiveTagsBySession", t1, `count=${activeTags.length}`);
171969
+ const t1b = performance.now();
171970
+ const targetTagNumbers = [...targets.keys()];
171971
+ const targetsSliceTags = getTagsByNumbers(db, sessionId, targetTagNumbers);
171972
+ logTransformTiming(sessionId, "getTagsByNumbers", t1b, `targets=${targetTagNumbers.length} fetched=${targetsSliceTags.length}`);
170715
171973
  let didMutateFromFlushedStatuses = false;
170716
171974
  if (taggingSucceeded) {
170717
171975
  try {
170718
171976
  const t2 = performance.now();
170719
- didMutateFromFlushedStatuses = applyFlushedStatuses(sessionId, db, targets, tags);
171977
+ didMutateFromFlushedStatuses = applyFlushedStatuses(sessionId, db, targets, targetsSliceTags);
170720
171978
  logTransformTiming(sessionId, "applyFlushedStatuses", t2);
170721
171979
  batch?.finalize();
170722
171980
  logTransformTiming(sessionId, "batchFinalize:flushed", t2);
@@ -170732,12 +171990,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
170732
171990
  const t3 = performance.now();
170733
171991
  const strippedStructuralNoise = stripStructuralNoise(messages);
170734
171992
  logTransformTiming(sessionId, "stripStructuralNoise", t3, `strippedParts=${strippedStructuralNoise}`);
170735
- const currentSessionModel = deps.liveModelBySession?.get(sessionId) ?? findLastAssistantModel(messages) ?? undefined;
170736
- const skipTypedReasoningCleanup = modelRequiresInterleavedReasoning(currentSessionModel);
170737
171993
  const persistedReasoningWatermark = sessionMeta?.clearedReasoningThroughTag ?? 0;
170738
171994
  if (persistedReasoningWatermark > 0) {
170739
171995
  const tReplay = performance.now();
170740
- const replayed = replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark, skipTypedReasoningCleanup);
171996
+ const replayed = replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark);
170741
171997
  const replayedInline = replayStrippedInlineThinking(messages, messageTagNumbers, persistedReasoningWatermark);
170742
171998
  if (replayed > 0 || replayedInline > 0) {
170743
171999
  sessionLog(sessionId, `reasoning replay: cleared=${replayed} inlineStripped=${replayedInline} (watermark=${persistedReasoningWatermark})`);
@@ -170746,27 +172002,22 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
170746
172002
  }
170747
172003
  if (!deps.ctxReduceEnabled && !reducedMode && deps.cavemanTextCompression?.enabled) {
170748
172004
  const tCavemanReplay = performance.now();
170749
- const replayedCaveman = replayCavemanCompression(sessionId, db, targets, tags);
172005
+ const replayedCaveman = replayCavemanCompression(sessionId, db, targets, targetsSliceTags);
170750
172006
  if (replayedCaveman > 0) {
170751
172007
  sessionLog(sessionId, `caveman replay: re-applied ${replayedCaveman} text tags`);
170752
172008
  }
170753
172009
  logTransformTiming(sessionId, "replayCavemanCompression", tCavemanReplay);
170754
172010
  }
170755
172011
  const t4 = performance.now();
170756
- const strippedClearedReasoning = stripClearedReasoning(messages, skipTypedReasoningCleanup);
172012
+ const strippedClearedReasoning = stripClearedReasoning(messages);
170757
172013
  logTransformTiming(sessionId, "stripClearedReasoning", t4, `strippedParts=${strippedClearedReasoning}`);
170758
172014
  const tMergeStrip = performance.now();
170759
- const strippedMergedReasoning = skipTypedReasoningCleanup ? 0 : stripReasoningFromMergedAssistants(messages);
172015
+ const strippedMergedReasoning = stripReasoningFromMergedAssistants(messages);
170760
172016
  if (strippedMergedReasoning > 0) {
170761
172017
  sessionLog(sessionId, `stripped ${strippedMergedReasoning} reasoning parts from merged assistants (anthropic groupIntoBlocks workaround)`);
170762
172018
  }
170763
172019
  logTransformTiming(sessionId, "stripReasoningFromMergedAssistants", tMergeStrip, `strippedParts=${strippedMergedReasoning}`);
170764
- let watermark = 0;
170765
- for (const tag of tags) {
170766
- if (tag.status === "dropped" && tag.tagNumber > watermark) {
170767
- watermark = tag.tagNumber;
170768
- }
170769
- }
172020
+ const watermark = getMaxDroppedTagNumber(db, sessionId);
170770
172021
  const contextUsage = contextUsageEarly;
170771
172022
  const schedulerDecision = schedulerDecisionEarly;
170772
172023
  const rawGetNotifParams = deps.getNotificationParams;
@@ -170816,7 +172067,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
170816
172067
  sessionId,
170817
172068
  db,
170818
172069
  messages,
170819
- tags,
172070
+ tags: activeTags,
170820
172071
  targets,
170821
172072
  reasoningByMessage,
170822
172073
  messageTagNumbers,
@@ -170842,7 +172093,6 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
170842
172093
  watermark,
170843
172094
  forceMaterializationPercentage: FORCE_MATERIALIZE_PERCENTAGE,
170844
172095
  hasRecentReduceCall,
170845
- skipTypedReasoningCleanup,
170846
172096
  projectPath: deps.projectPath,
170847
172097
  autoSearch: deps.autoSearch,
170848
172098
  cavemanTextCompression: deps.ctxReduceEnabled === false && !reducedMode ? deps.cavemanTextCompression : undefined
@@ -171211,6 +172461,7 @@ function createEventHandler2(deps) {
171211
172461
  sessionLog(info.sessionID, `event message.removed: invalidating state for message ${info.messageID}`);
171212
172462
  try {
171213
172463
  const cleanup = cleanupRemovedMessageState(deps, info.sessionID, info.messageID);
172464
+ scheduleClearAndReindex(deps.db, info.sessionID, readRawSessionMessages);
171214
172465
  deps.tagger.cleanup(info.sessionID);
171215
172466
  sessionLog(info.sessionID, "event message.removed: invalidated tagger session cache");
171216
172467
  if (cleanup.clearedNudgePlacement) {
@@ -171329,7 +172580,7 @@ function createNudger(config2) {
171329
172580
  }
171330
172581
  const largest = formatLargestTags(topNFn(db, sessionId, 3));
171331
172582
  const protectedCount = config2.protected_tags;
171332
- const activeTags = (preloadedTags ?? getTagsBySession(db, sessionId)).filter((t) => t.status === "active");
172583
+ const activeTags = preloadedTags ? preloadedTags.filter((t) => t.status === "active") : getActiveTagsBySession(db, sessionId);
171333
172584
  const highestProtected = activeTags.map((t) => t.tagNumber).sort((a, b) => b - a).slice(0, protectedCount)[0];
171334
172585
  const protectedHint = highestProtected ? ` Tags §${highestProtected}§ and above are protected (last ${protectedCount}) — You MUST NOT try to reduce those.` : "";
171335
172586
  const oldToolHint = formatOldToolTags(activeTags, protectedCount, 5);
@@ -171420,7 +172671,7 @@ function estimateProjectedPercentage(db, sessionId, contextUsage, preloadedTags)
171420
172671
  if (pendingDrops.length === 0) {
171421
172672
  return null;
171422
172673
  }
171423
- const activeTags = (preloadedTags ?? getTagsBySession(db, sessionId)).filter((t) => t.status === "active");
172674
+ const activeTags = preloadedTags ? preloadedTags.filter((t) => t.status === "active") : getActiveTagsBySession(db, sessionId);
171424
172675
  const totalActiveBytes = activeTags.reduce((sum, t) => sum + t.byteSize, 0);
171425
172676
  if (totalActiveBytes === 0) {
171426
172677
  return null;
@@ -171493,6 +172744,7 @@ function clearSidebarSnapshotCache(sessionId) {
171493
172744
  // src/hooks/magic-context/hook-handlers.ts
171494
172745
  init_logger();
171495
172746
  init_note_nudger();
172747
+ await init_read_session_chunk();
171496
172748
  var TOOL_HEAVY_TURN_REMINDER_THRESHOLD = 5;
171497
172749
  var TOOL_HEAVY_TURN_REMINDER_TEXT = `
171498
172750
 
@@ -171545,6 +172797,14 @@ function createEventHook(args) {
171545
172797
  return async (input) => {
171546
172798
  await args.eventHandler(input);
171547
172799
  if (input.event.type === "message.updated") {
172800
+ const messageInfo = getMessageUpdatedInfo(input.event.properties);
172801
+ if (messageInfo?.messageID) {
172802
+ const isTerminalUser = messageInfo.role === "user";
172803
+ const isTerminalAssistant = messageInfo.role === "assistant" && (typeof messageInfo.completedAt === "number" || typeof messageInfo.finish === "string");
172804
+ if (isTerminalUser || isTerminalAssistant) {
172805
+ scheduleIncrementalIndex(args.db, messageInfo.sessionID, messageInfo.messageID, readRawSessionMessageById);
172806
+ }
172807
+ }
171548
172808
  const assistantInfo = getMessageUpdatedAssistantInfo(input.event.properties);
171549
172809
  if (assistantInfo?.providerID && assistantInfo?.modelID) {
171550
172810
  const previous = args.liveModelBySession.get(assistantInfo.sessionID);
@@ -171566,10 +172826,14 @@ function createEventHook(args) {
171566
172826
  const sessionId = resolveSessionId(properties);
171567
172827
  if (!sessionId)
171568
172828
  return;
172829
+ if (input.event.type !== "session.deleted") {
172830
+ scheduleReconciliation(args.db, sessionId, readRawSessionMessages);
172831
+ }
171569
172832
  if (input.event.type === "session.deleted") {
171570
172833
  args.liveModelBySession.delete(sessionId);
171571
172834
  args.variantBySession.delete(sessionId);
171572
172835
  args.agentBySession.delete(sessionId);
172836
+ args.sessionDirectoryBySession.delete(sessionId);
171573
172837
  args.recentReduceBySession.delete(sessionId);
171574
172838
  args.toolUsageSinceUserTurn.delete(sessionId);
171575
172839
  args.historyRefreshSessions.delete(sessionId);
@@ -171580,6 +172844,7 @@ function createEventHook(args) {
171580
172844
  clearNoteNudgeState(args.db, sessionId);
171581
172845
  clearAutoSearchForSession(sessionId);
171582
172846
  clearSidebarSnapshotCache(sessionId);
172847
+ clearSessionTracking(sessionId);
171583
172848
  }
171584
172849
  };
171585
172850
  }
@@ -171627,8 +172892,8 @@ init_send_session_notification();
171627
172892
 
171628
172893
  // src/hooks/magic-context/system-prompt-hash.ts
171629
172894
  import { createHash as createHash4 } from "node:crypto";
171630
- import { existsSync as existsSync10, readFileSync as readFileSync8, realpathSync } from "node:fs";
171631
- import { join as join19, resolve as resolve4, sep } from "node:path";
172895
+ import { existsSync as existsSync12, readFileSync as readFileSync8, realpathSync } from "node:fs";
172896
+ import { join as join21, resolve as resolve4, sep } from "node:path";
171632
172897
 
171633
172898
  // src/agents/magic-context-prompt.ts
171634
172899
  function getToolHistoryGuidance(dropToolStructure) {
@@ -171741,9 +173006,9 @@ var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
171741
173006
  function readProjectDocs(directory) {
171742
173007
  const sections = [];
171743
173008
  for (const filename of DOC_FILES) {
171744
- const filePath = join19(directory, filename);
173009
+ const filePath = join21(directory, filename);
171745
173010
  try {
171746
- if (existsSync10(filePath)) {
173011
+ if (existsSync12(filePath)) {
171747
173012
  const content = readFileSync8(filePath, "utf-8").trim();
171748
173013
  if (content.length > 0) {
171749
173014
  sections.push(`<${filename}>
@@ -171856,7 +173121,7 @@ ${items}
171856
173121
  log(`[magic-context] key file path escapes project root, skipping: ${entry.filePath}`);
171857
173122
  continue;
171858
173123
  }
171859
- if (!existsSync10(absPath))
173124
+ if (!existsSync12(absPath))
171860
173125
  continue;
171861
173126
  let realPath;
171862
173127
  try {
@@ -172024,6 +173289,7 @@ function createMagicContextHook(deps) {
172024
173289
  const variantBySession = deps.liveSessionState?.variantBySession ?? new Map;
172025
173290
  const liveModelBySession = deps.liveSessionState?.liveModelBySession ?? new Map;
172026
173291
  const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
173292
+ const sessionDirectoryBySession = deps.liveSessionState?.sessionDirectoryBySession ?? new Map;
172027
173293
  const recentReduceBySession = new Map;
172028
173294
  const toolUsageSinceUserTurn = new Map;
172029
173295
  const resolveLiveModel = (sessionId) => {
@@ -172091,6 +173357,7 @@ function createMagicContextHook(deps) {
172091
173357
  compressorMaxMergeDepth: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.max_merge_depth,
172092
173358
  compressorCooldownMs: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.cooldown_ms,
172093
173359
  liveModelBySession,
173360
+ sessionDirectoryBySession,
172094
173361
  autoSearch: deps.config.experimental?.auto_search?.enabled ? {
172095
173362
  enabled: true,
172096
173363
  scoreThreshold: deps.config.experimental.auto_search.score_threshold,
@@ -172255,6 +173522,7 @@ function createMagicContextHook(deps) {
172255
173522
  liveModelBySession,
172256
173523
  variantBySession,
172257
173524
  agentBySession,
173525
+ sessionDirectoryBySession,
172258
173526
  recentReduceBySession,
172259
173527
  toolUsageSinceUserTurn,
172260
173528
  historyRefreshSessions,
@@ -172398,8 +173666,14 @@ function truncateError(name2, code, message, maxLen = 240) {
172398
173666
 
172399
173667
  // src/plugin/rpc-handlers.ts
172400
173668
  init_project_identity();
173669
+ init_storage_memory();
173670
+ init_tool_definition_tokens();
172401
173671
  await init_storage();
172402
173672
  init_read_session_formatting();
173673
+ await __promiseAll([
173674
+ init_inject_compartments(),
173675
+ init_read_session_db()
173676
+ ]);
172403
173677
 
172404
173678
  // src/hooks/magic-context/tokenizer-calibration.ts
172405
173679
  var CALIBRATION_TABLE = [
@@ -172606,7 +173880,7 @@ function resolveConfigValue(cfg, key, modelKey, defaultValue) {
172606
173880
  }
172607
173881
  return defaultValue;
172608
173882
  }
172609
- function buildSidebarSnapshot(db, sessionId, directory, liveSessionState) {
173883
+ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, injectionBudgetTokens) {
172610
173884
  const empty = {
172611
173885
  sessionId,
172612
173886
  usagePercentage: 0,
@@ -172691,6 +173965,20 @@ ${c.content}
172691
173965
  const cached2 = meta3.memory_block_cache;
172692
173966
  if (typeof cached2 === "string" && cached2.length > 0) {
172693
173967
  memoryTokens = estimateTokens(cached2);
173968
+ } else if (memoryBlockCount > 0 && projectIdentity) {
173969
+ try {
173970
+ let memories = getMemoriesByProject(db, projectIdentity, [
173971
+ "active",
173972
+ "permanent"
173973
+ ]);
173974
+ if (injectionBudgetTokens && memories.length > 0) {
173975
+ memories = trimMemoriesToBudget(sessionId, memories, injectionBudgetTokens);
173976
+ }
173977
+ const block = renderMemoryBlock(memories);
173978
+ memoryTokens = block ? estimateTokens(block) : 0;
173979
+ } catch {
173980
+ memoryTokens = 0;
173981
+ }
172694
173982
  }
172695
173983
  }
172696
173984
  let lastDreamerRunAt = null;
@@ -172709,8 +173997,24 @@ ${c.content}
172709
173997
  let activeProviderID;
172710
173998
  let activeModelID;
172711
173999
  if (liveSessionState) {
172712
- const model = liveSessionState.liveModelBySession.get(sessionId);
172713
- const agent = liveSessionState.agentBySession.get(sessionId);
174000
+ let model = liveSessionState.liveModelBySession.get(sessionId);
174001
+ let agent = liveSessionState.agentBySession.get(sessionId);
174002
+ if (!model || !agent) {
174003
+ const recovered = findLastAssistantModelFromOpenCodeDb(sessionId);
174004
+ if (recovered) {
174005
+ if (!model) {
174006
+ model = {
174007
+ providerID: recovered.providerID,
174008
+ modelID: recovered.modelID
174009
+ };
174010
+ liveSessionState.liveModelBySession.set(sessionId, model);
174011
+ }
174012
+ if (!agent && recovered.agent) {
174013
+ agent = recovered.agent;
174014
+ liveSessionState.agentBySession.set(sessionId, agent);
174015
+ }
174016
+ }
174017
+ }
172714
174018
  if (model) {
172715
174019
  activeProviderID = model.providerID;
172716
174020
  activeModelID = model.modelID;
@@ -172759,8 +174063,8 @@ ${c.content}
172759
174063
  return empty;
172760
174064
  }
172761
174065
  }
172762
- function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSessionState) {
172763
- const base = buildSidebarSnapshot(db, sessionId, directory, liveSessionState);
174066
+ function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSessionState, injectionBudgetTokens) {
174067
+ const base = buildSidebarSnapshot(db, sessionId, directory, liveSessionState, injectionBudgetTokens);
172764
174068
  const detail = {
172765
174069
  ...base,
172766
174070
  tagCounter: 0,
@@ -172876,13 +174180,14 @@ function registerRpcHandlers(rpcServer, args) {
172876
174180
  const { directory, config: config2, liveSessionState } = args;
172877
174181
  const rawConfig = config2;
172878
174182
  const getNotificationParams = (sessionId) => getLiveNotificationParams(sessionId, liveSessionState.liveModelBySession, liveSessionState.variantBySession, liveSessionState.agentBySession);
174183
+ const injectionBudgetTokens = config2.memory?.injection_budget_tokens;
172879
174184
  rpcServer.handle("sidebar-snapshot", async (params) => {
172880
174185
  const sessionId = String(params.sessionId ?? "");
172881
174186
  const dir = String(params.directory ?? directory);
172882
174187
  const db = getDb();
172883
174188
  if (!db || !sessionId)
172884
174189
  return { error: "unavailable" };
172885
- return buildSidebarSnapshot(db, sessionId, dir, liveSessionState);
174190
+ return buildSidebarSnapshot(db, sessionId, dir, liveSessionState, injectionBudgetTokens);
172886
174191
  });
172887
174192
  rpcServer.handle("status-detail", async (params) => {
172888
174193
  const sessionId = String(params.sessionId ?? "");
@@ -172891,7 +174196,7 @@ function registerRpcHandlers(rpcServer, args) {
172891
174196
  const db = getDb();
172892
174197
  if (!db || !sessionId)
172893
174198
  return { error: "unavailable" };
172894
- return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState);
174199
+ return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState, injectionBudgetTokens);
172895
174200
  });
172896
174201
  rpcServer.handle("compartment-count", async (params) => {
172897
174202
  const sessionId = String(params.sessionId ?? "");
@@ -173898,19 +175203,19 @@ init_models_dev_cache();
173898
175203
 
173899
175204
  // src/shared/rpc-server.ts
173900
175205
  init_logger();
173901
- import { mkdirSync as mkdirSync5, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
175206
+ import { mkdirSync as mkdirSync6, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
173902
175207
  import { createServer } from "node:http";
173903
- import { dirname as dirname4 } from "node:path";
175208
+ import { dirname as dirname5 } from "node:path";
173904
175209
 
173905
175210
  // src/shared/rpc-utils.ts
173906
175211
  import { createHash as createHash5 } from "node:crypto";
173907
- import { join as join20 } from "node:path";
175212
+ import { join as join22 } from "node:path";
173908
175213
  function projectHash(directory) {
173909
175214
  const normalized = directory.replace(/\/+$/, "");
173910
175215
  return createHash5("sha256").update(normalized).digest("hex").slice(0, 16);
173911
175216
  }
173912
175217
  function rpcPortFilePath(storageDir, directory) {
173913
- return join20(storageDir, "rpc", projectHash(directory), "port");
175218
+ return join22(storageDir, "rpc", projectHash(directory), "port");
173914
175219
  }
173915
175220
 
173916
175221
  // src/shared/rpc-server.ts
@@ -173941,10 +175246,10 @@ class MagicContextRpcServer {
173941
175246
  this.port = addr.port;
173942
175247
  this.server = server;
173943
175248
  try {
173944
- const dir = dirname4(this.portFilePath);
173945
- mkdirSync5(dir, { recursive: true });
175249
+ const dir = dirname5(this.portFilePath);
175250
+ mkdirSync6(dir, { recursive: true });
173946
175251
  const tmpPath = `${this.portFilePath}.tmp`;
173947
- writeFileSync5(tmpPath, String(this.port), "utf-8");
175252
+ writeFileSync6(tmpPath, String(this.port), "utf-8");
173948
175253
  renameSync(tmpPath, this.portFilePath);
173949
175254
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
173950
175255
  } catch (err) {
@@ -174106,7 +175411,7 @@ var plugin = async (ctx) => {
174106
175411
  refreshModelLimitsFromApi(ctx.client);
174107
175412
  setInterval(() => {
174108
175413
  refreshModelLimitsFromApi(ctx.client);
174109
- }, 5 * 60 * 1000);
175414
+ }, 60 * 60 * 1000);
174110
175415
  }
174111
175416
  if (conflictResult?.hasConflict) {
174112
175417
  sendConflictWarning(ctx.client, ctx.directory, conflictResult);