@cortexkit/pi-magic-context 0.17.2 → 0.19.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.
@@ -11277,7 +11277,7 @@ function finalize(ctx, schema) {
11277
11277
  result.$schema = "http://json-schema.org/draft-07/schema#";
11278
11278
  } else if (ctx.target === "draft-04") {
11279
11279
  result.$schema = "http://json-schema.org/draft-04/schema#";
11280
- } else if (ctx.target === "openapi-3.0") {} else {}
11280
+ } else if (ctx.target === "openapi-3.0") {}
11281
11281
  if (ctx.external?.uri) {
11282
11282
  const id = ctx.external.registry.get(schema)?.id;
11283
11283
  if (!id)
@@ -11538,7 +11538,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
11538
11538
  if (val === undefined) {
11539
11539
  if (ctx.unrepresentable === "throw") {
11540
11540
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
11541
- } else {}
11541
+ }
11542
11542
  } else if (typeof val === "bigint") {
11543
11543
  if (ctx.unrepresentable === "throw") {
11544
11544
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -14891,10 +14891,63 @@ var init_magic_context = __esm(() => {
14891
14891
  });
14892
14892
  });
14893
14893
 
14894
- // ../plugin/src/shared/logger.ts
14895
- import * as fs from "node:fs";
14894
+ // ../plugin/src/shared/harness.ts
14895
+ function setHarness(value) {
14896
+ if (harnessLocked && currentHarness !== value) {
14897
+ throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
14898
+ }
14899
+ currentHarness = value;
14900
+ harnessLocked = true;
14901
+ }
14902
+ function getHarness() {
14903
+ return currentHarness;
14904
+ }
14905
+ var currentHarness = "opencode", harnessLocked = false;
14906
+
14907
+ // ../plugin/src/shared/data-path.ts
14896
14908
  import * as os from "node:os";
14897
14909
  import * as path from "node:path";
14910
+ function getDataDir() {
14911
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
14912
+ }
14913
+ function getMagicContextTempDir(harness = getHarness()) {
14914
+ return path.join(os.tmpdir(), harness, "magic-context");
14915
+ }
14916
+ function getMagicContextLogPath(harness = getHarness()) {
14917
+ return path.join(getMagicContextTempDir(harness), "magic-context.log");
14918
+ }
14919
+ function getProjectMagicContextDir(directory) {
14920
+ return path.join(directory, ".opencode", "magic-context");
14921
+ }
14922
+ function getProjectMagicContextHistorianDir(directory) {
14923
+ return path.join(getProjectMagicContextDir(directory), "historian");
14924
+ }
14925
+ function getOpenCodeStorageDir() {
14926
+ return path.join(getDataDir(), "opencode", "storage");
14927
+ }
14928
+ function getMagicContextStorageDir() {
14929
+ return path.join(getDataDir(), "cortexkit", "magic-context");
14930
+ }
14931
+ function getLegacyOpenCodeMagicContextStorageDir() {
14932
+ return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
14933
+ }
14934
+ function getCacheDir() {
14935
+ return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
14936
+ }
14937
+ var init_data_path = () => {};
14938
+
14939
+ // ../plugin/src/shared/logger.ts
14940
+ import * as fs from "node:fs";
14941
+ import * as path2 from "node:path";
14942
+ function ensureDir(filePath) {
14943
+ const dir = path2.dirname(filePath);
14944
+ if (dir === lastEnsuredDir)
14945
+ return;
14946
+ try {
14947
+ fs.mkdirSync(dir, { recursive: true });
14948
+ lastEnsuredDir = dir;
14949
+ } catch {}
14950
+ }
14898
14951
  function flush() {
14899
14952
  if (flushTimer) {
14900
14953
  clearTimeout(flushTimer);
@@ -14905,6 +14958,8 @@ function flush() {
14905
14958
  const data = buffer.join("");
14906
14959
  buffer = [];
14907
14960
  try {
14961
+ const logFile = getMagicContextLogPath();
14962
+ ensureDir(logFile);
14908
14963
  fs.appendFileSync(logFile, data);
14909
14964
  } catch {}
14910
14965
  }
@@ -14935,9 +14990,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
14935
14990
  function sessionLog(sessionId, message, data) {
14936
14991
  log(`[magic-context][${sessionId}] ${message}`, data);
14937
14992
  }
14938
- var logFile, isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
14993
+ var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, lastEnsuredDir = null;
14939
14994
  var init_logger = __esm(() => {
14940
- logFile = path.join(os.tmpdir(), "magic-context.log");
14995
+ init_data_path();
14941
14996
  buffer = [];
14942
14997
  if (!isTestEnv) {
14943
14998
  process.on("exit", flush);
@@ -14961,30 +15016,47 @@ function cosineSimilarity(a, b) {
14961
15016
  return denominator === 0 ? 0 : dotProduct / denominator;
14962
15017
  }
14963
15018
 
14964
- // ../plugin/src/shared/data-path.ts
14965
- import * as os2 from "node:os";
14966
- import * as path2 from "node:path";
14967
- function getDataDir() {
14968
- return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
14969
- }
14970
- function getOpenCodeStorageDir() {
14971
- return path2.join(getDataDir(), "opencode", "storage");
15019
+ // ../plugin/src/features/magic-context/memory/normalize-hash.ts
15020
+ import { createHash } from "node:crypto";
15021
+ function normalizeMemoryContent(content) {
15022
+ return content.toLowerCase().replace(/\s+/g, " ").trim();
14972
15023
  }
14973
- function getMagicContextStorageDir() {
14974
- return path2.join(getDataDir(), "cortexkit", "magic-context");
15024
+ function computeNormalizedHash(content) {
15025
+ const normalized = normalizeMemoryContent(content);
15026
+ return createHash("md5").update(normalized).digest("hex");
14975
15027
  }
14976
- function getLegacyOpenCodeMagicContextStorageDir() {
14977
- return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15028
+ var init_normalize_hash = () => {};
15029
+
15030
+ // ../plugin/src/features/magic-context/memory/embedding-identity.ts
15031
+ function normalizeEndpoint(endpoint) {
15032
+ return endpoint?.trim().replace(/\/+$/, "") ?? "";
14978
15033
  }
14979
- function getCacheDir() {
14980
- return process.env.XDG_CACHE_HOME ?? path2.join(os2.homedir(), ".cache");
15034
+ function getEmbeddingProviderIdentity(config2) {
15035
+ if (config2.provider === "off") {
15036
+ return "embedding-provider:off";
15037
+ }
15038
+ const identityInput = config2.provider === "openai-compatible" ? {
15039
+ provider: "openai-compatible",
15040
+ model: config2.model.trim(),
15041
+ endpoint: normalizeEndpoint(config2.endpoint),
15042
+ apiKeyPresent: Boolean(config2.api_key?.trim())
15043
+ } : {
15044
+ provider: "local",
15045
+ model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
15046
+ endpoint: "",
15047
+ apiKeyPresent: false
15048
+ };
15049
+ return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
14981
15050
  }
14982
- var init_data_path = () => {};
15051
+ var init_embedding_identity = __esm(() => {
15052
+ init_magic_context();
15053
+ init_normalize_hash();
15054
+ });
14983
15055
 
14984
15056
  // ../plugin/src/features/magic-context/memory/embedding-local.ts
14985
- import { mkdirSync } from "node:fs";
15057
+ import { mkdirSync as mkdirSync2 } from "node:fs";
14986
15058
  import { open, stat, unlink, writeFile } from "node:fs/promises";
14987
- import { join as join3 } from "node:path";
15059
+ import { join as join2 } from "node:path";
14988
15060
  async function acquireModelLoadLock(lockPath) {
14989
15061
  const waitStart = Date.now();
14990
15062
  while (true) {
@@ -15099,7 +15171,7 @@ class LocalEmbeddingProvider {
15099
15171
  initPromise = null;
15100
15172
  constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
15101
15173
  this.model = model;
15102
- this.modelId = `local:${model}`;
15174
+ this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
15103
15175
  }
15104
15176
  async initialize() {
15105
15177
  if (this.pipeline) {
@@ -15118,15 +15190,15 @@ class LocalEmbeddingProvider {
15118
15190
  if (LogLevel && "ERROR" in LogLevel) {
15119
15191
  env.logLevel = LogLevel.ERROR;
15120
15192
  }
15121
- const modelCacheDir = join3(getMagicContextStorageDir(), "models");
15193
+ const modelCacheDir = join2(getMagicContextStorageDir(), "models");
15122
15194
  try {
15123
- mkdirSync(modelCacheDir, { recursive: true });
15195
+ mkdirSync2(modelCacheDir, { recursive: true });
15124
15196
  env.cacheDir = modelCacheDir;
15125
15197
  } catch {
15126
15198
  log("[magic-context] could not create model cache dir, using library default");
15127
15199
  }
15128
15200
  const createPipeline = transformersModule.pipeline;
15129
- const lockPath = join3(modelCacheDir, ".load.lock");
15201
+ const lockPath = join2(modelCacheDir, ".load.lock");
15130
15202
  const releaseLock = await acquireModelLoadLock(lockPath);
15131
15203
  const stopHeartbeat = startLockHeartbeat(lockPath);
15132
15204
  try {
@@ -15241,12 +15313,13 @@ var init_embedding_local = __esm(() => {
15241
15313
  init_magic_context();
15242
15314
  init_data_path();
15243
15315
  init_logger();
15316
+ init_embedding_identity();
15244
15317
  STALE_LOCK_MS = 3 * 60000;
15245
15318
  MAX_LOCK_WAIT_MS = 5 * 60000;
15246
15319
  });
15247
15320
 
15248
15321
  // ../plugin/src/features/magic-context/memory/embedding-openai.ts
15249
- function normalizeEndpoint(endpoint) {
15322
+ function normalizeEndpoint2(endpoint) {
15250
15323
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
15251
15324
  }
15252
15325
 
@@ -15261,10 +15334,15 @@ class OpenAICompatibleEmbeddingProvider {
15261
15334
  openLogged = false;
15262
15335
  halfOpenProbeInFlight = false;
15263
15336
  constructor(options) {
15264
- this.endpoint = normalizeEndpoint(options.endpoint);
15337
+ this.endpoint = normalizeEndpoint2(options.endpoint);
15265
15338
  this.model = options.model?.trim() ?? "";
15266
15339
  this.apiKey = options.apiKey?.trim() ?? "";
15267
- this.modelId = `openai-compat:${this.endpoint}:${this.model}`;
15340
+ this.modelId = getEmbeddingProviderIdentity({
15341
+ provider: "openai-compatible",
15342
+ endpoint: this.endpoint,
15343
+ model: this.model,
15344
+ ...this.apiKey ? { api_key: this.apiKey } : {}
15345
+ });
15268
15346
  }
15269
15347
  async initialize() {
15270
15348
  if (this.initialized)
@@ -15446,20 +15524,10 @@ class OpenAICompatibleEmbeddingProvider {
15446
15524
  var FAILURE_THRESHOLD = 3, FAILURE_WINDOW_MS = 60000, OPEN_DURATION_MS, FETCH_TIMEOUT_MS = 30000;
15447
15525
  var init_embedding_openai = __esm(() => {
15448
15526
  init_logger();
15527
+ init_embedding_identity();
15449
15528
  OPEN_DURATION_MS = 5 * 60000;
15450
15529
  });
15451
15530
 
15452
- // ../plugin/src/features/magic-context/memory/normalize-hash.ts
15453
- import { createHash } from "node:crypto";
15454
- function normalizeMemoryContent(content) {
15455
- return content.toLowerCase().replace(/\s+/g, " ").trim();
15456
- }
15457
- function computeNormalizedHash(content) {
15458
- const normalized = normalizeMemoryContent(content);
15459
- return createHash("md5").update(normalized).digest("hex");
15460
- }
15461
- var init_normalize_hash = () => {};
15462
-
15463
15531
  // ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
15464
15532
  var exports_storage_memory_embeddings = {};
15465
15533
  __export(exports_storage_memory_embeddings, {
@@ -15604,17 +15672,8 @@ function resolveEmbeddingConfig(config2) {
15604
15672
  }
15605
15673
  return { provider: "off" };
15606
15674
  }
15607
- function resolveModelId(config2) {
15608
- if (config2.provider === "off") {
15609
- return "off";
15610
- }
15611
- if (config2.provider === "openai-compatible") {
15612
- const endpoint = config2.endpoint.trim();
15613
- const model = config2.model.trim();
15614
- const keyHash = config2.api_key ? computeNormalizedHash(config2.api_key) : "nokey";
15615
- return `openai-compat:${endpoint}:${model}:${keyHash}`;
15616
- }
15617
- return config2.model.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL;
15675
+ function resolveProviderIdentity(config2) {
15676
+ return getEmbeddingProviderIdentity(config2);
15618
15677
  }
15619
15678
  function createProvider(config2) {
15620
15679
  if (config2.provider === "off") {
@@ -15638,10 +15697,10 @@ function getOrCreateProvider() {
15638
15697
  }
15639
15698
  function initializeEmbedding(config2) {
15640
15699
  const nextConfig = resolveEmbeddingConfig(config2);
15641
- const nextModelId = resolveModelId(nextConfig);
15700
+ const nextProviderIdentity = resolveProviderIdentity(nextConfig);
15642
15701
  const previousProvider = provider;
15643
- const previousModelId = previousProvider?.modelId ?? resolveModelId(embeddingConfig);
15644
- if (previousModelId === nextModelId) {
15702
+ const previousProviderIdentity = previousProvider?.modelId ?? resolveProviderIdentity(embeddingConfig);
15703
+ if (previousProviderIdentity === nextProviderIdentity) {
15645
15704
  embeddingConfig = nextConfig;
15646
15705
  return;
15647
15706
  }
@@ -15789,9 +15848,9 @@ var DEFAULT_EMBEDDING_CONFIG, embeddingConfig, provider = null, loadUnembeddedMe
15789
15848
  var init_embedding = __esm(() => {
15790
15849
  init_magic_context();
15791
15850
  init_logger();
15851
+ init_embedding_identity();
15792
15852
  init_embedding_local();
15793
15853
  init_embedding_openai();
15794
- init_normalize_hash();
15795
15854
  init_storage_memory_embeddings();
15796
15855
  DEFAULT_EMBEDDING_CONFIG = {
15797
15856
  provider: "local",
@@ -15983,7 +16042,7 @@ var exports_native_binding = {};
15983
16042
  __export(exports_native_binding, {
15984
16043
  resolveBetterSqliteNativeBinding: () => resolveBetterSqliteNativeBinding
15985
16044
  });
15986
- import { existsSync, mkdirSync as mkdirSync2, writeFileSync } from "node:fs";
16045
+ import { existsSync, mkdirSync as mkdirSync3, writeFileSync } from "node:fs";
15987
16046
  import { createRequire } from "node:module";
15988
16047
  import * as path3 from "node:path";
15989
16048
  function logInfo(message) {
@@ -16083,7 +16142,7 @@ async function resolveBetterSqliteNativeBinding() {
16083
16142
  }
16084
16143
  logWarn(`cached binary at ${cachedPath} has wrong ABI (${cachedProbe.actual ?? "unknown"} != ${expected}); refetching`);
16085
16144
  }
16086
- mkdirSync2(path3.dirname(cachedPath), { recursive: true });
16145
+ mkdirSync3(path3.dirname(cachedPath), { recursive: true });
16087
16146
  const nodeFileBytes = await downloadElectronPrebuild(pkgVersion, expected);
16088
16147
  writeFileSync(cachedPath, nodeFileBytes);
16089
16148
  logInfo(`cached Electron prebuild at ${cachedPath} (${(nodeFileBytes.length / 1024).toFixed(1)} KB)`);
@@ -23749,10 +23808,10 @@ var require_stringify = __commonJS((exports, module) => {
23749
23808
  replacer = null;
23750
23809
  indent = EMPTY;
23751
23810
  };
23752
- var join7 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
23811
+ var join6 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
23753
23812
  var join_content = (inside, value, gap) => {
23754
23813
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
23755
- return join7(comment, inside, gap);
23814
+ return join6(comment, inside, gap);
23756
23815
  };
23757
23816
  var stringify_string = (holder, key, value) => {
23758
23817
  const raw = get_raw_string_literal(holder, key);
@@ -23774,13 +23833,13 @@ var require_stringify = __commonJS((exports, module) => {
23774
23833
  if (i !== 0) {
23775
23834
  inside += COMMA;
23776
23835
  }
23777
- const before = join7(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
23836
+ const before = join6(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
23778
23837
  inside += before || LF + deeper_gap;
23779
23838
  inside += stringify(i, value, deeper_gap) || STR_NULL;
23780
23839
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
23781
23840
  after_comma = process_comments(value, AFTER(i), deeper_gap);
23782
23841
  }
23783
- inside += join7(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23842
+ inside += join6(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23784
23843
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
23785
23844
  };
23786
23845
  var object_stringify = (value, gap) => {
@@ -23801,13 +23860,13 @@ var require_stringify = __commonJS((exports, module) => {
23801
23860
  inside += COMMA;
23802
23861
  }
23803
23862
  first = false;
23804
- const before = join7(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
23863
+ const before = join6(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
23805
23864
  inside += before || LF + deeper_gap;
23806
23865
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
23807
23866
  after_comma = process_comments(value, AFTER(key), deeper_gap);
23808
23867
  };
23809
23868
  keys.forEach(iteratee);
23810
- inside += join7(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23869
+ inside += join6(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23811
23870
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
23812
23871
  };
23813
23872
  function stringify(key, holder, gap) {
@@ -23908,9 +23967,9 @@ __export(exports_read_session_db, {
23908
23967
  findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
23909
23968
  closeReadOnlySessionDb: () => closeReadOnlySessionDb
23910
23969
  });
23911
- import { join as join8 } from "node:path";
23970
+ import { join as join7 } from "node:path";
23912
23971
  function getOpenCodeDbPath() {
23913
- return join8(getDataDir(), "opencode", "opencode.db");
23972
+ return join7(getDataDir(), "opencode", "opencode.db");
23914
23973
  }
23915
23974
  function closeCachedReadOnlyDb() {
23916
23975
  if (!cachedReadOnlyDb) {
@@ -24005,8 +24064,8 @@ init_embedding();
24005
24064
 
24006
24065
  // ../plugin/src/features/magic-context/storage-db.ts
24007
24066
  init_data_path();
24008
- import { copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
24009
- import { join as join6 } from "node:path";
24067
+ import { copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync4 } from "node:fs";
24068
+ import { join as join5 } from "node:path";
24010
24069
 
24011
24070
  // ../plugin/src/shared/error-message.ts
24012
24071
  function getErrorMessage(error51) {
@@ -24223,6 +24282,9 @@ var MIGRATIONS = [
24223
24282
  embedding BLOB NOT NULL,
24224
24283
  model_id TEXT NOT NULL,
24225
24284
  created_at INTEGER NOT NULL,
24285
+ -- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
24286
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
24287
+ -- every connection and v12 cleans historical orphan rows.
24226
24288
  FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
24227
24289
  );
24228
24290
 
@@ -24343,6 +24405,48 @@ var MIGRATIONS = [
24343
24405
  WHERE type = 'tool' AND tool_owner_message_id IS NULL;
24344
24406
  `);
24345
24407
  }
24408
+ },
24409
+ {
24410
+ version: 11,
24411
+ description: "Add todo state synthesis columns to session_meta",
24412
+ up: (db) => {
24413
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
24414
+ if (!cols.some((c) => c.name === "last_todo_state")) {
24415
+ db.exec("ALTER TABLE session_meta ADD COLUMN last_todo_state TEXT DEFAULT ''");
24416
+ }
24417
+ if (!cols.some((c) => c.name === "todo_synthetic_call_id")) {
24418
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_call_id TEXT DEFAULT ''");
24419
+ }
24420
+ if (!cols.some((c) => c.name === "todo_synthetic_anchor_message_id")) {
24421
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_anchor_message_id TEXT DEFAULT ''");
24422
+ }
24423
+ if (!cols.some((c) => c.name === "todo_synthetic_state_json")) {
24424
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
24425
+ }
24426
+ }
24427
+ },
24428
+ {
24429
+ version: 12,
24430
+ description: "Clean orphan rows from FK-cascade embedding tables",
24431
+ up: (db) => {
24432
+ const hasTable = (name) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
24433
+ const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
24434
+ WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
24435
+ log(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
24436
+ const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
24437
+ WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
24438
+ log(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
24439
+ }
24440
+ },
24441
+ {
24442
+ version: 13,
24443
+ description: "Add pending_compaction_marker_state column for deferred marker drain",
24444
+ up: (db) => {
24445
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
24446
+ if (!cols.some((c) => c.name === "pending_compaction_marker_state")) {
24447
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
24448
+ }
24449
+ }
24346
24450
  }
24347
24451
  ];
24348
24452
  function ensureMigrationsTable(db) {
@@ -157002,6 +157106,28 @@ function mergeCommitHashes(existing, next) {
157002
157106
  return merged;
157003
157107
  }
157004
157108
 
157109
+ // ../plugin/src/shared/stable-json.ts
157110
+ function stableStringify(value, seen = new WeakSet) {
157111
+ if (value === undefined)
157112
+ return "undefined";
157113
+ if (value === null || typeof value !== "object")
157114
+ return JSON.stringify(value) ?? String(value);
157115
+ if (seen.has(value))
157116
+ return '"[Circular]"';
157117
+ seen.add(value);
157118
+ if (Array.isArray(value)) {
157119
+ return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
157120
+ }
157121
+ const entries = Object.entries(value).sort(([a], [b]) => {
157122
+ if (a < b)
157123
+ return -1;
157124
+ if (a > b)
157125
+ return 1;
157126
+ return 0;
157127
+ });
157128
+ return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
157129
+ }
157130
+
157005
157131
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
157006
157132
  var measurements = new Map;
157007
157133
  var fingerprints = new Map;
@@ -157037,11 +157163,11 @@ function loadToolDefinitionMeasurements(db) {
157037
157163
  init_data_path();
157038
157164
  init_logger();
157039
157165
  import { existsSync as existsSync2 } from "node:fs";
157040
- import { join as join5 } from "node:path";
157166
+ import { join as join4 } from "node:path";
157041
157167
  var LEASE_DURATION_MS = 5 * 60 * 1000;
157042
157168
  var LEASE_RENEWAL_MS = 60 * 1000;
157043
157169
  function resolveOpencodeDbPath() {
157044
- return join5(getDataDir(), "opencode", "opencode.db");
157170
+ return join4(getDataDir(), "opencode", "opencode.db");
157045
157171
  }
157046
157172
  function ensureBackfillStateTable(db) {
157047
157173
  db.exec(`
@@ -157284,20 +157410,20 @@ var persistenceByDatabase = new WeakMap;
157284
157410
  var persistenceErrorByDatabase = new WeakMap;
157285
157411
  function resolveDatabasePath() {
157286
157412
  const dbDir = getMagicContextStorageDir();
157287
- return { dbDir, dbPath: join6(dbDir, "context.db") };
157413
+ return { dbDir, dbPath: join5(dbDir, "context.db") };
157288
157414
  }
157289
157415
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
157290
157416
  if (existsSync3(targetDbPath))
157291
157417
  return;
157292
157418
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
157293
- const legacyDbPath = join6(legacyDir, "context.db");
157419
+ const legacyDbPath = join5(legacyDir, "context.db");
157294
157420
  if (!existsSync3(legacyDbPath))
157295
157421
  return;
157296
157422
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
157297
- mkdirSync3(targetDbDir, { recursive: true });
157423
+ mkdirSync4(targetDbDir, { recursive: true });
157298
157424
  for (const suffix of ["", "-wal", "-shm"]) {
157299
157425
  const src = `${legacyDbPath}${suffix}`;
157300
- const dst = join6(targetDbDir, `context.db${suffix}`);
157426
+ const dst = join5(targetDbDir, `context.db${suffix}`);
157301
157427
  if (existsSync3(src)) {
157302
157428
  try {
157303
157429
  copyFileSync(src, dst);
@@ -157306,8 +157432,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
157306
157432
  }
157307
157433
  }
157308
157434
  }
157309
- const legacyModelsDir = join6(legacyDir, "models");
157310
- const targetModelsDir = join6(targetDbDir, "models");
157435
+ const legacyModelsDir = join5(legacyDir, "models");
157436
+ const targetModelsDir = join5(targetDbDir, "models");
157311
157437
  if (existsSync3(legacyModelsDir) && !existsSync3(targetModelsDir)) {
157312
157438
  try {
157313
157439
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
@@ -157317,9 +157443,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
157317
157443
  }
157318
157444
  }
157319
157445
  function initializeDatabase(db) {
157446
+ db.exec("PRAGMA foreign_keys=ON");
157320
157447
  db.exec("PRAGMA journal_mode=WAL");
157321
157448
  db.exec("PRAGMA busy_timeout=5000");
157322
- db.exec("PRAGMA foreign_keys=ON");
157323
157449
  db.exec(`
157324
157450
  CREATE TABLE IF NOT EXISTS tags (
157325
157451
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -157417,6 +157543,9 @@ function initializeDatabase(db) {
157417
157543
  );
157418
157544
 
157419
157545
  CREATE TABLE IF NOT EXISTS memory_embeddings (
157546
+ -- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
157547
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
157548
+ -- every connection and v12 cleans historical orphan rows.
157420
157549
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
157421
157550
  embedding BLOB NOT NULL,
157422
157551
  model_id TEXT
@@ -157509,6 +157638,10 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
157509
157638
  note_nudge_trigger_message_id TEXT DEFAULT '',
157510
157639
  note_nudge_sticky_text TEXT DEFAULT '',
157511
157640
  note_nudge_sticky_message_id TEXT DEFAULT '',
157641
+ last_todo_state TEXT DEFAULT '',
157642
+ todo_synthetic_call_id TEXT DEFAULT '',
157643
+ todo_synthetic_anchor_message_id TEXT DEFAULT '',
157644
+ todo_synthetic_state_json TEXT DEFAULT '',
157512
157645
  is_subagent INTEGER DEFAULT 0,
157513
157646
  last_context_percentage REAL DEFAULT 0,
157514
157647
  last_input_tokens INTEGER DEFAULT 0,
@@ -157520,7 +157653,13 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
157520
157653
  system_prompt_hash TEXT DEFAULT '',
157521
157654
  memory_block_cache TEXT DEFAULT '',
157522
157655
  memory_block_count INTEGER DEFAULT 0,
157523
- memory_block_ids TEXT DEFAULT ''
157656
+ memory_block_ids TEXT DEFAULT '',
157657
+ -- pending_compaction_marker_state: intentionally NULLABLE without a
157658
+ -- default. Absence of a deferred marker is SQL NULL; presence is a
157659
+ -- valid JSON blob written via setPendingCompactionMarkerState.
157660
+ -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
157661
+ -- != empty-string defensively. Plan v6 section 3.
157662
+ pending_compaction_marker_state TEXT
157524
157663
  );
157525
157664
 
157526
157665
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -157573,6 +157712,10 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
157573
157712
  ensureColumn(db, "session_meta", "note_nudge_trigger_message_id", "TEXT DEFAULT ''");
157574
157713
  ensureColumn(db, "session_meta", "note_nudge_sticky_text", "TEXT DEFAULT ''");
157575
157714
  ensureColumn(db, "session_meta", "note_nudge_sticky_message_id", "TEXT DEFAULT ''");
157715
+ ensureColumn(db, "session_meta", "last_todo_state", "TEXT DEFAULT ''");
157716
+ ensureColumn(db, "session_meta", "todo_synthetic_call_id", "TEXT DEFAULT ''");
157717
+ ensureColumn(db, "session_meta", "todo_synthetic_anchor_message_id", "TEXT DEFAULT ''");
157718
+ ensureColumn(db, "session_meta", "todo_synthetic_state_json", "TEXT DEFAULT ''");
157576
157719
  ensureColumn(db, "session_meta", "note_last_read_at", "INTEGER DEFAULT 0");
157577
157720
  ensureColumn(db, "session_meta", "times_execute_threshold_reached", "INTEGER DEFAULT 0");
157578
157721
  ensureColumn(db, "session_meta", "compartment_in_progress", "INTEGER DEFAULT 0");
@@ -157604,6 +157747,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
157604
157747
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
157605
157748
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
157606
157749
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
157750
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
157607
157751
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
157608
157752
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
157609
157753
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -157637,6 +157781,10 @@ function healNullTextColumns(db) {
157637
157781
  ["note_nudge_trigger_message_id", ""],
157638
157782
  ["note_nudge_sticky_text", ""],
157639
157783
  ["note_nudge_sticky_message_id", ""],
157784
+ ["last_todo_state", ""],
157785
+ ["todo_synthetic_call_id", ""],
157786
+ ["todo_synthetic_anchor_message_id", ""],
157787
+ ["todo_synthetic_state_json", ""],
157640
157788
  ["system_prompt_hash", ""],
157641
157789
  ["stripped_placeholder_ids", ""],
157642
157790
  ["memory_block_cache", ""],
@@ -157689,7 +157837,7 @@ function openDatabase() {
157689
157837
  }
157690
157838
  try {
157691
157839
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
157692
- mkdirSync3(dbDir, { recursive: true });
157840
+ mkdirSync4(dbDir, { recursive: true });
157693
157841
  const db = new Database(dbPath);
157694
157842
  initializeDatabase(db);
157695
157843
  runMigrations(db);
@@ -157711,20 +157859,6 @@ function openDatabase() {
157711
157859
  }
157712
157860
  }
157713
157861
 
157714
- // ../plugin/src/shared/harness.ts
157715
- var currentHarness = "opencode";
157716
- var harnessLocked = false;
157717
- function setHarness(value) {
157718
- if (harnessLocked && currentHarness !== value) {
157719
- throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
157720
- }
157721
- currentHarness = value;
157722
- harnessLocked = true;
157723
- }
157724
- function getHarness() {
157725
- return currentHarness;
157726
- }
157727
-
157728
157862
  // src/subagent-entry.ts
157729
157863
  init_logger();
157730
157864
 
@@ -157732,12 +157866,12 @@ init_logger();
157732
157866
  init_magic_context();
157733
157867
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
157734
157868
  import { homedir as homedir3 } from "node:os";
157735
- import { join as join7 } from "node:path";
157869
+ import { join as join6 } from "node:path";
157736
157870
 
157737
157871
  // ../plugin/src/config/variable.ts
157738
157872
  import { existsSync as existsSync4, readFileSync } from "node:fs";
157739
157873
  import { homedir as homedir2 } from "node:os";
157740
- import { dirname as dirname2, isAbsolute, resolve } from "node:path";
157874
+ import { dirname as dirname3, isAbsolute, resolve } from "node:path";
157741
157875
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
157742
157876
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
157743
157877
  function substituteConfigVariables(input) {
@@ -157756,7 +157890,7 @@ function substituteConfigVariables(input) {
157756
157890
  if (fileMatches.length === 0) {
157757
157891
  return { text, warnings };
157758
157892
  }
157759
- const configDir = input.configPath ? dirname2(input.configPath) : process.cwd();
157893
+ const configDir = input.configPath ? dirname3(input.configPath) : process.cwd();
157760
157894
  let output = "";
157761
157895
  let cursor = 0;
157762
157896
  for (const match of fileMatches) {
@@ -157800,12 +157934,12 @@ function substituteConfigVariables(input) {
157800
157934
  var import_comment_json = __toESM(require_src2(), 1);
157801
157935
  var CONFIG_FILE_NAME = "magic-context";
157802
157936
  function getProjectConfigPaths(cwd) {
157803
- const basePath = join7(cwd, ".pi", CONFIG_FILE_NAME);
157937
+ const basePath = join6(cwd, ".pi", CONFIG_FILE_NAME);
157804
157938
  return [`${basePath}.jsonc`, `${basePath}.json`];
157805
157939
  }
157806
157940
  function getUserConfigPaths() {
157807
157941
  const home = process.env.HOME ?? homedir3();
157808
- const basePath = join7(home, ".pi", "agent", CONFIG_FILE_NAME);
157942
+ const basePath = join6(home, ".pi", "agent", CONFIG_FILE_NAME);
157809
157943
  return [`${basePath}.jsonc`, `${basePath}.json`];
157810
157944
  }
157811
157945
  function resolveFirstExisting(paths) {
@@ -163998,7 +164132,8 @@ var META_COLUMNS = {
163998
164132
  systemPromptTokens: "system_prompt_tokens",
163999
164133
  conversationTokens: "conversation_tokens",
164000
164134
  toolCallTokens: "tool_call_tokens",
164001
- clearedReasoningThroughTag: "cleared_reasoning_through_tag"
164135
+ clearedReasoningThroughTag: "cleared_reasoning_through_tag",
164136
+ lastTodoState: "last_todo_state"
164002
164137
  };
164003
164138
  var BOOLEAN_META_KEYS = new Set(["isSubagent", "compartmentInProgress"]);
164004
164139
  function isStringOrNull(value) {
@@ -164011,7 +164146,7 @@ function isSessionMetaRow(row) {
164011
164146
  if (row === null || typeof row !== "object")
164012
164147
  return false;
164013
164148
  const r = row;
164014
- return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag);
164149
+ return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state);
164015
164150
  }
164016
164151
  function getDefaultSessionMeta(sessionId) {
164017
164152
  return {
@@ -164031,7 +164166,8 @@ function getDefaultSessionMeta(sessionId) {
164031
164166
  systemPromptTokens: 0,
164032
164167
  conversationTokens: 0,
164033
164168
  toolCallTokens: 0,
164034
- clearedReasoningThroughTag: 0
164169
+ clearedReasoningThroughTag: 0,
164170
+ lastTodoState: ""
164035
164171
  };
164036
164172
  }
164037
164173
  function ensureSessionMetaRow(db, sessionId) {
@@ -164043,6 +164179,7 @@ function toSessionMeta(row) {
164043
164179
  const transformErrorRaw = typeof row.last_transform_error === "string" ? row.last_transform_error : "";
164044
164180
  const cacheTtlRaw = typeof row.cache_ttl === "string" && row.cache_ttl.length > 0 ? row.cache_ttl : "5m";
164045
164181
  const systemPromptHashRaw = row.system_prompt_hash == null ? "" : row.system_prompt_hash;
164182
+ const lastTodoStateRaw = typeof row.last_todo_state === "string" ? row.last_todo_state : "";
164046
164183
  const numOrZero = (value) => typeof value === "number" ? value : 0;
164047
164184
  return {
164048
164185
  sessionId: row.session_id,
@@ -164061,7 +164198,8 @@ function toSessionMeta(row) {
164061
164198
  systemPromptTokens: numOrZero(row.system_prompt_tokens),
164062
164199
  conversationTokens: numOrZero(row.conversation_tokens),
164063
164200
  toolCallTokens: numOrZero(row.tool_call_tokens),
164064
- clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag)
164201
+ clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
164202
+ lastTodoState: lastTodoStateRaw
164065
164203
  };
164066
164204
  }
164067
164205
 
@@ -164258,6 +164396,28 @@ function setStrippedPlaceholderIds(db, sessionId, ids) {
164258
164396
  const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
164259
164397
  db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
164260
164398
  }
164399
+ function isPendingCompactionMarker(value) {
164400
+ return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
164401
+ }
164402
+ function getPendingCompactionMarkerState(db, sessionId) {
164403
+ const row = db.prepare("SELECT pending_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
164404
+ const raw = row?.pending_compaction_marker_state;
164405
+ if (raw === null || raw === undefined || raw === "")
164406
+ return null;
164407
+ try {
164408
+ const parsed = JSON.parse(raw);
164409
+ if (isPendingCompactionMarker(parsed)) {
164410
+ return parsed;
164411
+ }
164412
+ } catch {}
164413
+ return null;
164414
+ }
164415
+ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
164416
+ const expectedBlob = stableStringify(expected);
164417
+ const result = db.prepare(`UPDATE session_meta SET pending_compaction_marker_state = NULL
164418
+ WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
164419
+ return result.changes > 0;
164420
+ }
164261
164421
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
164262
164422
  init_logger();
164263
164423
  await init_read_session_db();
@@ -164277,7 +164437,7 @@ function resolveIsSubagentFromOpenCodeDb(sessionId) {
164277
164437
 
164278
164438
  // ../plugin/src/features/magic-context/storage-meta-session.ts
164279
164439
  function getOrCreateSessionMeta(db, sessionId) {
164280
- const result = db.prepare("SELECT session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, system_prompt_tokens, conversation_tokens, tool_call_tokens, cleared_reasoning_through_tag FROM session_meta WHERE session_id = ?").get(sessionId);
164440
+ const result = db.prepare("SELECT session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, system_prompt_tokens, conversation_tokens, tool_call_tokens, cleared_reasoning_through_tag, last_todo_state FROM session_meta WHERE session_id = ?").get(sessionId);
164281
164441
  if (isSessionMetaRow(result)) {
164282
164442
  return toSessionMeta(result);
164283
164443
  }
@@ -166174,16 +166334,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
166174
166334
  function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
166175
166335
  const cached2 = injectionCache.get(sessionId);
166176
166336
  if (!isCacheBusting && cached2) {
166177
- if (cached2.compartmentEndMessageId.length > 0) {
166178
- const cutoffIndex2 = messages.findIndex((message) => message.info.id === cached2.compartmentEndMessageId);
166179
- if (cutoffIndex2 >= 0) {
166180
- const remaining = messages.slice(cutoffIndex2 + 1);
166181
- messages.splice(0, messages.length, ...remaining);
166182
- } else {
166183
- sessionLog(sessionId, `compartment injection: cached boundary ${cached2.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
166337
+ if (cached2.kind === "empty") {
166338
+ return null;
166339
+ }
166340
+ const prepared = cached2.injection;
166341
+ if (prepared.compartmentEndMessageId === null) {
166342
+ sessionLog(sessionId, "compartment injection cache in degraded mode (null boundary), forcing rebuild");
166343
+ } else {
166344
+ if (prepared.compartmentEndMessageId.length > 0) {
166345
+ const cutoffIndex2 = messages.findIndex((message) => message.info.id === prepared.compartmentEndMessageId);
166346
+ if (cutoffIndex2 >= 0) {
166347
+ const remaining = messages.slice(cutoffIndex2 + 1);
166348
+ messages.splice(0, messages.length, ...remaining);
166349
+ } else {
166350
+ sessionLog(sessionId, `compartment injection: cached boundary ${prepared.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
166351
+ }
166184
166352
  }
166353
+ return { ...prepared, rebuiltFromDb: false };
166185
166354
  }
166186
- return cached2;
166187
166355
  }
166188
166356
  const compartments = getCompartments(db, sessionId);
166189
166357
  const facts = getSessionFacts(db, sessionId);
@@ -166215,7 +166383,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166215
166383
  }
166216
166384
  }
166217
166385
  if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
166218
- injectionCache.delete(sessionId);
166386
+ injectionCache.set(sessionId, {
166387
+ kind: "empty",
166388
+ compartmentEndMessageId: "",
166389
+ renderedBytes: 0
166390
+ });
166219
166391
  return null;
166220
166392
  }
166221
166393
  let dateRanges;
@@ -166248,9 +166420,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166248
166420
  compartmentCount: 0,
166249
166421
  skippedVisibleMessages: 0,
166250
166422
  factCount: facts.length,
166251
- memoryCount
166423
+ memoryCount,
166424
+ rebuiltFromDb: true
166252
166425
  };
166253
- injectionCache.set(sessionId, result2);
166426
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
166254
166427
  return result2;
166255
166428
  }
166256
166429
  const lastCompartment = compartments[compartments.length - 1];
@@ -166268,9 +166441,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166268
166441
  compartmentCount: compartments.length,
166269
166442
  skippedVisibleMessages: 0,
166270
166443
  factCount: facts.length,
166271
- memoryCount
166444
+ memoryCount,
166445
+ rebuiltFromDb: true
166272
166446
  };
166273
- injectionCache.set(sessionId, result2);
166447
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
166274
166448
  return result2;
166275
166449
  }
166276
166450
  let skippedVisibleMessages = 0;
@@ -166279,17 +166453,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166279
166453
  skippedVisibleMessages = cutoffIndex + 1;
166280
166454
  const remaining = messages.slice(cutoffIndex + 1);
166281
166455
  messages.splice(0, messages.length, ...remaining);
166456
+ } else {
166457
+ sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
166282
166458
  }
166283
166459
  const result = {
166284
166460
  block,
166285
166461
  compartmentEndMessage: lastEnd,
166286
- compartmentEndMessageId: lastEndMessageId,
166462
+ compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
166287
166463
  compartmentCount: compartments.length,
166288
166464
  skippedVisibleMessages,
166289
166465
  factCount: facts.length,
166290
- memoryCount
166466
+ memoryCount,
166467
+ rebuiltFromDb: true
166291
166468
  };
166292
- injectionCache.set(sessionId, result);
166469
+ injectionCache.set(sessionId, { kind: "populated", injection: result });
166293
166470
  return result;
166294
166471
  }
166295
166472