@cortexkit/opencode-magic-context 0.18.0 → 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.
Files changed (94) hide show
  1. package/README.md +1 -1
  2. package/dist/config/index.d.ts.map +1 -1
  3. package/dist/features/magic-context/compaction-marker.d.ts +17 -0
  4. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  5. package/dist/features/magic-context/compartment-storage.d.ts +11 -0
  6. package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
  7. package/dist/features/magic-context/dreamer/queue.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
  9. package/dist/features/magic-context/memory/embedding-identity.d.ts +11 -0
  10. package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -0
  11. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  12. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  13. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  14. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  15. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  16. package/dist/features/magic-context/storage-meta-persisted.d.ts +56 -0
  17. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  18. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  19. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  20. package/dist/features/magic-context/storage.d.ts +1 -1
  21. package/dist/features/magic-context/storage.d.ts.map +1 -1
  22. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  23. package/dist/hooks/magic-context/cache-busting-signals.d.ts +10 -0
  24. package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -0
  25. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +50 -0
  27. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
  29. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts +1 -1
  30. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/compartment-runner-types.d.ts +16 -7
  34. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/compartment-runner.d.ts +7 -2
  36. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/historian-state-file.d.ts +25 -11
  39. package/dist/hooks/magic-context/historian-state-file.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/hook-handlers.d.ts +11 -4
  41. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/inject-compartments.d.ts +2 -1
  44. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/live-session-state.d.ts +3 -1
  46. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +10 -4
  48. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +15 -1
  50. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/transform.d.ts +2 -0
  52. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  53. package/dist/index.js +1177 -547
  54. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  55. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  56. package/dist/plugin/sidebar-snapshot-cache.d.ts.map +1 -1
  57. package/dist/shared/conflict-detector.d.ts +49 -0
  58. package/dist/shared/conflict-detector.d.ts.map +1 -1
  59. package/dist/shared/conflict-fixer.d.ts +1 -1
  60. package/dist/shared/conflict-fixer.d.ts.map +1 -1
  61. package/dist/shared/data-path.d.ts +84 -0
  62. package/dist/shared/data-path.d.ts.map +1 -1
  63. package/dist/shared/logger.d.ts +6 -0
  64. package/dist/shared/logger.d.ts.map +1 -1
  65. package/dist/shared/rpc-client.d.ts +2 -1
  66. package/dist/shared/rpc-client.d.ts.map +1 -1
  67. package/dist/shared/rpc-notifications.d.ts +3 -2
  68. package/dist/shared/rpc-notifications.d.ts.map +1 -1
  69. package/dist/shared/rpc-server.d.ts +3 -0
  70. package/dist/shared/rpc-server.d.ts.map +1 -1
  71. package/dist/shared/rpc-types.d.ts +1 -0
  72. package/dist/shared/rpc-types.d.ts.map +1 -1
  73. package/dist/shared/rpc-utils.d.ts +13 -2
  74. package/dist/shared/rpc-utils.d.ts.map +1 -1
  75. package/dist/shared/stable-json.d.ts +21 -0
  76. package/dist/shared/stable-json.d.ts.map +1 -0
  77. package/dist/tui/data/context-db.d.ts.map +1 -1
  78. package/package.json +1 -1
  79. package/src/shared/conflict-detector.ts +4 -4
  80. package/src/shared/conflict-fixer.test.ts +124 -0
  81. package/src/shared/conflict-fixer.ts +34 -28
  82. package/src/shared/data-path.test.ts +38 -0
  83. package/src/shared/data-path.ts +99 -0
  84. package/src/shared/logger.ts +29 -3
  85. package/src/shared/rpc-client.test.ts +161 -0
  86. package/src/shared/rpc-client.ts +82 -22
  87. package/src/shared/rpc-notifications.test.ts +20 -0
  88. package/src/shared/rpc-notifications.ts +9 -6
  89. package/src/shared/rpc-server.ts +42 -4
  90. package/src/shared/rpc-types.ts +1 -0
  91. package/src/shared/rpc-utils.ts +59 -3
  92. package/src/shared/stable-json.test.ts +87 -0
  93. package/src/shared/stable-json.ts +37 -0
  94. package/src/tui/data/context-db.ts +20 -1
package/dist/index.js CHANGED
@@ -11403,7 +11403,7 @@ function finalize(ctx, schema) {
11403
11403
  result.$schema = "http://json-schema.org/draft-07/schema#";
11404
11404
  } else if (ctx.target === "draft-04") {
11405
11405
  result.$schema = "http://json-schema.org/draft-04/schema#";
11406
- } else if (ctx.target === "openapi-3.0") {} else {}
11406
+ } else if (ctx.target === "openapi-3.0") {}
11407
11407
  if (ctx.external?.uri) {
11408
11408
  const id = ctx.external.registry.get(schema)?.id;
11409
11409
  if (!id)
@@ -11664,7 +11664,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
11664
11664
  if (val === undefined) {
11665
11665
  if (ctx.unrepresentable === "throw") {
11666
11666
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
11667
- } else {}
11667
+ }
11668
11668
  } else if (typeof val === "bigint") {
11669
11669
  if (ctx.unrepresentable === "throw") {
11670
11670
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -15017,10 +15017,56 @@ var init_magic_context = __esm(() => {
15017
15017
  });
15018
15018
  });
15019
15019
 
15020
- // src/shared/logger.ts
15021
- import * as fs from "node:fs";
15020
+ // src/shared/harness.ts
15021
+ function getHarness() {
15022
+ return currentHarness;
15023
+ }
15024
+ var currentHarness = "opencode";
15025
+
15026
+ // src/shared/data-path.ts
15022
15027
  import * as os from "node:os";
15023
15028
  import * as path from "node:path";
15029
+ function getDataDir() {
15030
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
15031
+ }
15032
+ function getMagicContextTempDir(harness = getHarness()) {
15033
+ return path.join(os.tmpdir(), harness, "magic-context");
15034
+ }
15035
+ function getMagicContextLogPath(harness = getHarness()) {
15036
+ return path.join(getMagicContextTempDir(harness), "magic-context.log");
15037
+ }
15038
+ function getProjectMagicContextDir(directory) {
15039
+ return path.join(directory, ".opencode", "magic-context");
15040
+ }
15041
+ function getProjectMagicContextHistorianDir(directory) {
15042
+ return path.join(getProjectMagicContextDir(directory), "historian");
15043
+ }
15044
+ function getOpenCodeStorageDir() {
15045
+ return path.join(getDataDir(), "opencode", "storage");
15046
+ }
15047
+ function getMagicContextStorageDir() {
15048
+ return path.join(getDataDir(), "cortexkit", "magic-context");
15049
+ }
15050
+ function getLegacyOpenCodeMagicContextStorageDir() {
15051
+ return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15052
+ }
15053
+ function getCacheDir() {
15054
+ return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
15055
+ }
15056
+ var init_data_path = () => {};
15057
+
15058
+ // src/shared/logger.ts
15059
+ import * as fs from "node:fs";
15060
+ import * as path2 from "node:path";
15061
+ function ensureDir(filePath) {
15062
+ const dir = path2.dirname(filePath);
15063
+ if (dir === lastEnsuredDir)
15064
+ return;
15065
+ try {
15066
+ fs.mkdirSync(dir, { recursive: true });
15067
+ lastEnsuredDir = dir;
15068
+ } catch {}
15069
+ }
15024
15070
  function flush() {
15025
15071
  if (flushTimer) {
15026
15072
  clearTimeout(flushTimer);
@@ -15031,6 +15077,8 @@ function flush() {
15031
15077
  const data = buffer.join("");
15032
15078
  buffer = [];
15033
15079
  try {
15080
+ const logFile = getMagicContextLogPath();
15081
+ ensureDir(logFile);
15034
15082
  fs.appendFileSync(logFile, data);
15035
15083
  } catch {}
15036
15084
  }
@@ -15061,9 +15109,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
15061
15109
  function sessionLog(sessionId, message, data) {
15062
15110
  log(`[magic-context][${sessionId}] ${message}`, data);
15063
15111
  }
15064
- var logFile, isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
15112
+ var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, lastEnsuredDir = null;
15065
15113
  var init_logger = __esm(() => {
15066
- logFile = path.join(os.tmpdir(), "magic-context.log");
15114
+ init_data_path();
15067
15115
  buffer = [];
15068
15116
  if (!isTestEnv) {
15069
15117
  process.on("exit", flush);
@@ -148138,21 +148186,36 @@ var init_read_session_formatting = __esm(() => {
148138
148186
  tokenizer = new src_default(exports_claude);
148139
148187
  });
148140
148188
 
148189
+ // src/shared/stable-json.ts
148190
+ function stableStringify(value, seen = new WeakSet) {
148191
+ if (value === undefined)
148192
+ return "undefined";
148193
+ if (value === null || typeof value !== "object")
148194
+ return JSON.stringify(value) ?? String(value);
148195
+ if (seen.has(value))
148196
+ return '"[Circular]"';
148197
+ seen.add(value);
148198
+ if (Array.isArray(value)) {
148199
+ return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
148200
+ }
148201
+ const entries = Object.entries(value).sort(([a], [b]) => {
148202
+ if (a < b)
148203
+ return -1;
148204
+ if (a > b)
148205
+ return 1;
148206
+ return 0;
148207
+ });
148208
+ return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
148209
+ }
148210
+
148141
148211
  // src/features/magic-context/tool-definition-tokens.ts
148212
+ import { createHash } from "node:crypto";
148142
148213
  function keyFor(providerID, modelID, agentName) {
148143
148214
  const agent = agentName && agentName.length > 0 ? agentName : "default";
148144
148215
  return `${providerID}/${modelID}/${agent}`;
148145
148216
  }
148146
148217
  function fingerprintFor(description, parameters) {
148147
- const descLen = description.length;
148148
- if (parameters === undefined)
148149
- return `${descLen}:none`;
148150
- if (parameters === null)
148151
- return `${descLen}:null`;
148152
- if (typeof parameters !== "object")
148153
- return `${descLen}:${typeof parameters}`;
148154
- const keys = Object.keys(parameters);
148155
- return `${descLen}:obj:${keys.length}:${keys.sort().join(",")}`;
148218
+ return createHash("sha256").update(description).update("\x00").update(stableStringify(parameters)).digest("hex");
148156
148219
  }
148157
148220
  function setDatabase(db) {
148158
148221
  persistenceDb = db;
@@ -155990,12 +156053,6 @@ var require_src2 = __commonJS((exports, module) => {
155990
156053
  };
155991
156054
  });
155992
156055
 
155993
- // src/shared/harness.ts
155994
- function getHarness() {
155995
- return currentHarness;
155996
- }
155997
- var currentHarness = "opencode";
155998
-
155999
156056
  // src/features/magic-context/compartment-storage.ts
156000
156057
  function getInsertCompartmentStatement(db) {
156001
156058
  let stmt = insertCompartmentStatements.get(db);
@@ -156069,6 +156126,10 @@ function getLastCompartmentEndMessage(db, sessionId) {
156069
156126
  const row = db.prepare("SELECT MAX(end_message) as max_end FROM compartments WHERE session_id = ?").get(sessionId);
156070
156127
  return row?.max_end ?? -1;
156071
156128
  }
156129
+ function getCompartmentsByEndMessageId(db, sessionId, endMessageId) {
156130
+ const rows = db.prepare("SELECT * FROM compartments WHERE session_id = ? AND end_message_id = ? ORDER BY sequence ASC").all(sessionId, endMessageId).filter(isCompartmentRow);
156131
+ return rows.map(toCompartment);
156132
+ }
156072
156133
  function appendCompartments(db, sessionId, compartments) {
156073
156134
  if (compartments.length === 0)
156074
156135
  return;
@@ -156572,7 +156633,7 @@ var init_compartment_prompt = __esm(() => {
156572
156633
  });
156573
156634
 
156574
156635
  // src/shared/opencode-config-dir.ts
156575
- import { homedir as homedir5 } from "node:os";
156636
+ import { homedir as homedir6 } from "node:os";
156576
156637
  import { join as join7, resolve as resolve3 } from "node:path";
156577
156638
  function getCliConfigDir() {
156578
156639
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
@@ -156580,9 +156641,9 @@ function getCliConfigDir() {
156580
156641
  return resolve3(envConfigDir);
156581
156642
  }
156582
156643
  if (process.platform === "win32") {
156583
- return join7(homedir5(), ".config", "opencode");
156644
+ return join7(homedir6(), ".config", "opencode");
156584
156645
  }
156585
- return join7(process.env.XDG_CONFIG_HOME || join7(homedir5(), ".config"), "opencode");
156646
+ return join7(process.env.XDG_CONFIG_HOME || join7(homedir6(), ".config"), "opencode");
156586
156647
  }
156587
156648
  function getOpenCodeConfigDir(_options) {
156588
156649
  return getCliConfigDir();
@@ -156813,11 +156874,11 @@ __export(exports_conflict_warning_hook, {
156813
156874
  cleanupConflictWarnings: () => cleanupConflictWarnings
156814
156875
  });
156815
156876
  import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
156816
- import { homedir as homedir6, platform as platform2 } from "node:os";
156877
+ import { homedir as homedir7, platform as platform2 } from "node:os";
156817
156878
  import { join as join9 } from "node:path";
156818
156879
  function getDesktopStatePath() {
156819
156880
  const os2 = platform2();
156820
- const home = homedir6();
156881
+ const home = homedir7();
156821
156882
  if (os2 === "darwin") {
156822
156883
  return join9(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
156823
156884
  }
@@ -157096,26 +157157,6 @@ var init_conflict_warning_hook = __esm(() => {
157096
157157
  cachedDesktopStateByDir = new Map;
157097
157158
  });
157098
157159
 
157099
- // src/shared/data-path.ts
157100
- import * as os2 from "node:os";
157101
- import * as path2 from "node:path";
157102
- function getDataDir() {
157103
- return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
157104
- }
157105
- function getOpenCodeStorageDir() {
157106
- return path2.join(getDataDir(), "opencode", "storage");
157107
- }
157108
- function getMagicContextStorageDir() {
157109
- return path2.join(getDataDir(), "cortexkit", "magic-context");
157110
- }
157111
- function getLegacyOpenCodeMagicContextStorageDir() {
157112
- return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
157113
- }
157114
- function getCacheDir() {
157115
- return process.env.XDG_CACHE_HOME ?? path2.join(os2.homedir(), ".cache");
157116
- }
157117
- var init_data_path = () => {};
157118
-
157119
157160
  // src/shared/error-message.ts
157120
157161
  function getErrorMessage(error51) {
157121
157162
  return error51 instanceof Error ? error51.message : String(error51);
@@ -157373,7 +157414,7 @@ var exports_native_binding = {};
157373
157414
  __export(exports_native_binding, {
157374
157415
  resolveBetterSqliteNativeBinding: () => resolveBetterSqliteNativeBinding
157375
157416
  });
157376
- import { existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "node:fs";
157417
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
157377
157418
  import { createRequire } from "node:module";
157378
157419
  import * as path3 from "node:path";
157379
157420
  function logInfo(message) {
@@ -157473,7 +157514,7 @@ async function resolveBetterSqliteNativeBinding() {
157473
157514
  }
157474
157515
  logWarn(`cached binary at ${cachedPath} has wrong ABI (${cachedProbe.actual ?? "unknown"} != ${expected}); refetching`);
157475
157516
  }
157476
- mkdirSync2(path3.dirname(cachedPath), { recursive: true });
157517
+ mkdirSync3(path3.dirname(cachedPath), { recursive: true });
157477
157518
  const nodeFileBytes = await downloadElectronPrebuild(pkgVersion, expected);
157478
157519
  writeFileSync4(cachedPath, nodeFileBytes);
157479
157520
  logInfo(`cached Electron prebuild at ${cachedPath} (${(nodeFileBytes.length / 1024).toFixed(1)} KB)`);
@@ -157651,13 +157692,13 @@ var init_embedding_cache = __esm(() => {
157651
157692
  });
157652
157693
 
157653
157694
  // src/features/magic-context/memory/normalize-hash.ts
157654
- import { createHash } from "node:crypto";
157695
+ import { createHash as createHash2 } from "node:crypto";
157655
157696
  function normalizeMemoryContent(content) {
157656
157697
  return content.toLowerCase().replace(/\s+/g, " ").trim();
157657
157698
  }
157658
157699
  function computeNormalizedHash(content) {
157659
157700
  const normalized = normalizeMemoryContent(content);
157660
- return createHash("md5").update(normalized).digest("hex");
157701
+ return createHash2("md5").update(normalized).digest("hex");
157661
157702
  }
157662
157703
  var init_normalize_hash = () => {};
157663
157704
 
@@ -158251,9 +158292,9 @@ __export(exports_read_session_db, {
158251
158292
  findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
158252
158293
  closeReadOnlySessionDb: () => closeReadOnlySessionDb
158253
158294
  });
158254
- import { join as join12 } from "node:path";
158295
+ import { join as join11 } from "node:path";
158255
158296
  function getOpenCodeDbPath() {
158256
- return join12(getDataDir(), "opencode", "opencode.db");
158297
+ return join11(getDataDir(), "opencode", "opencode.db");
158257
158298
  }
158258
158299
  function closeCachedReadOnlyDb() {
158259
158300
  if (!cachedReadOnlyDb) {
@@ -158360,10 +158401,36 @@ function cosineSimilarity(a, b) {
158360
158401
  return denominator === 0 ? 0 : dotProduct / denominator;
158361
158402
  }
158362
158403
 
158404
+ // src/features/magic-context/memory/embedding-identity.ts
158405
+ function normalizeEndpoint(endpoint) {
158406
+ return endpoint?.trim().replace(/\/+$/, "") ?? "";
158407
+ }
158408
+ function getEmbeddingProviderIdentity(config2) {
158409
+ if (config2.provider === "off") {
158410
+ return "embedding-provider:off";
158411
+ }
158412
+ const identityInput = config2.provider === "openai-compatible" ? {
158413
+ provider: "openai-compatible",
158414
+ model: config2.model.trim(),
158415
+ endpoint: normalizeEndpoint(config2.endpoint),
158416
+ apiKeyPresent: Boolean(config2.api_key?.trim())
158417
+ } : {
158418
+ provider: "local",
158419
+ model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
158420
+ endpoint: "",
158421
+ apiKeyPresent: false
158422
+ };
158423
+ return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
158424
+ }
158425
+ var init_embedding_identity = __esm(() => {
158426
+ init_magic_context();
158427
+ init_normalize_hash();
158428
+ });
158429
+
158363
158430
  // src/features/magic-context/memory/embedding-local.ts
158364
- import { mkdirSync as mkdirSync3 } from "node:fs";
158431
+ import { mkdirSync as mkdirSync4 } from "node:fs";
158365
158432
  import { open, stat, unlink, writeFile } from "node:fs/promises";
158366
- import { join as join14 } from "node:path";
158433
+ import { join as join13 } from "node:path";
158367
158434
  async function acquireModelLoadLock(lockPath) {
158368
158435
  const waitStart = Date.now();
158369
158436
  while (true) {
@@ -158478,7 +158545,7 @@ class LocalEmbeddingProvider {
158478
158545
  initPromise = null;
158479
158546
  constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
158480
158547
  this.model = model;
158481
- this.modelId = `local:${model}`;
158548
+ this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
158482
158549
  }
158483
158550
  async initialize() {
158484
158551
  if (this.pipeline) {
@@ -158497,15 +158564,15 @@ class LocalEmbeddingProvider {
158497
158564
  if (LogLevel && "ERROR" in LogLevel) {
158498
158565
  env.logLevel = LogLevel.ERROR;
158499
158566
  }
158500
- const modelCacheDir = join14(getMagicContextStorageDir(), "models");
158567
+ const modelCacheDir = join13(getMagicContextStorageDir(), "models");
158501
158568
  try {
158502
- mkdirSync3(modelCacheDir, { recursive: true });
158569
+ mkdirSync4(modelCacheDir, { recursive: true });
158503
158570
  env.cacheDir = modelCacheDir;
158504
158571
  } catch {
158505
158572
  log("[magic-context] could not create model cache dir, using library default");
158506
158573
  }
158507
158574
  const createPipeline = transformersModule.pipeline;
158508
- const lockPath = join14(modelCacheDir, ".load.lock");
158575
+ const lockPath = join13(modelCacheDir, ".load.lock");
158509
158576
  const releaseLock = await acquireModelLoadLock(lockPath);
158510
158577
  const stopHeartbeat = startLockHeartbeat(lockPath);
158511
158578
  try {
@@ -158620,12 +158687,13 @@ var init_embedding_local = __esm(() => {
158620
158687
  init_magic_context();
158621
158688
  init_data_path();
158622
158689
  init_logger();
158690
+ init_embedding_identity();
158623
158691
  STALE_LOCK_MS = 3 * 60000;
158624
158692
  MAX_LOCK_WAIT_MS = 5 * 60000;
158625
158693
  });
158626
158694
 
158627
158695
  // src/features/magic-context/memory/embedding-openai.ts
158628
- function normalizeEndpoint(endpoint) {
158696
+ function normalizeEndpoint2(endpoint) {
158629
158697
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
158630
158698
  }
158631
158699
 
@@ -158640,10 +158708,15 @@ class OpenAICompatibleEmbeddingProvider {
158640
158708
  openLogged = false;
158641
158709
  halfOpenProbeInFlight = false;
158642
158710
  constructor(options) {
158643
- this.endpoint = normalizeEndpoint(options.endpoint);
158711
+ this.endpoint = normalizeEndpoint2(options.endpoint);
158644
158712
  this.model = options.model?.trim() ?? "";
158645
158713
  this.apiKey = options.apiKey?.trim() ?? "";
158646
- this.modelId = `openai-compat:${this.endpoint}:${this.model}`;
158714
+ this.modelId = getEmbeddingProviderIdentity({
158715
+ provider: "openai-compatible",
158716
+ endpoint: this.endpoint,
158717
+ model: this.model,
158718
+ ...this.apiKey ? { api_key: this.apiKey } : {}
158719
+ });
158647
158720
  }
158648
158721
  async initialize() {
158649
158722
  if (this.initialized)
@@ -158825,6 +158898,7 @@ class OpenAICompatibleEmbeddingProvider {
158825
158898
  var FAILURE_THRESHOLD = 3, FAILURE_WINDOW_MS = 60000, OPEN_DURATION_MS, FETCH_TIMEOUT_MS = 30000;
158826
158899
  var init_embedding_openai = __esm(() => {
158827
158900
  init_logger();
158901
+ init_embedding_identity();
158828
158902
  OPEN_DURATION_MS = 5 * 60000;
158829
158903
  });
158830
158904
 
@@ -158862,17 +158936,8 @@ function resolveEmbeddingConfig(config2) {
158862
158936
  }
158863
158937
  return { provider: "off" };
158864
158938
  }
158865
- function resolveModelId(config2) {
158866
- if (config2.provider === "off") {
158867
- return "off";
158868
- }
158869
- if (config2.provider === "openai-compatible") {
158870
- const endpoint = config2.endpoint.trim();
158871
- const model = config2.model.trim();
158872
- const keyHash = config2.api_key ? computeNormalizedHash(config2.api_key) : "nokey";
158873
- return `openai-compat:${endpoint}:${model}:${keyHash}`;
158874
- }
158875
- return config2.model.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL;
158939
+ function resolveProviderIdentity(config2) {
158940
+ return getEmbeddingProviderIdentity(config2);
158876
158941
  }
158877
158942
  function createProvider(config2) {
158878
158943
  if (config2.provider === "off") {
@@ -158896,10 +158961,10 @@ function getOrCreateProvider() {
158896
158961
  }
158897
158962
  function initializeEmbedding(config2) {
158898
158963
  const nextConfig = resolveEmbeddingConfig(config2);
158899
- const nextModelId = resolveModelId(nextConfig);
158964
+ const nextProviderIdentity = resolveProviderIdentity(nextConfig);
158900
158965
  const previousProvider = provider;
158901
- const previousModelId = previousProvider?.modelId ?? resolveModelId(embeddingConfig);
158902
- if (previousModelId === nextModelId) {
158966
+ const previousProviderIdentity = previousProvider?.modelId ?? resolveProviderIdentity(embeddingConfig);
158967
+ if (previousProviderIdentity === nextProviderIdentity) {
158903
158968
  embeddingConfig = nextConfig;
158904
158969
  return;
158905
158970
  }
@@ -159026,9 +159091,9 @@ var DEFAULT_EMBEDDING_CONFIG, embeddingConfig, provider = null, loadUnembeddedMe
159026
159091
  var init_embedding = __esm(() => {
159027
159092
  init_magic_context();
159028
159093
  init_logger();
159094
+ init_embedding_identity();
159029
159095
  init_embedding_local();
159030
159096
  init_embedding_openai();
159031
- init_normalize_hash();
159032
159097
  init_storage_memory_embeddings();
159033
159098
  DEFAULT_EMBEDDING_CONFIG = {
159034
159099
  provider: "local",
@@ -159075,7 +159140,7 @@ var init_storage_memory_fts = __esm(() => {
159075
159140
 
159076
159141
  // src/features/magic-context/memory/project-identity.ts
159077
159142
  import { execSync } from "node:child_process";
159078
- import { createHash as createHash2 } from "node:crypto";
159143
+ import { createHash as createHash3 } from "node:crypto";
159079
159144
  import path4 from "node:path";
159080
159145
  function getRootCommitHash(directory) {
159081
159146
  try {
@@ -159094,7 +159159,7 @@ function getRootCommitHash(directory) {
159094
159159
  }
159095
159160
  function directoryFallback(directory) {
159096
159161
  const canonical = path4.resolve(directory);
159097
- const hash2 = createHash2("md5").update(canonical).digest("hex").slice(0, 12);
159162
+ const hash2 = createHash3("md5").update(canonical).digest("hex").slice(0, 12);
159098
159163
  return `dir:${hash2}`;
159099
159164
  }
159100
159165
  function resolveProjectIdentity(directory) {
@@ -160144,6 +160209,9 @@ var init_migrations = __esm(async () => {
160144
160209
  embedding BLOB NOT NULL,
160145
160210
  model_id TEXT NOT NULL,
160146
160211
  created_at INTEGER NOT NULL,
160212
+ -- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
160213
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
160214
+ -- every connection and v12 cleans historical orphan rows.
160147
160215
  FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
160148
160216
  );
160149
160217
 
@@ -160283,15 +160351,38 @@ var init_migrations = __esm(async () => {
160283
160351
  db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
160284
160352
  }
160285
160353
  }
160354
+ },
160355
+ {
160356
+ version: 12,
160357
+ description: "Clean orphan rows from FK-cascade embedding tables",
160358
+ up: (db) => {
160359
+ const hasTable = (name2) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
160360
+ const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
160361
+ WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
160362
+ log(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
160363
+ const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
160364
+ WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
160365
+ log(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
160366
+ }
160367
+ },
160368
+ {
160369
+ version: 13,
160370
+ description: "Add pending_compaction_marker_state column for deferred marker drain",
160371
+ up: (db) => {
160372
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
160373
+ if (!cols.some((c) => c.name === "pending_compaction_marker_state")) {
160374
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
160375
+ }
160376
+ }
160286
160377
  }
160287
160378
  ];
160288
160379
  });
160289
160380
 
160290
160381
  // src/features/magic-context/tool-owner-backfill.ts
160291
160382
  import { existsSync as existsSync10 } from "node:fs";
160292
- import { join as join15 } from "node:path";
160383
+ import { join as join14 } from "node:path";
160293
160384
  function resolveOpencodeDbPath() {
160294
- return join15(getDataDir(), "opencode", "opencode.db");
160385
+ return join14(getDataDir(), "opencode", "opencode.db");
160295
160386
  }
160296
160387
  function ensureBackfillStateTable(db) {
160297
160388
  db.exec(`
@@ -160536,24 +160627,24 @@ var init_tool_owner_backfill = __esm(() => {
160536
160627
  });
160537
160628
 
160538
160629
  // src/features/magic-context/storage-db.ts
160539
- import { copyFileSync, cpSync, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "node:fs";
160540
- import { join as join16 } from "node:path";
160630
+ import { copyFileSync, cpSync, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "node:fs";
160631
+ import { join as join15 } from "node:path";
160541
160632
  function resolveDatabasePath() {
160542
160633
  const dbDir = getMagicContextStorageDir();
160543
- return { dbDir, dbPath: join16(dbDir, "context.db") };
160634
+ return { dbDir, dbPath: join15(dbDir, "context.db") };
160544
160635
  }
160545
160636
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
160546
160637
  if (existsSync11(targetDbPath))
160547
160638
  return;
160548
160639
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
160549
- const legacyDbPath = join16(legacyDir, "context.db");
160640
+ const legacyDbPath = join15(legacyDir, "context.db");
160550
160641
  if (!existsSync11(legacyDbPath))
160551
160642
  return;
160552
160643
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
160553
- mkdirSync4(targetDbDir, { recursive: true });
160644
+ mkdirSync5(targetDbDir, { recursive: true });
160554
160645
  for (const suffix of ["", "-wal", "-shm"]) {
160555
160646
  const src = `${legacyDbPath}${suffix}`;
160556
- const dst = join16(targetDbDir, `context.db${suffix}`);
160647
+ const dst = join15(targetDbDir, `context.db${suffix}`);
160557
160648
  if (existsSync11(src)) {
160558
160649
  try {
160559
160650
  copyFileSync(src, dst);
@@ -160562,8 +160653,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
160562
160653
  }
160563
160654
  }
160564
160655
  }
160565
- const legacyModelsDir = join16(legacyDir, "models");
160566
- const targetModelsDir = join16(targetDbDir, "models");
160656
+ const legacyModelsDir = join15(legacyDir, "models");
160657
+ const targetModelsDir = join15(targetDbDir, "models");
160567
160658
  if (existsSync11(legacyModelsDir) && !existsSync11(targetModelsDir)) {
160568
160659
  try {
160569
160660
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
@@ -160573,9 +160664,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
160573
160664
  }
160574
160665
  }
160575
160666
  function initializeDatabase(db) {
160667
+ db.exec("PRAGMA foreign_keys=ON");
160576
160668
  db.exec("PRAGMA journal_mode=WAL");
160577
160669
  db.exec("PRAGMA busy_timeout=5000");
160578
- db.exec("PRAGMA foreign_keys=ON");
160579
160670
  db.exec(`
160580
160671
  CREATE TABLE IF NOT EXISTS tags (
160581
160672
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -160673,6 +160764,9 @@ function initializeDatabase(db) {
160673
160764
  );
160674
160765
 
160675
160766
  CREATE TABLE IF NOT EXISTS memory_embeddings (
160767
+ -- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
160768
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
160769
+ -- every connection and v12 cleans historical orphan rows.
160676
160770
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
160677
160771
  embedding BLOB NOT NULL,
160678
160772
  model_id TEXT
@@ -160780,7 +160874,13 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
160780
160874
  system_prompt_hash TEXT DEFAULT '',
160781
160875
  memory_block_cache TEXT DEFAULT '',
160782
160876
  memory_block_count INTEGER DEFAULT 0,
160783
- memory_block_ids TEXT DEFAULT ''
160877
+ memory_block_ids TEXT DEFAULT '',
160878
+ -- pending_compaction_marker_state: intentionally NULLABLE without a
160879
+ -- default. Absence of a deferred marker is SQL NULL; presence is a
160880
+ -- valid JSON blob written via setPendingCompactionMarkerState.
160881
+ -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
160882
+ -- != empty-string defensively. Plan v6 section 3.
160883
+ pending_compaction_marker_state TEXT
160784
160884
  );
160785
160885
 
160786
160886
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -160868,6 +160968,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
160868
160968
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
160869
160969
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
160870
160970
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
160971
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
160871
160972
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
160872
160973
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
160873
160974
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -160957,7 +161058,7 @@ function openDatabase() {
160957
161058
  }
160958
161059
  try {
160959
161060
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
160960
- mkdirSync4(dbDir, { recursive: true });
161061
+ mkdirSync5(dbDir, { recursive: true });
160961
161062
  const db = new Database(dbPath);
160962
161063
  initializeDatabase(db);
160963
161064
  runMigrations(db);
@@ -161398,6 +161499,39 @@ function removeStrippedPlaceholderId(db, sessionId, messageId) {
161398
161499
  setStrippedPlaceholderIds(db, sessionId, ids);
161399
161500
  return true;
161400
161501
  }
161502
+ function isPendingCompactionMarker(value) {
161503
+ return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
161504
+ }
161505
+ function getPendingCompactionMarkerState(db, sessionId) {
161506
+ const row = db.prepare("SELECT pending_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
161507
+ const raw = row?.pending_compaction_marker_state;
161508
+ if (raw === null || raw === undefined || raw === "")
161509
+ return null;
161510
+ try {
161511
+ const parsed = JSON.parse(raw);
161512
+ if (isPendingCompactionMarker(parsed)) {
161513
+ return parsed;
161514
+ }
161515
+ } catch {}
161516
+ return null;
161517
+ }
161518
+ function setPendingCompactionMarkerState(db, sessionId, state) {
161519
+ ensureSessionMetaRow(db, sessionId);
161520
+ const blob = state ? stableStringify(state) : null;
161521
+ db.prepare("UPDATE session_meta SET pending_compaction_marker_state = ? WHERE session_id = ?").run(blob, sessionId);
161522
+ }
161523
+ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
161524
+ const expectedBlob = stableStringify(expected);
161525
+ const result = db.prepare(`UPDATE session_meta SET pending_compaction_marker_state = NULL
161526
+ WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
161527
+ return result.changes > 0;
161528
+ }
161529
+ function getSessionsWithPendingMarker(db) {
161530
+ const rows = db.prepare(`SELECT session_id FROM session_meta
161531
+ WHERE pending_compaction_marker_state IS NOT NULL
161532
+ AND pending_compaction_marker_state != ''`).all();
161533
+ return rows.map((r) => r.session_id);
161534
+ }
161401
161535
  var init_storage_meta_persisted = __esm(() => {
161402
161536
  init_logger();
161403
161537
  init_storage_meta_shared();
@@ -161925,12 +162059,12 @@ var init_storage = __esm(async () => {
161925
162059
  });
161926
162060
 
161927
162061
  // src/shared/models-dev-cache.ts
161928
- import { createHash as createHash3 } from "node:crypto";
162062
+ import { createHash as createHash4 } from "node:crypto";
161929
162063
  import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
161930
162064
  import { homedir as homedir8, platform as platform3 } from "node:os";
161931
- import { join as join17 } from "node:path";
162065
+ import { join as join16 } from "node:path";
161932
162066
  function hashFast(input) {
161933
- return createHash3("sha1").update(input).digest("hex");
162067
+ return createHash4("sha1").update(input).digest("hex");
161934
162068
  }
161935
162069
  function getModelsJsonPath() {
161936
162070
  const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
@@ -161939,15 +162073,15 @@ function getModelsJsonPath() {
161939
162073
  const cacheBase = getCacheDir();
161940
162074
  const source = process.env.OPENCODE_MODELS_URL?.trim();
161941
162075
  const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
161942
- return join17(cacheBase, "opencode", filename);
162076
+ return join16(cacheBase, "opencode", filename);
161943
162077
  }
161944
162078
  function getOpencodeConfigPath() {
161945
162079
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
161946
- const configDir = envDir ? envDir : platform3() === "win32" ? join17(homedir8(), ".config", "opencode") : join17(process.env.XDG_CONFIG_HOME || join17(homedir8(), ".config"), "opencode");
161947
- const jsonc = join17(configDir, "opencode.jsonc");
162080
+ const configDir = envDir ? envDir : platform3() === "win32" ? join16(homedir8(), ".config", "opencode") : join16(process.env.XDG_CONFIG_HOME || join16(homedir8(), ".config"), "opencode");
162081
+ const jsonc = join16(configDir, "opencode.jsonc");
161948
162082
  if (existsSync12(jsonc))
161949
162083
  return jsonc;
161950
- const json2 = join17(configDir, "opencode.json");
162084
+ const json2 = join16(configDir, "opencode.json");
161951
162085
  if (existsSync12(json2))
161952
162086
  return json2;
161953
162087
  return null;
@@ -162084,25 +162218,359 @@ __export(exports_rpc_notifications, {
162084
162218
  drainNotifications: () => drainNotifications
162085
162219
  });
162086
162220
  function pushNotification(type, payload, sessionId) {
162087
- queue2.push({ type, payload, sessionId });
162221
+ queue2.push({ id: nextNotificationId++, type, payload, sessionId });
162088
162222
  if (queue2.length > 100) {
162089
162223
  queue2 = queue2.slice(-50);
162090
162224
  }
162091
162225
  }
162092
- function drainNotifications() {
162226
+ function drainNotifications(lastReceivedId = 0) {
162093
162227
  lastDrainAt = Date.now();
162094
- const result = queue2;
162095
- queue2 = [];
162096
- return result;
162228
+ if (lastReceivedId > 0) {
162229
+ queue2 = queue2.filter((notification) => notification.id > lastReceivedId);
162230
+ }
162231
+ return [...queue2];
162097
162232
  }
162098
162233
  function isTuiConnected() {
162099
162234
  return lastDrainAt > 0 && Date.now() - lastDrainAt < TUI_CONNECTED_WINDOW_MS;
162100
162235
  }
162101
- var queue2, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
162236
+ var queue2, nextNotificationId = 1, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
162102
162237
  var init_rpc_notifications = __esm(() => {
162103
162238
  queue2 = [];
162104
162239
  });
162105
162240
 
162241
+ // src/features/magic-context/compaction-marker.ts
162242
+ import { join as join17 } from "node:path";
162243
+ function randomBase62(length) {
162244
+ const chars = [];
162245
+ for (let i = 0;i < length; i++) {
162246
+ chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
162247
+ }
162248
+ return chars.join("");
162249
+ }
162250
+ function generateId(prefix, timestampMs, counter = 0n) {
162251
+ const encoded = BigInt(timestampMs) * 0x1000n + counter;
162252
+ const hex3 = encoded.toString(16).padStart(14, "0");
162253
+ return `${prefix}_${hex3}${randomBase62(14)}`;
162254
+ }
162255
+ function generateMessageId(timestampMs, counter = 0n) {
162256
+ return generateId("msg", timestampMs, counter);
162257
+ }
162258
+ function generatePartId(timestampMs, counter = 0n) {
162259
+ return generateId("prt", timestampMs, counter);
162260
+ }
162261
+ function getOpenCodeDbPath3() {
162262
+ return join17(getDataDir(), "opencode", "opencode.db");
162263
+ }
162264
+ function isOpenCodeSchemaCompatible(db, dbPath) {
162265
+ if (cachedSchemaCompatible?.path === dbPath) {
162266
+ return cachedSchemaCompatible.compatible;
162267
+ }
162268
+ try {
162269
+ const messageCols = new Set(db.prepare("PRAGMA table_info(message)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
162270
+ const partCols = new Set(db.prepare("PRAGMA table_info(part)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
162271
+ const missingMessage = REQUIRED_MESSAGE_COLUMNS.filter((c) => !messageCols.has(c));
162272
+ const missingPart = REQUIRED_PART_COLUMNS.filter((c) => !partCols.has(c));
162273
+ if (missingMessage.length > 0 || missingPart.length > 0) {
162274
+ log(`[magic-context] compaction-marker: OpenCode DB schema missing required columns ` + `(message: [${missingMessage.join(", ")}], part: [${missingPart.join(", ")}]). ` + `Marker injection disabled for this process. ` + `This usually means OpenCode was updated and magic-context is out of date.`);
162275
+ cachedSchemaCompatible = { path: dbPath, compatible: false };
162276
+ return false;
162277
+ }
162278
+ cachedSchemaCompatible = { path: dbPath, compatible: true };
162279
+ return true;
162280
+ } catch (error51) {
162281
+ log(`[magic-context] compaction-marker: schema probe failed: ${error51 instanceof Error ? error51.message : String(error51)}. ` + `Marker injection disabled until next process restart.`);
162282
+ cachedSchemaCompatible = { path: dbPath, compatible: false };
162283
+ return false;
162284
+ }
162285
+ }
162286
+ function getWritableOpenCodeDb() {
162287
+ const dbPath = getOpenCodeDbPath3();
162288
+ if (cachedWriteDb?.path === dbPath) {
162289
+ return cachedWriteDb.db;
162290
+ }
162291
+ if (cachedWriteDb) {
162292
+ try {
162293
+ closeQuietly(cachedWriteDb.db);
162294
+ } catch {}
162295
+ }
162296
+ const db = new Database(dbPath);
162297
+ db.exec("PRAGMA journal_mode=WAL");
162298
+ db.exec("PRAGMA busy_timeout=5000");
162299
+ cachedWriteDb = { path: dbPath, db };
162300
+ return db;
162301
+ }
162302
+ function findBoundaryUserMessage(sessionId, endOrdinal) {
162303
+ const db = getWritableOpenCodeDb();
162304
+ const rows = db.prepare(`SELECT id, time_created, data
162305
+ FROM message
162306
+ WHERE session_id = ?
162307
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
162308
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
162309
+ ORDER BY time_created ASC, id ASC
162310
+ LIMIT ?`).all(sessionId, endOrdinal);
162311
+ let bestMatch = null;
162312
+ for (const row of rows) {
162313
+ try {
162314
+ const info = JSON.parse(row.data);
162315
+ if (info.role === "user") {
162316
+ bestMatch = { id: row.id, timeCreated: row.time_created };
162317
+ }
162318
+ } catch {}
162319
+ }
162320
+ return bestMatch;
162321
+ }
162322
+ function getOpenCodeMessageById(sessionId, messageId) {
162323
+ const db = getWritableOpenCodeDb();
162324
+ const row = db.prepare(`SELECT id FROM message WHERE session_id = ? AND id = ? LIMIT 1`).get(sessionId, messageId);
162325
+ return row ?? null;
162326
+ }
162327
+ function injectCompactionMarker(args) {
162328
+ const db = getWritableOpenCodeDb();
162329
+ if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath3())) {
162330
+ return null;
162331
+ }
162332
+ const boundary = findBoundaryUserMessage(args.sessionId, args.endOrdinal);
162333
+ if (!boundary) {
162334
+ log(`[magic-context] compaction-marker: no user message found at or before ordinal ${args.endOrdinal}`);
162335
+ return null;
162336
+ }
162337
+ const boundaryTime = boundary.timeCreated;
162338
+ const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
162339
+ const compactionPartId = generatePartId(boundaryTime, 1n);
162340
+ const summaryPartId = generatePartId(boundaryTime + 1, 2n);
162341
+ const summaryMsgData = JSON.stringify({
162342
+ role: "assistant",
162343
+ parentID: boundary.id,
162344
+ summary: true,
162345
+ finish: "stop",
162346
+ mode: "compaction",
162347
+ agent: "compaction",
162348
+ path: { cwd: args.directory, root: args.directory },
162349
+ cost: 0,
162350
+ tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
162351
+ modelID: "magic-context",
162352
+ providerID: "magic-context",
162353
+ time: { created: boundaryTime + 1 }
162354
+ });
162355
+ try {
162356
+ db.transaction(() => {
162357
+ db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
162358
+ db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
162359
+ db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
162360
+ })();
162361
+ log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
162362
+ return {
162363
+ boundaryMessageId: boundary.id,
162364
+ summaryMessageId: summaryMsgId,
162365
+ compactionPartId,
162366
+ summaryPartId
162367
+ };
162368
+ } catch (error51) {
162369
+ log(`[magic-context] compaction-marker: injection failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
162370
+ return null;
162371
+ }
162372
+ }
162373
+ function removeCompactionMarker(state) {
162374
+ try {
162375
+ const db = getWritableOpenCodeDb();
162376
+ db.transaction(() => {
162377
+ db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
162378
+ db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
162379
+ db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
162380
+ })();
162381
+ return true;
162382
+ } catch (error51) {
162383
+ log(`[magic-context] compaction-marker: removal failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
162384
+ return false;
162385
+ }
162386
+ }
162387
+ var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null, REQUIRED_MESSAGE_COLUMNS, REQUIRED_PART_COLUMNS, cachedSchemaCompatible = null;
162388
+ var init_compaction_marker = __esm(async () => {
162389
+ init_data_path();
162390
+ init_logger();
162391
+ await init_sqlite();
162392
+ REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
162393
+ REQUIRED_PART_COLUMNS = [
162394
+ "id",
162395
+ "message_id",
162396
+ "session_id",
162397
+ "time_created",
162398
+ "time_updated",
162399
+ "data"
162400
+ ];
162401
+ });
162402
+
162403
+ // src/hooks/magic-context/compaction-marker-manager.ts
162404
+ import { join as join18 } from "node:path";
162405
+ function validatePendingTarget(db, sessionId, pending) {
162406
+ const ocMessage = getOpenCodeMessageById(sessionId, pending.endMessageId);
162407
+ if (!ocMessage) {
162408
+ return "compartment-removed";
162409
+ }
162410
+ const compartments = getCompartmentsByEndMessageId(db, sessionId, pending.endMessageId);
162411
+ if (compartments.length === 0) {
162412
+ return "compartment-removed";
162413
+ }
162414
+ if (compartments.length > 1) {
162415
+ log(`[magic-context][${sessionId}] WARNING: ${compartments.length} compartments share endMessageId=${pending.endMessageId} — schema invariant violated; treating as stale`);
162416
+ return "compartment-removed";
162417
+ }
162418
+ const compartment = compartments[0];
162419
+ if (compartment.endMessage !== pending.ordinal) {
162420
+ return "target-superseded";
162421
+ }
162422
+ return "ok";
162423
+ }
162424
+ function applyDeferredCompactionMarker(db, sessionId, pending, directory) {
162425
+ try {
162426
+ const validation = validatePendingTarget(db, sessionId, pending);
162427
+ if (validation !== "ok") {
162428
+ sessionLog(sessionId, `compaction-marker drain: stale-skip (${validation}) for ordinal ${pending.ordinal} endMessageId=${pending.endMessageId}`);
162429
+ return { kind: "stale-skip", reason: validation };
162430
+ }
162431
+ const existing = getPersistedCompactionMarkerState(db, sessionId);
162432
+ if (existing && existing.boundaryOrdinal >= pending.ordinal) {
162433
+ return { kind: "already-current" };
162434
+ }
162435
+ if (existing) {
162436
+ const removed = removeCompactionMarker(existing);
162437
+ if (!removed) {
162438
+ return {
162439
+ kind: "retryable-failure",
162440
+ error: new Error(`failed to remove old compaction marker at ordinal ${existing.boundaryOrdinal}`)
162441
+ };
162442
+ }
162443
+ sessionLog(sessionId, `compaction-marker drain: removed old boundary at ordinal ${existing.boundaryOrdinal}, advancing to ${pending.ordinal}`);
162444
+ }
162445
+ const result = injectCompactionMarker({
162446
+ sessionId,
162447
+ endOrdinal: pending.ordinal,
162448
+ summaryText: MARKER_SUMMARY_TEXT,
162449
+ directory: directory ?? process.cwd()
162450
+ });
162451
+ if (!result) {
162452
+ return {
162453
+ kind: "retryable-failure",
162454
+ error: new Error(`injectCompactionMarker returned null for ordinal ${pending.ordinal}; will retry`)
162455
+ };
162456
+ }
162457
+ setPersistedCompactionMarkerState(db, sessionId, {
162458
+ ...result,
162459
+ boundaryOrdinal: pending.ordinal
162460
+ });
162461
+ sessionLog(sessionId, `compaction-marker drain: applied at ordinal ${pending.ordinal}, boundary user msg ${result.boundaryMessageId}`);
162462
+ return { kind: "applied", markerOrdinal: pending.ordinal };
162463
+ } catch (err) {
162464
+ const error51 = err instanceof Error ? err : new Error(String(err));
162465
+ sessionLog(sessionId, `compaction-marker drain: retryable failure for ordinal ${pending.ordinal}:`, error51);
162466
+ return { kind: "retryable-failure", error: error51 };
162467
+ }
162468
+ }
162469
+ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
162470
+ const existing = getPersistedCompactionMarkerState(db, sessionId);
162471
+ if (existing) {
162472
+ if (existing.boundaryOrdinal === lastCompartmentEnd) {
162473
+ return;
162474
+ }
162475
+ try {
162476
+ removeCompactionMarker(existing);
162477
+ setPersistedCompactionMarkerState(db, sessionId, null);
162478
+ sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
162479
+ } catch (error51) {
162480
+ sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error51);
162481
+ }
162482
+ }
162483
+ const result = injectCompactionMarker({
162484
+ sessionId,
162485
+ endOrdinal: lastCompartmentEnd,
162486
+ summaryText: MARKER_SUMMARY_TEXT,
162487
+ directory: directory ?? process.cwd()
162488
+ });
162489
+ if (result) {
162490
+ setPersistedCompactionMarkerState(db, sessionId, {
162491
+ ...result,
162492
+ boundaryOrdinal: lastCompartmentEnd
162493
+ });
162494
+ sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
162495
+ }
162496
+ }
162497
+ function removeCompactionMarkerForSession(db, sessionId) {
162498
+ const existing = getPersistedCompactionMarkerState(db, sessionId);
162499
+ if (existing) {
162500
+ try {
162501
+ removeCompactionMarker(existing);
162502
+ setPersistedCompactionMarkerState(db, sessionId, null);
162503
+ sessionLog(sessionId, "compaction-marker: removed on session cleanup");
162504
+ } catch (error51) {
162505
+ setPersistedCompactionMarkerState(db, sessionId, null);
162506
+ sessionLog(sessionId, "compaction-marker: removal failed during session cleanup, cleared persisted state:", error51);
162507
+ }
162508
+ }
162509
+ }
162510
+ function checkCompactionMarkerConsistency(db) {
162511
+ const opencodeDbPath = join18(getDataDir(), "opencode", "opencode.db");
162512
+ let opencodeDb;
162513
+ try {
162514
+ opencodeDb = new Database(opencodeDbPath, { readonly: true });
162515
+ } catch (error51) {
162516
+ log(`[magic-context] compaction-marker consistency check skipped: ${error51 instanceof Error ? error51.message : String(error51)}`);
162517
+ return;
162518
+ }
162519
+ try {
162520
+ const persistedRows = db.prepare("SELECT session_id, compaction_marker_state FROM session_meta WHERE compaction_marker_state IS NOT NULL AND compaction_marker_state != ''").all();
162521
+ if (persistedRows.length === 0)
162522
+ return;
162523
+ const checkMessage = opencodeDb.prepare("SELECT 1 FROM message WHERE id = ? LIMIT 1");
162524
+ const checkPart = opencodeDb.prepare("SELECT 1 FROM part WHERE id = ? LIMIT 1");
162525
+ let reconciledCount = 0;
162526
+ for (const row of persistedRows) {
162527
+ const state = getPersistedCompactionMarkerState(db, row.session_id);
162528
+ if (!state)
162529
+ continue;
162530
+ const boundaryExists = checkMessage.get(state.boundaryMessageId) !== null;
162531
+ const summaryMessageExists = checkMessage.get(state.summaryMessageId) !== null;
162532
+ const compactionPartExists = checkPart.get(state.compactionPartId) !== null;
162533
+ const summaryPartExists = checkPart.get(state.summaryPartId) !== null;
162534
+ const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
162535
+ if (allPresent)
162536
+ continue;
162537
+ let removedOk = false;
162538
+ try {
162539
+ removedOk = removeCompactionMarker(state);
162540
+ } catch (error51) {
162541
+ sessionLog(row.session_id, "compaction-marker consistency: partial cleanup of half-written marker failed:", error51);
162542
+ }
162543
+ if (removedOk) {
162544
+ setPersistedCompactionMarkerState(db, row.session_id, null);
162545
+ sessionLog(row.session_id, `compaction-marker consistency: cleared orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); next publication will re-inject`);
162546
+ reconciledCount++;
162547
+ } else {
162548
+ sessionLog(row.session_id, `compaction-marker consistency: cleanup failed for orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); will retry on next startup`);
162549
+ }
162550
+ }
162551
+ if (reconciledCount > 0) {
162552
+ log(`[magic-context] compaction-marker consistency: reconciled ${reconciledCount} session(s) with orphaned marker state at startup`);
162553
+ }
162554
+ } catch (error51) {
162555
+ log(`[magic-context] compaction-marker consistency check failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
162556
+ } finally {
162557
+ try {
162558
+ closeQuietly(opencodeDb);
162559
+ } catch {}
162560
+ }
162561
+ }
162562
+ var MARKER_SUMMARY_TEXT = "[Compacted by magic-context — session history is managed by the plugin]";
162563
+ var init_compaction_marker_manager = __esm(async () => {
162564
+ init_compartment_storage();
162565
+ init_storage_meta_persisted();
162566
+ init_data_path();
162567
+ init_logger();
162568
+ await __promiseAll([
162569
+ init_compaction_marker(),
162570
+ init_sqlite()
162571
+ ]);
162572
+ });
162573
+
162106
162574
  // src/hooks/magic-context/compartment-parser.ts
162107
162575
  function parseCompartmentOutput(text) {
162108
162576
  const compartments = [];
@@ -162323,9 +162791,11 @@ var init_compartment_runner_validation = __esm(async () => {
162323
162791
  });
162324
162792
 
162325
162793
  // src/hooks/magic-context/compartment-runner-historian.ts
162326
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
162327
- import { tmpdir as tmpdir2 } from "node:os";
162328
- import { join as join18 } from "node:path";
162794
+ import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
162795
+ import { join as join19 } from "node:path";
162796
+ function historianResponseDumpDir(directory) {
162797
+ return getProjectMagicContextHistorianDir(directory);
162798
+ }
162329
162799
  async function runValidatedHistorianPass(args) {
162330
162800
  const firstRun = await runHistorianPrompt({
162331
162801
  ...args,
@@ -162476,7 +162946,7 @@ async function runHistorianPrompt(args) {
162476
162946
  if (!result) {
162477
162947
  return { ok: false, error: "Historian returned no assistant output." };
162478
162948
  }
162479
- const dumpPath = dumpHistorianResponse(parentSessionId, dumpLabel ?? "historian-response", result);
162949
+ const dumpPath = dumpHistorianResponse(parentSessionId, sessionDirectory, dumpLabel ?? "historian-response", result);
162480
162950
  return { ok: true, result, dumpPath };
162481
162951
  } catch (modelError) {
162482
162952
  const desc = describeError(modelError);
@@ -162568,12 +163038,13 @@ function cleanupHistorianDump(sessionId, dumpPath) {
162568
163038
  });
162569
163039
  }
162570
163040
  }
162571
- function dumpHistorianResponse(sessionId, label, text) {
163041
+ function dumpHistorianResponse(sessionId, directory, label, text) {
162572
163042
  try {
162573
- mkdirSync5(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
163043
+ const dumpDir = historianResponseDumpDir(directory);
163044
+ mkdirSync6(dumpDir, { recursive: true });
162574
163045
  const safeSessionId = sanitizeDumpName(sessionId);
162575
163046
  const safeLabel = sanitizeDumpName(label);
162576
- const dumpPath = join18(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
163047
+ const dumpPath = join19(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
162577
163048
  writeFileSync5(dumpPath, text, "utf8");
162578
163049
  sessionLog(sessionId, "compartment agent: historian response dumped", {
162579
163050
  label,
@@ -162591,14 +163062,14 @@ function dumpHistorianResponse(sessionId, label, text) {
162591
163062
  function sanitizeDumpName(value) {
162592
163063
  return value.replace(/[^a-zA-Z0-9._-]/g, "-");
162593
163064
  }
162594
- var HISTORIAN_RESPONSE_DUMP_DIR, MAX_HISTORIAN_RETRIES = 2;
163065
+ var MAX_HISTORIAN_RETRIES = 2;
162595
163066
  var init_compartment_runner_historian = __esm(async () => {
162596
163067
  init_magic_context();
162597
163068
  init_shared();
162598
163069
  init_assistant_message_extractor();
163070
+ init_data_path();
162599
163071
  init_compartment_prompt();
162600
163072
  await init_compartment_runner_validation();
162601
- HISTORIAN_RESPONSE_DUMP_DIR = join18(tmpdir2(), "magic-context-historian");
162602
163073
  });
162603
163074
 
162604
163075
  // src/hooks/magic-context/compartment-runner-state-xml.ts
@@ -162900,16 +163371,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
162900
163371
  function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
162901
163372
  const cached2 = injectionCache.get(sessionId);
162902
163373
  if (!isCacheBusting && cached2) {
162903
- if (cached2.compartmentEndMessageId.length > 0) {
162904
- const cutoffIndex2 = messages.findIndex((message) => message.info.id === cached2.compartmentEndMessageId);
162905
- if (cutoffIndex2 >= 0) {
162906
- const remaining = messages.slice(cutoffIndex2 + 1);
162907
- messages.splice(0, messages.length, ...remaining);
162908
- } else {
162909
- sessionLog(sessionId, `compartment injection: cached boundary ${cached2.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
163374
+ if (cached2.kind === "empty") {
163375
+ return null;
163376
+ }
163377
+ const prepared = cached2.injection;
163378
+ if (prepared.compartmentEndMessageId === null) {
163379
+ sessionLog(sessionId, "compartment injection cache in degraded mode (null boundary), forcing rebuild");
163380
+ } else {
163381
+ if (prepared.compartmentEndMessageId.length > 0) {
163382
+ const cutoffIndex2 = messages.findIndex((message) => message.info.id === prepared.compartmentEndMessageId);
163383
+ if (cutoffIndex2 >= 0) {
163384
+ const remaining = messages.slice(cutoffIndex2 + 1);
163385
+ messages.splice(0, messages.length, ...remaining);
163386
+ } else {
163387
+ sessionLog(sessionId, `compartment injection: cached boundary ${prepared.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
163388
+ }
162910
163389
  }
163390
+ return { ...prepared, rebuiltFromDb: false };
162911
163391
  }
162912
- return cached2;
162913
163392
  }
162914
163393
  const compartments = getCompartments(db, sessionId);
162915
163394
  const facts = getSessionFacts(db, sessionId);
@@ -162941,7 +163420,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
162941
163420
  }
162942
163421
  }
162943
163422
  if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
162944
- injectionCache.delete(sessionId);
163423
+ injectionCache.set(sessionId, {
163424
+ kind: "empty",
163425
+ compartmentEndMessageId: "",
163426
+ renderedBytes: 0
163427
+ });
162945
163428
  return null;
162946
163429
  }
162947
163430
  let dateRanges;
@@ -162974,9 +163457,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
162974
163457
  compartmentCount: 0,
162975
163458
  skippedVisibleMessages: 0,
162976
163459
  factCount: facts.length,
162977
- memoryCount
163460
+ memoryCount,
163461
+ rebuiltFromDb: true
162978
163462
  };
162979
- injectionCache.set(sessionId, result2);
163463
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
162980
163464
  return result2;
162981
163465
  }
162982
163466
  const lastCompartment = compartments[compartments.length - 1];
@@ -162994,9 +163478,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
162994
163478
  compartmentCount: compartments.length,
162995
163479
  skippedVisibleMessages: 0,
162996
163480
  factCount: facts.length,
162997
- memoryCount
163481
+ memoryCount,
163482
+ rebuiltFromDb: true
162998
163483
  };
162999
- injectionCache.set(sessionId, result2);
163484
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
163000
163485
  return result2;
163001
163486
  }
163002
163487
  let skippedVisibleMessages = 0;
@@ -163005,17 +163490,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
163005
163490
  skippedVisibleMessages = cutoffIndex + 1;
163006
163491
  const remaining = messages.slice(cutoffIndex + 1);
163007
163492
  messages.splice(0, messages.length, ...remaining);
163493
+ } else {
163494
+ sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
163008
163495
  }
163009
163496
  const result = {
163010
163497
  block,
163011
163498
  compartmentEndMessage: lastEnd,
163012
- compartmentEndMessageId: lastEndMessageId,
163499
+ compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
163013
163500
  compartmentCount: compartments.length,
163014
163501
  skippedVisibleMessages,
163015
163502
  factCount: facts.length,
163016
- memoryCount
163503
+ memoryCount,
163504
+ rebuiltFromDb: true
163017
163505
  };
163018
- injectionCache.set(sessionId, result);
163506
+ injectionCache.set(sessionId, { kind: "populated", injection: result });
163019
163507
  return result;
163020
163508
  }
163021
163509
  function renderCompartmentInjection(sessionId, messages, prepared) {
@@ -163307,9 +163795,18 @@ async function executePartialRecompInternal(deps, range) {
163307
163795
  }
163308
163796
  setRecompPartialRange(db, sessionId, null);
163309
163797
  clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
163310
- clearInjectionCache(sessionId);
163311
- deps.onInjectionCacheCleared?.(sessionId);
163798
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
163799
+ clearInjectionCache(sessionId);
163800
+ }
163801
+ deps.onCompartmentStatePublished?.(sessionId);
163312
163802
  const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
163803
+ if (deps.experimentalCompactionMarkers && lastEnd > 0) {
163804
+ updateCompactionMarkerAfterPublication(db, sessionId, lastEnd, deps.directory);
163805
+ const stalePending = getPendingCompactionMarkerState(db, sessionId);
163806
+ if (stalePending) {
163807
+ clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
163808
+ }
163809
+ }
163313
163810
  return { compartmentCount: merged.length, lastEndMessage: lastEnd };
163314
163811
  };
163315
163812
  const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
@@ -163509,6 +164006,7 @@ var init_compartment_runner_partial_recomp = __esm(async () => {
163509
164006
  init_send_session_notification();
163510
164007
  await __promiseAll([
163511
164008
  init_storage_meta(),
164009
+ init_compaction_marker_manager(),
163512
164010
  init_compartment_runner_historian(),
163513
164011
  init_compartment_runner_validation(),
163514
164012
  init_inject_compartments(),
@@ -163577,269 +164075,6 @@ var init_derive_budgets = __esm(() => {
163577
164075
  init_models_dev_cache();
163578
164076
  });
163579
164077
 
163580
- // src/features/magic-context/compaction-marker.ts
163581
- import { join as join19 } from "node:path";
163582
- function randomBase62(length) {
163583
- const chars = [];
163584
- for (let i = 0;i < length; i++) {
163585
- chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
163586
- }
163587
- return chars.join("");
163588
- }
163589
- function generateId(prefix, timestampMs, counter = 0n) {
163590
- const encoded = BigInt(timestampMs) * 0x1000n + counter;
163591
- const hex3 = encoded.toString(16).padStart(14, "0");
163592
- return `${prefix}_${hex3}${randomBase62(14)}`;
163593
- }
163594
- function generateMessageId(timestampMs, counter = 0n) {
163595
- return generateId("msg", timestampMs, counter);
163596
- }
163597
- function generatePartId(timestampMs, counter = 0n) {
163598
- return generateId("prt", timestampMs, counter);
163599
- }
163600
- function getOpenCodeDbPath3() {
163601
- return join19(getDataDir(), "opencode", "opencode.db");
163602
- }
163603
- function isOpenCodeSchemaCompatible(db, dbPath) {
163604
- if (cachedSchemaCompatible?.path === dbPath) {
163605
- return cachedSchemaCompatible.compatible;
163606
- }
163607
- try {
163608
- const messageCols = new Set(db.prepare("PRAGMA table_info(message)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
163609
- const partCols = new Set(db.prepare("PRAGMA table_info(part)").all().map((r) => r.name ?? "").filter((n) => n.length > 0));
163610
- const missingMessage = REQUIRED_MESSAGE_COLUMNS.filter((c) => !messageCols.has(c));
163611
- const missingPart = REQUIRED_PART_COLUMNS.filter((c) => !partCols.has(c));
163612
- if (missingMessage.length > 0 || missingPart.length > 0) {
163613
- log(`[magic-context] compaction-marker: OpenCode DB schema missing required columns ` + `(message: [${missingMessage.join(", ")}], part: [${missingPart.join(", ")}]). ` + `Marker injection disabled for this process. ` + `This usually means OpenCode was updated and magic-context is out of date.`);
163614
- cachedSchemaCompatible = { path: dbPath, compatible: false };
163615
- return false;
163616
- }
163617
- cachedSchemaCompatible = { path: dbPath, compatible: true };
163618
- return true;
163619
- } catch (error51) {
163620
- log(`[magic-context] compaction-marker: schema probe failed: ${error51 instanceof Error ? error51.message : String(error51)}. ` + `Marker injection disabled until next process restart.`);
163621
- cachedSchemaCompatible = { path: dbPath, compatible: false };
163622
- return false;
163623
- }
163624
- }
163625
- function getWritableOpenCodeDb() {
163626
- const dbPath = getOpenCodeDbPath3();
163627
- if (cachedWriteDb?.path === dbPath) {
163628
- return cachedWriteDb.db;
163629
- }
163630
- if (cachedWriteDb) {
163631
- try {
163632
- closeQuietly(cachedWriteDb.db);
163633
- } catch {}
163634
- }
163635
- const db = new Database(dbPath);
163636
- db.exec("PRAGMA journal_mode=WAL");
163637
- db.exec("PRAGMA busy_timeout=5000");
163638
- cachedWriteDb = { path: dbPath, db };
163639
- return db;
163640
- }
163641
- function findBoundaryUserMessage(sessionId, endOrdinal) {
163642
- const db = getWritableOpenCodeDb();
163643
- const rows = db.prepare(`SELECT id, time_created, data
163644
- FROM message
163645
- WHERE session_id = ?
163646
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
163647
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
163648
- ORDER BY time_created ASC, id ASC
163649
- LIMIT ?`).all(sessionId, endOrdinal);
163650
- let bestMatch = null;
163651
- for (const row of rows) {
163652
- try {
163653
- const info = JSON.parse(row.data);
163654
- if (info.role === "user") {
163655
- bestMatch = { id: row.id, timeCreated: row.time_created };
163656
- }
163657
- } catch {}
163658
- }
163659
- return bestMatch;
163660
- }
163661
- function injectCompactionMarker(args) {
163662
- const db = getWritableOpenCodeDb();
163663
- if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath3())) {
163664
- return null;
163665
- }
163666
- const boundary = findBoundaryUserMessage(args.sessionId, args.endOrdinal);
163667
- if (!boundary) {
163668
- log(`[magic-context] compaction-marker: no user message found at or before ordinal ${args.endOrdinal}`);
163669
- return null;
163670
- }
163671
- const boundaryTime = boundary.timeCreated;
163672
- const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
163673
- const compactionPartId = generatePartId(boundaryTime, 1n);
163674
- const summaryPartId = generatePartId(boundaryTime + 1, 2n);
163675
- const summaryMsgData = JSON.stringify({
163676
- role: "assistant",
163677
- parentID: boundary.id,
163678
- summary: true,
163679
- finish: "stop",
163680
- mode: "compaction",
163681
- agent: "compaction",
163682
- path: { cwd: args.directory, root: args.directory },
163683
- cost: 0,
163684
- tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
163685
- modelID: "magic-context",
163686
- providerID: "magic-context",
163687
- time: { created: boundaryTime + 1 }
163688
- });
163689
- try {
163690
- db.transaction(() => {
163691
- db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
163692
- db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
163693
- db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
163694
- })();
163695
- log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
163696
- return {
163697
- boundaryMessageId: boundary.id,
163698
- summaryMessageId: summaryMsgId,
163699
- compactionPartId,
163700
- summaryPartId
163701
- };
163702
- } catch (error51) {
163703
- log(`[magic-context] compaction-marker: injection failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
163704
- return null;
163705
- }
163706
- }
163707
- function removeCompactionMarker(state) {
163708
- try {
163709
- const db = getWritableOpenCodeDb();
163710
- db.transaction(() => {
163711
- db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
163712
- db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
163713
- db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
163714
- })();
163715
- return true;
163716
- } catch (error51) {
163717
- log(`[magic-context] compaction-marker: removal failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
163718
- return false;
163719
- }
163720
- }
163721
- var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null, REQUIRED_MESSAGE_COLUMNS, REQUIRED_PART_COLUMNS, cachedSchemaCompatible = null;
163722
- var init_compaction_marker = __esm(async () => {
163723
- init_data_path();
163724
- init_logger();
163725
- await init_sqlite();
163726
- REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
163727
- REQUIRED_PART_COLUMNS = [
163728
- "id",
163729
- "message_id",
163730
- "session_id",
163731
- "time_created",
163732
- "time_updated",
163733
- "data"
163734
- ];
163735
- });
163736
-
163737
- // src/hooks/magic-context/compaction-marker-manager.ts
163738
- import { join as join20 } from "node:path";
163739
- function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
163740
- const existing = getPersistedCompactionMarkerState(db, sessionId);
163741
- if (existing) {
163742
- if (existing.boundaryOrdinal === lastCompartmentEnd) {
163743
- return;
163744
- }
163745
- try {
163746
- removeCompactionMarker(existing);
163747
- setPersistedCompactionMarkerState(db, sessionId, null);
163748
- sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
163749
- } catch (error51) {
163750
- sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error51);
163751
- }
163752
- }
163753
- const result = injectCompactionMarker({
163754
- sessionId,
163755
- endOrdinal: lastCompartmentEnd,
163756
- summaryText: MARKER_SUMMARY_TEXT,
163757
- directory: directory ?? process.cwd()
163758
- });
163759
- if (result) {
163760
- setPersistedCompactionMarkerState(db, sessionId, {
163761
- ...result,
163762
- boundaryOrdinal: lastCompartmentEnd
163763
- });
163764
- sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
163765
- }
163766
- }
163767
- function removeCompactionMarkerForSession(db, sessionId) {
163768
- const existing = getPersistedCompactionMarkerState(db, sessionId);
163769
- if (existing) {
163770
- try {
163771
- removeCompactionMarker(existing);
163772
- setPersistedCompactionMarkerState(db, sessionId, null);
163773
- sessionLog(sessionId, "compaction-marker: removed on session cleanup");
163774
- } catch (error51) {
163775
- setPersistedCompactionMarkerState(db, sessionId, null);
163776
- sessionLog(sessionId, "compaction-marker: removal failed during session cleanup, cleared persisted state:", error51);
163777
- }
163778
- }
163779
- }
163780
- function checkCompactionMarkerConsistency(db) {
163781
- const opencodeDbPath = join20(getDataDir(), "opencode", "opencode.db");
163782
- let opencodeDb;
163783
- try {
163784
- opencodeDb = new Database(opencodeDbPath, { readonly: true });
163785
- } catch (error51) {
163786
- log(`[magic-context] compaction-marker consistency check skipped: ${error51 instanceof Error ? error51.message : String(error51)}`);
163787
- return;
163788
- }
163789
- try {
163790
- const persistedRows = db.prepare("SELECT session_id, compaction_marker_state FROM session_meta WHERE compaction_marker_state IS NOT NULL AND compaction_marker_state != ''").all();
163791
- if (persistedRows.length === 0)
163792
- return;
163793
- const checkMessage = opencodeDb.prepare("SELECT 1 FROM message WHERE id = ? LIMIT 1");
163794
- const checkPart = opencodeDb.prepare("SELECT 1 FROM part WHERE id = ? LIMIT 1");
163795
- let reconciledCount = 0;
163796
- for (const row of persistedRows) {
163797
- const state = getPersistedCompactionMarkerState(db, row.session_id);
163798
- if (!state)
163799
- continue;
163800
- const boundaryExists = checkMessage.get(state.boundaryMessageId) !== null;
163801
- const summaryMessageExists = checkMessage.get(state.summaryMessageId) !== null;
163802
- const compactionPartExists = checkPart.get(state.compactionPartId) !== null;
163803
- const summaryPartExists = checkPart.get(state.summaryPartId) !== null;
163804
- const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
163805
- if (allPresent)
163806
- continue;
163807
- let removedOk = false;
163808
- try {
163809
- removedOk = removeCompactionMarker(state);
163810
- } catch (error51) {
163811
- sessionLog(row.session_id, "compaction-marker consistency: partial cleanup of half-written marker failed:", error51);
163812
- }
163813
- if (removedOk) {
163814
- setPersistedCompactionMarkerState(db, row.session_id, null);
163815
- sessionLog(row.session_id, `compaction-marker consistency: cleared orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); next publication will re-inject`);
163816
- reconciledCount++;
163817
- } else {
163818
- sessionLog(row.session_id, `compaction-marker consistency: cleanup failed for orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); will retry on next startup`);
163819
- }
163820
- }
163821
- if (reconciledCount > 0) {
163822
- log(`[magic-context] compaction-marker consistency: reconciled ${reconciledCount} session(s) with orphaned marker state at startup`);
163823
- }
163824
- } catch (error51) {
163825
- log(`[magic-context] compaction-marker consistency check failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
163826
- } finally {
163827
- try {
163828
- closeQuietly(opencodeDb);
163829
- } catch {}
163830
- }
163831
- }
163832
- var MARKER_SUMMARY_TEXT = "[Compacted by magic-context — session history is managed by the plugin]";
163833
- var init_compaction_marker_manager = __esm(async () => {
163834
- init_storage_meta_persisted();
163835
- init_data_path();
163836
- init_logger();
163837
- await __promiseAll([
163838
- init_compaction_marker(),
163839
- init_sqlite()
163840
- ]);
163841
- });
163842
-
163843
164078
  // src/hooks/magic-context/note-nudger.ts
163844
164079
  function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
163845
164080
  return lastDeliveredAt.get(sessionId) ?? 0;
@@ -164165,15 +164400,15 @@ var init_caveman = __esm(() => {
164165
164400
  });
164166
164401
 
164167
164402
  // src/hooks/magic-context/historian-state-file.ts
164168
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
164169
- import { tmpdir as tmpdir3 } from "node:os";
164170
- import { join as join21 } from "node:path";
164171
- function maybeWriteHistorianStateFile(sessionId, existingState) {
164403
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
164404
+ import { join as join20 } from "node:path";
164405
+ function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
164172
164406
  if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
164173
164407
  return;
164174
164408
  try {
164175
- mkdirSync6(HISTORIAN_STATE_DIR, { recursive: true });
164176
- const path5 = join21(HISTORIAN_STATE_DIR, `state-${sessionId}-${Date.now()}.xml`);
164409
+ const dir = getProjectMagicContextHistorianDir(directory);
164410
+ mkdirSync7(dir, { recursive: true });
164411
+ const path5 = join20(dir, `state-${sessionId}-${Date.now()}.xml`);
164177
164412
  writeFileSync6(path5, existingState, "utf8");
164178
164413
  return path5;
164179
164414
  } catch {
@@ -164187,9 +164422,9 @@ function cleanupHistorianStateFile(path5) {
164187
164422
  unlinkSync2(path5);
164188
164423
  } catch {}
164189
164424
  }
164190
- var HISTORIAN_STATE_INLINE_THRESHOLD = 30000, HISTORIAN_STATE_DIR;
164425
+ var HISTORIAN_STATE_INLINE_THRESHOLD = 30000;
164191
164426
  var init_historian_state_file = __esm(() => {
164192
- HISTORIAN_STATE_DIR = join21(tmpdir3(), "magic-context-historian");
164427
+ init_data_path();
164193
164428
  });
164194
164429
 
164195
164430
  // src/features/magic-context/memory/embedding-backfill.ts
@@ -164801,7 +165036,7 @@ No new compartments or facts were written.`);
164801
165036
  const existingState = priorCompartments.length > 0 || priorFacts.length > 0 ? buildExistingStateXml(priorCompartments, priorFacts, memoryBlock) : memoryBlock ? `${memoryBlock}
164802
165037
 
164803
165038
  This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
164804
- stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
165039
+ stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, directory);
164805
165040
  if (stateFilePath) {
164806
165041
  sessionLog(sessionId, `historian: existing state offloaded to file (${existingState.length} chars) → ${stateFilePath}`);
164807
165042
  }
@@ -164849,21 +165084,36 @@ Historian returned compartments that made no forward progress beyond raw message
164849
165084
  No new compartments or facts were written. Check the historian model/output and try again.`);
164850
165085
  return;
164851
165086
  }
165087
+ const deferMarkerApplication = deps.preserveInjectionCacheUntilConsumed === true && deps.experimentalCompactionMarkers === true;
165088
+ const lastCompartmentEnd = lastNewEnd;
165089
+ const lastNewEndMessageId = newCompartments[newCompartments.length - 1]?.endMessageId;
164852
165090
  db.transaction(() => {
164853
165091
  appendCompartments(db, sessionId, newCompartments);
164854
165092
  replaceSessionFacts(db, sessionId, validatedPass.facts ?? []);
164855
165093
  clearHistorianFailureState(db, sessionId);
164856
165094
  clearEmergencyRecovery(db, sessionId);
165095
+ if (deferMarkerApplication && lastNewEndMessageId) {
165096
+ setPendingCompactionMarkerState(db, sessionId, {
165097
+ ordinal: lastCompartmentEnd,
165098
+ endMessageId: lastNewEndMessageId,
165099
+ publishedAt: Date.now()
165100
+ });
165101
+ }
164857
165102
  })();
164858
- clearInjectionCache(sessionId);
164859
- deps.onInjectionCacheCleared?.(sessionId);
165103
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
165104
+ clearInjectionCache(sessionId);
165105
+ }
165106
+ deps.onCompartmentStatePublished?.(sessionId);
164860
165107
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
164861
165108
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), validatedPass.facts ?? []);
164862
165109
  }
164863
- const lastCompartmentEnd = lastNewEnd;
164864
165110
  queueDropsForCompartmentalizedMessages(db, sessionId, lastCompartmentEnd);
164865
165111
  if (deps.experimentalCompactionMarkers) {
164866
- updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, sessionDirectory);
165112
+ if (deferMarkerApplication) {
165113
+ deps.onDeferredMarkerPending?.(sessionId);
165114
+ } else {
165115
+ updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, sessionDirectory);
165116
+ }
164867
165117
  }
164868
165118
  if (deps.historyBudgetTokens && deps.historyBudgetTokens > 0) {
164869
165119
  await runCompressionPassIfNeeded({
@@ -164968,8 +165218,10 @@ async function executeContextRecompInternal(deps) {
164968
165218
  if (!promoted2)
164969
165219
  return null;
164970
165220
  clearCompressionDepth(db, sessionId);
164971
- clearInjectionCache(sessionId);
164972
- deps.onInjectionCacheCleared?.(sessionId);
165221
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
165222
+ clearInjectionCache(sessionId);
165223
+ }
165224
+ deps.onCompartmentStatePublished?.(sessionId);
164973
165225
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
164974
165226
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
164975
165227
  }
@@ -164979,6 +165231,10 @@ async function executeContextRecompInternal(deps) {
164979
165231
  }
164980
165232
  if (deps.experimentalCompactionMarkers && lastCompartmentEnd2 > 0) {
164981
165233
  updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
165234
+ const stalePending = getPendingCompactionMarkerState(db, sessionId);
165235
+ if (stalePending) {
165236
+ clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
165237
+ }
164982
165238
  }
164983
165239
  return [
164984
165240
  `Persisted ${promoted2.compartments.length} compartment${promoted2.compartments.length === 1 ? "" : "s"} from ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
@@ -165044,7 +165300,7 @@ Nothing was written.`;
165044
165300
 
165045
165301
  This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
165046
165302
  cleanupHistorianStateFile(currentStateFilePath);
165047
- currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
165303
+ currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, sessionDirectory);
165048
165304
  const prompt = buildCompartmentAgentPrompt(existingState, `Messages ${chunk.startIndex}-${chunk.endIndex}:
165049
165305
 
165050
165306
  ${chunk.text}`, { stateFilePath: currentStateFilePath });
@@ -165140,8 +165396,10 @@ Nothing was written.`;
165140
165396
  clearRecompStaging(db, sessionId);
165141
165397
  }
165142
165398
  clearCompressionDepth(db, sessionId);
165143
- clearInjectionCache(sessionId);
165144
- deps.onInjectionCacheCleared?.(sessionId);
165399
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
165400
+ clearInjectionCache(sessionId);
165401
+ }
165402
+ deps.onCompartmentStatePublished?.(sessionId);
165145
165403
  const finalCompartments = promoted?.compartments ?? candidateCompartments;
165146
165404
  const finalFacts = promoted?.facts ?? candidateFacts;
165147
165405
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
@@ -165216,50 +165474,78 @@ __export(exports_compartment_runner, {
165216
165474
  startCompartmentAgent: () => startCompartmentAgent,
165217
165475
  runCompartmentAgent: () => runCompartmentAgent,
165218
165476
  registerActiveCompartmentRun: () => registerActiveCompartmentRun,
165477
+ markActiveCompartmentRunPublished: () => markActiveCompartmentRunPublished,
165219
165478
  getActiveCompartmentRun: () => getActiveCompartmentRun,
165220
165479
  executeContextRecomp: () => executeContextRecomp
165221
165480
  });
165222
165481
  function getActiveCompartmentRun(sessionId) {
165223
165482
  return activeRuns.get(sessionId);
165224
165483
  }
165484
+ function markActiveCompartmentRunPublished(sessionId) {
165485
+ const activeRun = activeRuns.get(sessionId);
165486
+ if (activeRun)
165487
+ activeRun.published = true;
165488
+ }
165225
165489
  function registerActiveCompartmentRun(sessionId, promise2) {
165490
+ const activeRun = {
165491
+ promise: Promise.resolve(),
165492
+ published: false
165493
+ };
165226
165494
  const wrapped = promise2.finally(() => {
165227
- if (activeRuns.get(sessionId) === wrapped) {
165495
+ if (activeRuns.get(sessionId)?.promise === wrapped) {
165228
165496
  activeRuns.delete(sessionId);
165229
165497
  }
165230
165498
  });
165231
- activeRuns.set(sessionId, wrapped);
165499
+ activeRun.promise = wrapped;
165500
+ activeRuns.set(sessionId, activeRun);
165501
+ return activeRun;
165502
+ }
165503
+ function withPublishedCallback(deps) {
165504
+ return {
165505
+ ...deps,
165506
+ onCompartmentStatePublished: (sid) => {
165507
+ markActiveCompartmentRunPublished(sid);
165508
+ deps.onCompartmentStatePublished?.(sid);
165509
+ }
165510
+ };
165232
165511
  }
165233
165512
  function startCompartmentAgent(deps) {
165234
165513
  const existing = activeRuns.get(deps.sessionId);
165235
165514
  if (existing) {
165236
165515
  return;
165237
165516
  }
165238
- const promise2 = runCompartmentAgent(deps).catch((err) => {
165517
+ const runnerDeps = withPublishedCallback(deps);
165518
+ const promise2 = runCompartmentAgent(runnerDeps).catch((err) => {
165239
165519
  sessionLog(deps.sessionId, "compartment agent: unhandled rejection:", err);
165240
165520
  try {
165241
165521
  updateSessionMeta(deps.db, deps.sessionId, { compartmentInProgress: false });
165242
165522
  } catch {}
165243
165523
  }).finally(() => {
165244
- activeRuns.delete(deps.sessionId);
165524
+ if (activeRuns.get(deps.sessionId)?.promise === promise2) {
165525
+ activeRuns.delete(deps.sessionId);
165526
+ }
165245
165527
  });
165246
- activeRuns.set(deps.sessionId, promise2);
165528
+ activeRuns.set(deps.sessionId, { promise: promise2, published: false });
165247
165529
  }
165248
165530
  async function executeContextRecomp(deps, options = {}) {
165249
165531
  const { sessionId } = deps;
165250
165532
  if (activeRuns.has(sessionId)) {
165251
165533
  return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
165252
165534
  }
165253
- const promise2 = options.range ? executePartialRecompInternal(deps, options.range) : executeContextRecompInternal(deps);
165254
- activeRuns.set(sessionId, promise2.then(() => {
165535
+ const runnerDeps = withPublishedCallback(deps);
165536
+ const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
165537
+ const wrappedPromise = promise2.then(() => {
165255
165538
  return;
165256
165539
  }).catch((err) => {
165257
165540
  sessionLog(sessionId, "compartment agent: recomp unhandled rejection:", err);
165258
- }));
165541
+ });
165542
+ activeRuns.set(sessionId, { promise: wrappedPromise, published: false });
165259
165543
  try {
165260
165544
  return await promise2;
165261
165545
  } finally {
165262
- activeRuns.delete(sessionId);
165546
+ if (activeRuns.get(sessionId)?.promise === wrappedPromise) {
165547
+ activeRuns.delete(sessionId);
165548
+ }
165263
165549
  }
165264
165550
  }
165265
165551
  var activeRuns;
@@ -165280,8 +165566,8 @@ var exports_tui_config = {};
165280
165566
  __export(exports_tui_config, {
165281
165567
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
165282
165568
  });
165283
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "node:fs";
165284
- import { dirname as dirname7, join as join24 } from "node:path";
165569
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
165570
+ import { dirname as dirname8, join as join23 } from "node:path";
165285
165571
  function isMagicContextEntry(entry) {
165286
165572
  if (!entry)
165287
165573
  return false;
@@ -165295,8 +165581,8 @@ function isMagicContextEntry(entry) {
165295
165581
  }
165296
165582
  function resolveTuiConfigPath() {
165297
165583
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
165298
- const jsoncPath = join24(configDir, "tui.jsonc");
165299
- const jsonPath = join24(configDir, "tui.json");
165584
+ const jsoncPath = join23(configDir, "tui.jsonc");
165585
+ const jsonPath = join23(configDir, "tui.json");
165300
165586
  if (existsSync14(jsoncPath))
165301
165587
  return jsoncPath;
165302
165588
  if (existsSync14(jsonPath))
@@ -165308,7 +165594,7 @@ function ensureTuiPluginEntry() {
165308
165594
  const configPath = resolveTuiConfigPath();
165309
165595
  let config2 = {};
165310
165596
  if (existsSync14(configPath)) {
165311
- const raw = readFileSync10(configPath, "utf-8");
165597
+ const raw = readFileSync11(configPath, "utf-8");
165312
165598
  config2 = import_comment_json3.parse(raw) ?? {};
165313
165599
  }
165314
165600
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p) => typeof p === "string") : [];
@@ -165327,7 +165613,7 @@ function ensureTuiPluginEntry() {
165327
165613
  plugins.push(PLUGIN_ENTRY);
165328
165614
  }
165329
165615
  config2.plugin = plugins;
165330
- mkdirSync8(dirname7(configPath), { recursive: true });
165616
+ mkdirSync9(dirname8(configPath), { recursive: true });
165331
165617
  writeFileSync8(configPath, `${import_comment_json3.stringify(config2, null, 2)}
165332
165618
  `);
165333
165619
  log(`[magic-context] updated TUI plugin entry in ${configPath}`);
@@ -165438,34 +165724,20 @@ function loadConfigFile(configPath) {
165438
165724
  return null;
165439
165725
  }
165440
165726
  }
165441
- function mergeConfigs(base, override, options = {}) {
165442
- const config2 = {
165443
- ...base,
165444
- ...override,
165445
- auto_update: options.allowUserOnlyFields ? override.auto_update : base.auto_update,
165446
- memory: {
165447
- ...base.memory ?? {},
165448
- ...override.memory ?? {}
165449
- },
165450
- embedding: override.embedding ?? base.embedding,
165451
- historian: override.historian ?? base.historian,
165452
- dreamer: override.dreamer ? {
165453
- ...base.dreamer ?? {},
165454
- ...override.dreamer
165455
- } : base.dreamer,
165456
- sidekick: override.sidekick ? {
165457
- ...base.sidekick ?? {},
165458
- ...override.sidekick
165459
- } : base.sidekick,
165460
- disabled_hooks: [
165461
- ...new Set([...base.disabled_hooks ?? [], ...override.disabled_hooks ?? []])
165462
- ],
165463
- command: {
165464
- ...base.command ?? {},
165465
- ...override.command ?? {}
165727
+ function deepMergeRawConfig(base, override) {
165728
+ const result = { ...base };
165729
+ for (const key of Object.keys(override)) {
165730
+ const baseVal = base[key];
165731
+ const overrideVal = override[key];
165732
+ if (baseVal !== null && typeof baseVal === "object" && !Array.isArray(baseVal) && overrideVal !== null && typeof overrideVal === "object" && !Array.isArray(overrideVal)) {
165733
+ result[key] = deepMergeRawConfig(baseVal, overrideVal);
165734
+ } else if (key === "disabled_hooks" && Array.isArray(baseVal) && Array.isArray(overrideVal)) {
165735
+ result[key] = [...new Set([...baseVal, ...overrideVal])];
165736
+ } else {
165737
+ result[key] = overrideVal;
165466
165738
  }
165467
- };
165468
- return config2;
165739
+ }
165740
+ return result;
165469
165741
  }
165470
165742
  function getProjectUserOnlyFields(config2) {
165471
165743
  return "auto_update" in config2 ? ["auto_update"] : [];
@@ -165606,30 +165878,38 @@ function loadPluginConfig(directory) {
165606
165878
  const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
165607
165879
  const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
165608
165880
  const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
165609
- let config2 = parsePluginConfig({});
165610
165881
  const allWarnings = [];
165882
+ let mergedRaw = {};
165611
165883
  if (userLoaded) {
165612
165884
  allWarnings.push(...userLoaded.warnings.map((w) => `[user config] ${w}`));
165613
- const parsed = parsePluginConfig(userLoaded.config);
165614
- if (parsed.configWarnings?.length) {
165615
- allWarnings.push(...parsed.configWarnings.map((w) => `[user config] ${w}`));
165616
- }
165617
- config2 = mergeConfigs(config2, parsed, { allowUserOnlyFields: true });
165885
+ mergedRaw = deepMergeRawConfig(mergedRaw, userLoaded.config);
165618
165886
  }
165619
165887
  if (projectLoaded) {
165620
165888
  allWarnings.push(...projectLoaded.warnings.map((w) => `[project config] ${w}`));
165621
- const parsed = parsePluginConfig(projectLoaded.config);
165622
- if (parsed.configWarnings?.length) {
165623
- allWarnings.push(...parsed.configWarnings.map((w) => `[project config] ${w}`));
165624
- }
165625
- const strippedUserOnlyFields = getProjectUserOnlyFields(projectLoaded.config);
165889
+ const projectRaw = { ...projectLoaded.config };
165890
+ const strippedUserOnlyFields = getProjectUserOnlyFields(projectRaw);
165626
165891
  if (strippedUserOnlyFields.length > 0) {
165892
+ for (const key of strippedUserOnlyFields) {
165893
+ delete projectRaw[key];
165894
+ }
165627
165895
  allWarnings.push(`[project config] Ignoring ${strippedUserOnlyFields.join(", ")} from project config (security: these settings only honor user-level config)`);
165628
165896
  }
165629
- config2 = mergeConfigs(config2, parsed);
165897
+ mergedRaw = deepMergeRawConfig(mergedRaw, projectRaw);
165898
+ }
165899
+ const config2 = parsePluginConfig(mergedRaw);
165900
+ if (config2.configWarnings?.length) {
165901
+ allWarnings.push(...config2.configWarnings.map((w) => {
165902
+ if (userLoaded && projectLoaded)
165903
+ return `[config] ${w}`;
165904
+ if (userLoaded)
165905
+ return `[user config] ${w}`;
165906
+ return `[project config] ${w}`;
165907
+ }));
165630
165908
  }
165631
165909
  if (allWarnings.length > 0) {
165632
165910
  config2.configWarnings = allWarnings;
165911
+ } else if ("configWarnings" in config2) {
165912
+ config2.configWarnings = undefined;
165633
165913
  }
165634
165914
  return config2;
165635
165915
  }
@@ -166111,41 +166391,41 @@ init_tool_definition_tokens();
166111
166391
 
166112
166392
  // src/hooks/auto-update-checker/index.ts
166113
166393
  init_logger();
166114
- import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync6, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
166115
- import { dirname as dirname4, join as join6 } from "node:path";
166394
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync6, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
166395
+ import { dirname as dirname5, join as join6 } from "node:path";
166116
166396
 
166117
166397
  // src/hooks/auto-update-checker/cache.ts
166118
166398
  init_logger();
166119
166399
  var import_comment_json2 = __toESM(require_src2(), 1);
166120
166400
  import { spawn } from "node:child_process";
166121
166401
  import { existsSync as existsSync5, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
166122
- import { basename, dirname as dirname3, join as join5 } from "node:path";
166402
+ import { basename, dirname as dirname4, join as join5 } from "node:path";
166123
166403
 
166124
166404
  // src/hooks/auto-update-checker/checker.ts
166125
166405
  init_logger();
166126
166406
  var import_comment_json = __toESM(require_src2(), 1);
166127
166407
  import { existsSync as existsSync4, readFileSync as readFileSync4, statSync, writeFileSync } from "node:fs";
166128
- import { homedir as homedir4 } from "node:os";
166129
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
166408
+ import { homedir as homedir5 } from "node:os";
166409
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
166130
166410
  import { fileURLToPath } from "node:url";
166131
166411
 
166132
166412
  // src/hooks/auto-update-checker/constants.ts
166133
- import { homedir as homedir3, platform } from "node:os";
166413
+ import { homedir as homedir4, platform } from "node:os";
166134
166414
  import { join as join3 } from "node:path";
166135
166415
  var PACKAGE_NAME = "@cortexkit/opencode-magic-context";
166136
166416
  var NPM_REGISTRY_URL = "https://registry.npmjs.org";
166137
166417
  var NPM_FETCH_TIMEOUT = 1e4;
166138
166418
  function getOpenCodeCacheRoot() {
166139
166419
  if (platform() === "win32") {
166140
- return join3(process.env.LOCALAPPDATA ?? homedir3(), "opencode");
166420
+ return join3(process.env.LOCALAPPDATA ?? homedir4(), "opencode");
166141
166421
  }
166142
- return join3(homedir3(), ".cache", "opencode");
166422
+ return join3(homedir4(), ".cache", "opencode");
166143
166423
  }
166144
166424
  function getOpenCodeConfigRoot() {
166145
166425
  if (platform() === "win32") {
166146
- return join3(process.env.APPDATA ?? join3(homedir3(), "AppData", "Roaming"), "opencode");
166426
+ return join3(process.env.APPDATA ?? join3(homedir4(), "AppData", "Roaming"), "opencode");
166147
166427
  }
166148
- return join3(process.env.XDG_CONFIG_HOME ?? join3(homedir3(), ".config"), "opencode");
166428
+ return join3(process.env.XDG_CONFIG_HOME ?? join3(homedir4(), ".config"), "opencode");
166149
166429
  }
166150
166430
  var CACHE_DIR = join3(getOpenCodeCacheRoot(), "packages");
166151
166431
  var USER_OPENCODE_CONFIG = join3(getOpenCodeConfigRoot(), "opencode.json");
@@ -166227,12 +166507,12 @@ function resolvePathPluginSpec(spec, configPath) {
166227
166507
  }
166228
166508
  if (isAbsolute2(spec) || /^[A-Za-z]:[\\/]/.test(spec))
166229
166509
  return spec;
166230
- return resolve2(dirname2(configPath), spec);
166510
+ return resolve2(dirname3(configPath), spec);
166231
166511
  }
166232
166512
  function findPackageJsonUp(startPath) {
166233
166513
  try {
166234
166514
  const stat = statSync(startPath);
166235
- let dir = stat.isDirectory() ? startPath : dirname2(startPath);
166515
+ let dir = stat.isDirectory() ? startPath : dirname3(startPath);
166236
166516
  for (let i = 0;i < 10; i++) {
166237
166517
  const pkgPath = join4(dir, "package.json");
166238
166518
  if (existsSync4(pkgPath)) {
@@ -166242,7 +166522,7 @@ function findPackageJsonUp(startPath) {
166242
166522
  return pkgPath;
166243
166523
  } catch {}
166244
166524
  }
166245
- const parent = dirname2(dir);
166525
+ const parent = dirname3(dir);
166246
166526
  if (parent === dir)
166247
166527
  break;
166248
166528
  dir = parent;
@@ -166290,7 +166570,7 @@ function getLocalDevVersion(directory) {
166290
166570
  }
166291
166571
  function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
166292
166572
  try {
166293
- return findPackageJsonUp(dirname2(fileURLToPath(currentModuleUrl)));
166573
+ return findPackageJsonUp(dirname3(fileURLToPath(currentModuleUrl)));
166294
166574
  } catch (err) {
166295
166575
  warn(`[auto-update-checker] Failed to resolve runtime package path: ${String(err)}`);
166296
166576
  return null;
@@ -166333,7 +166613,7 @@ function getCachedVersion(spec) {
166333
166613
  getCurrentRuntimePackageJsonPath(),
166334
166614
  spec ? getSpecCachePackageJsonPath(spec) : null,
166335
166615
  getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
166336
- join4(homedir4(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
166616
+ join4(homedir5(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
166337
166617
  ].filter(isString);
166338
166618
  for (const packageJsonPath of candidates) {
166339
166619
  try {
@@ -166387,7 +166667,7 @@ function stripPackageNameFromPath(pathValue, packageName) {
166387
166667
  for (const segment of [...packageName.split("/")].reverse()) {
166388
166668
  if (basename(current) !== segment)
166389
166669
  return null;
166390
- current = dirname3(current);
166670
+ current = dirname4(current);
166391
166671
  }
166392
166672
  return current;
166393
166673
  }
@@ -166450,19 +166730,19 @@ function removeInstalledPackage(installDir, packageName) {
166450
166730
  }
166451
166731
  function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
166452
166732
  if (runtimePackageJsonPath) {
166453
- const packageDir = dirname3(runtimePackageJsonPath);
166733
+ const packageDir = dirname4(runtimePackageJsonPath);
166454
166734
  const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
166455
166735
  if (nodeModulesDir && basename(nodeModulesDir) === "node_modules") {
166456
- const installDir = dirname3(nodeModulesDir);
166736
+ const installDir = dirname4(nodeModulesDir);
166457
166737
  const packageJsonPath = join5(installDir, "package.json");
166458
166738
  if (existsSync5(packageJsonPath))
166459
166739
  return { installDir, packageJsonPath };
166460
166740
  }
166461
166741
  return null;
166462
166742
  }
166463
- const legacyPackageJsonPath = join5(dirname3(CACHE_DIR), "package.json");
166743
+ const legacyPackageJsonPath = join5(dirname4(CACHE_DIR), "package.json");
166464
166744
  if (existsSync5(legacyPackageJsonPath)) {
166465
- return { installDir: dirname3(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
166745
+ return { installDir: dirname4(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
166466
166746
  }
166467
166747
  return null;
166468
166748
  }
@@ -166591,7 +166871,7 @@ function claimCheckSlot(storageDir, intervalMs) {
166591
166871
  }
166592
166872
  } catch {}
166593
166873
  }
166594
- mkdirSync(dirname4(file2), { recursive: true });
166874
+ mkdirSync2(dirname5(file2), { recursive: true });
166595
166875
  const tmp = `${file2}.tmp.${process.pid}`;
166596
166876
  writeFileSync3(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
166597
166877
  renameSync(tmp, file2);
@@ -166689,8 +166969,10 @@ function createLiveSessionState() {
166689
166969
  variantBySession: new Map,
166690
166970
  agentBySession: new Map,
166691
166971
  historyRefreshSessions: new Set,
166972
+ deferredHistoryRefreshSessions: new Set,
166692
166973
  systemPromptRefreshSessions: new Set,
166693
166974
  pendingMaterializationSessions: new Set,
166975
+ deferredMaterializationSessions: new Set,
166694
166976
  sessionDirectoryBySession: new Map
166695
166977
  };
166696
166978
  }
@@ -166793,11 +167075,23 @@ function releaseLease(db, holderId) {
166793
167075
  })();
166794
167076
  }
166795
167077
  // src/features/magic-context/dreamer/queue.ts
167078
+ function hasActiveDreamLease(db) {
167079
+ try {
167080
+ return isLeaseActive(db);
167081
+ } catch (error51) {
167082
+ if (String(error51).includes("no such table: dream_state")) {
167083
+ return false;
167084
+ }
167085
+ throw error51;
167086
+ }
167087
+ }
166796
167088
  function enqueueDream(db, projectIdentity, reason, force = false) {
166797
167089
  const now = Date.now();
166798
167090
  return db.transaction(() => {
166799
- const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
166800
- db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
167091
+ if (!hasActiveDreamLease(db)) {
167092
+ const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
167093
+ db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
167094
+ }
166801
167095
  const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
166802
167096
  if (existing) {
166803
167097
  return null;
@@ -166858,7 +167152,7 @@ init_data_path();
166858
167152
  init_logger();
166859
167153
  await init_sqlite();
166860
167154
  import { existsSync as existsSync9 } from "node:fs";
166861
- import { join as join13 } from "node:path";
167155
+ import { join as join12 } from "node:path";
166862
167156
 
166863
167157
  // src/features/magic-context/key-files/identify-key-files.ts
166864
167158
  init_logger();
@@ -167275,7 +167569,7 @@ function logWithStackHead(message, stackHead) {
167275
167569
  log(message, stackHead ? { stackHead } : undefined);
167276
167570
  }
167277
167571
  function getOpenCodeDbPath2() {
167278
- return join13(getDataDir(), "opencode", "opencode.db");
167572
+ return join12(getDataDir(), "opencode", "opencode.db");
167279
167573
  }
167280
167574
  function openOpenCodeDb() {
167281
167575
  const dbPath = getOpenCodeDbPath2();
@@ -167350,9 +167644,11 @@ async function identifyKeyFilesForSession(args) {
167350
167644
  try {
167351
167645
  if (!renewLease(args.db, args.holderId)) {
167352
167646
  log(`[key-files][${args.sessionId}] lease renewal failed — aborting`);
167647
+ args.onLeaseLost?.(`key-files:${args.sessionId}`);
167353
167648
  abortController.abort();
167354
167649
  }
167355
- } catch {
167650
+ } catch (error51) {
167651
+ args.onLeaseLost?.(`key-files:${args.sessionId}`, error51);
167356
167652
  abortController.abort();
167357
167653
  }
167358
167654
  }, 60000);
@@ -167407,6 +167703,10 @@ async function identifyKeyFilesForSession(args) {
167407
167703
  log(`[key-files][${args.sessionId}] could not parse agent output — using heuristic fallback`);
167408
167704
  applyHeuristicFallback();
167409
167705
  } catch (error51) {
167706
+ if (args.isLeaseLost?.() || abortController.signal.aborted) {
167707
+ log(`[key-files][${args.sessionId}] lease lost during identification — skipping heuristic fallback`);
167708
+ throw error51;
167709
+ }
167410
167710
  log(`[key-files][${args.sessionId}] identification failed: ${getErrorMessage(error51)} — using heuristic fallback`);
167411
167711
  try {
167412
167712
  applyHeuristicFallback();
@@ -167447,6 +167747,10 @@ async function identifyKeyFiles(args) {
167447
167747
  }
167448
167748
  log(`[key-files] evaluating ${sessionIds.length} active session(s) for ${args.projectIdentity}`);
167449
167749
  for (const sessionId of sessionIds) {
167750
+ if (args.isLeaseLost?.()) {
167751
+ log("[key-files] lease lost — stopping key-file identification");
167752
+ break;
167753
+ }
167450
167754
  if (Date.now() > args.deadline) {
167451
167755
  log("[key-files] deadline reached — stopping key-file identification");
167452
167756
  break;
@@ -167458,6 +167762,8 @@ async function identifyKeyFiles(args) {
167458
167762
  sessionDirectory: args.sessionDirectory,
167459
167763
  holderId: args.holderId,
167460
167764
  fallbackModels: args.fallbackModels,
167765
+ onLeaseLost: args.onLeaseLost,
167766
+ isLeaseLost: args.isLeaseLost,
167461
167767
  deadline: args.deadline,
167462
167768
  sessionId,
167463
167769
  config: args.config
@@ -167514,8 +167820,50 @@ async function runDream(args) {
167514
167820
  let lastErrorSignature = null;
167515
167821
  let consecutiveSameErrorFailures = 0;
167516
167822
  let circuitBreakerTripped = false;
167823
+ let lostLease = false;
167824
+ let lostLeaseReason = null;
167825
+ let lostLeaseRecorded = false;
167826
+ const markLeaseLost = (phase, error51) => {
167827
+ const detail = error51 ? `: ${getErrorMessage(error51)}` : "";
167828
+ lostLeaseReason = `Dream lease lost during ${phase}${detail}`;
167829
+ if (!lostLease) {
167830
+ log(`[dreamer] FATAL: ${lostLeaseReason}; aborting all remaining dream work`);
167831
+ } else {
167832
+ log(`[dreamer] FATAL: ${lostLeaseReason}; dream work is already aborting`);
167833
+ }
167834
+ lostLease = true;
167835
+ };
167836
+ const recordLeaseLostTask = (phase) => {
167837
+ if (lostLeaseRecorded)
167838
+ return;
167839
+ lostLeaseRecorded = true;
167840
+ result.tasks.push({
167841
+ name: "lease-lost",
167842
+ durationMs: 0,
167843
+ result: "",
167844
+ error: lostLeaseReason ?? `Dream lease lost during ${phase}; aborted remaining work`
167845
+ });
167846
+ };
167847
+ const verifyLeaseStillHeld = (phase) => {
167848
+ if (lostLease)
167849
+ return false;
167850
+ try {
167851
+ if (!renewLease(args.db, holderId)) {
167852
+ markLeaseLost(phase);
167853
+ return false;
167854
+ }
167855
+ return true;
167856
+ } catch (error51) {
167857
+ markLeaseLost(phase, error51);
167858
+ return false;
167859
+ }
167860
+ };
167517
167861
  try {
167518
167862
  for (const taskName of args.tasks) {
167863
+ if (!verifyLeaseStillHeld(`before task ${taskName}`)) {
167864
+ recordLeaseLostTask(`before task ${taskName}`);
167865
+ break;
167866
+ }
167519
167867
  if (Date.now() > deadline) {
167520
167868
  log(`[dreamer] deadline reached, stopping after ${result.tasks.length} tasks`);
167521
167869
  break;
@@ -167528,18 +167876,20 @@ async function runDream(args) {
167528
167876
  try {
167529
167877
  if (!renewLease(args.db, holderId)) {
167530
167878
  log(`[dreamer] task ${taskName}: lease renewal failed — aborting LLM call`);
167879
+ markLeaseLost(`task ${taskName} lease renewal`);
167531
167880
  taskAbortController.abort();
167532
167881
  }
167533
167882
  } catch (err) {
167534
167883
  log(`[dreamer] task ${taskName}: lease renewal threw — aborting LLM call: ${err}`);
167884
+ markLeaseLost(`task ${taskName} lease renewal`, err);
167535
167885
  taskAbortController.abort();
167536
167886
  }
167537
167887
  }, 60000);
167538
167888
  try {
167539
167889
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
167540
167890
  const existingDocs = taskName === "maintain-docs" ? {
167541
- architecture: existsSync9(join13(docsDir, "ARCHITECTURE.md")),
167542
- structure: existsSync9(join13(docsDir, "STRUCTURE.md"))
167891
+ architecture: existsSync9(join12(docsDir, "ARCHITECTURE.md")),
167892
+ structure: existsSync9(join12(docsDir, "STRUCTURE.md"))
167543
167893
  } : undefined;
167544
167894
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
167545
167895
  id: um.id,
@@ -167578,6 +167928,9 @@ async function runDream(args) {
167578
167928
  fallbackModels: args.fallbackModels,
167579
167929
  callContext: `dreamer:${taskName}`
167580
167930
  });
167931
+ if (lostLease) {
167932
+ throw new Error(lostLeaseReason ?? `Dream lease lost during ${taskName}`);
167933
+ }
167581
167934
  const messagesResponse = await args.client.session.messages({
167582
167935
  path: { id: agentSessionId },
167583
167936
  query: { directory: args.sessionDirectory ?? args.projectIdentity }
@@ -167608,7 +167961,10 @@ async function runDream(args) {
167608
167961
  result: null,
167609
167962
  error: errorDescription.brief
167610
167963
  });
167611
- if (shouldSkipCircuitBreaker(error51, errorDescription.brief)) {
167964
+ if (lostLease) {
167965
+ lastErrorSignature = null;
167966
+ consecutiveSameErrorFailures = 0;
167967
+ } else if (shouldSkipCircuitBreaker(error51, errorDescription.brief)) {
167612
167968
  lastErrorSignature = null;
167613
167969
  consecutiveSameErrorFailures = 0;
167614
167970
  } else {
@@ -167641,11 +167997,18 @@ async function runDream(args) {
167641
167997
  });
167642
167998
  }
167643
167999
  }
168000
+ if (lostLease) {
168001
+ recordLeaseLostTask(`task ${taskName}`);
168002
+ break;
168003
+ }
167644
168004
  if (circuitBreakerTripped) {
167645
168005
  break;
167646
168006
  }
167647
168007
  }
167648
- if (circuitBreakerTripped) {
168008
+ if (lostLease) {
168009
+ log("[dreamer] lease lost: skipping all post-task phases");
168010
+ recordLeaseLostTask("post-task phases");
168011
+ } else if (circuitBreakerTripped) {
167649
168012
  log("[dreamer] circuit breaker: skipping post-task phases");
167650
168013
  result.tasks.push({
167651
168014
  name: "post-task-phases",
@@ -167654,9 +168017,12 @@ async function runDream(args) {
167654
168017
  error: "Skipped post-task phases after circuit breaker tripped; configure dreamer model/fallback_models in magic-context.jsonc."
167655
168018
  });
167656
168019
  }
167657
- if (!circuitBreakerTripped && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
168020
+ if (!circuitBreakerTripped && !lostLease && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
167658
168021
  const umStart = Date.now();
167659
168022
  try {
168023
+ if (!verifyLeaseStillHeld("before user-memory review")) {
168024
+ throw new Error(lostLeaseReason ?? "Dream lease lost before user-memory review");
168025
+ }
167660
168026
  const reviewResult = await reviewUserMemories({
167661
168027
  db: args.db,
167662
168028
  client: args.client,
@@ -167667,6 +168033,9 @@ async function runDream(args) {
167667
168033
  promotionThreshold: args.experimentalUserMemories.promotionThreshold,
167668
168034
  fallbackModels: args.fallbackModels
167669
168035
  });
168036
+ if (!verifyLeaseStillHeld("after user-memory review")) {
168037
+ throw new Error(lostLeaseReason ?? "Dream lease lost after user-memory review");
168038
+ }
167670
168039
  const umOutput = `promoted=${reviewResult.promoted} merged=${reviewResult.merged} dismissed=${reviewResult.dismissed} consumed=${reviewResult.candidatesConsumed}`;
167671
168040
  if (reviewResult.promoted > 0 || reviewResult.merged > 0 || reviewResult.dismissed > 0) {
167672
168041
  log(`[dreamer] user-memories: ${umOutput}`);
@@ -167686,9 +168055,14 @@ async function runDream(args) {
167686
168055
  error: errorDescription.brief
167687
168056
  });
167688
168057
  }
168058
+ if (lostLease)
168059
+ recordLeaseLostTask("user-memory review");
167689
168060
  }
167690
- if (!circuitBreakerTripped && Date.now() <= deadline) {
168061
+ if (!circuitBreakerTripped && !lostLease && Date.now() <= deadline) {
167691
168062
  try {
168063
+ if (!verifyLeaseStillHeld("before smart-note evaluation")) {
168064
+ throw new Error(lostLeaseReason ?? "Dream lease lost before smart-note evaluation");
168065
+ }
167692
168066
  await evaluateSmartNotes({
167693
168067
  db: args.db,
167694
168068
  client: args.client,
@@ -167698,16 +168072,26 @@ async function runDream(args) {
167698
168072
  holderId,
167699
168073
  deadline,
167700
168074
  result,
167701
- fallbackModels: args.fallbackModels
168075
+ fallbackModels: args.fallbackModels,
168076
+ onLeaseLost: markLeaseLost,
168077
+ isLeaseLost: () => lostLease
167702
168078
  });
168079
+ if (!verifyLeaseStillHeld("after smart-note evaluation")) {
168080
+ throw new Error(lostLeaseReason ?? "Dream lease lost after smart-note evaluation");
168081
+ }
167703
168082
  } catch (error51) {
167704
168083
  const errorDescription = describeError(error51);
167705
168084
  logWithStackHead(`[dreamer] smart note evaluation failed: ${errorDescription.brief}`, errorDescription.stackHead);
167706
168085
  }
168086
+ if (lostLease)
168087
+ recordLeaseLostTask("smart-note evaluation");
167707
168088
  }
167708
- if (!circuitBreakerTripped && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
168089
+ if (!circuitBreakerTripped && !lostLease && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
167709
168090
  const kfStart = Date.now();
167710
168091
  try {
168092
+ if (!verifyLeaseStillHeld("before key-file identification")) {
168093
+ throw new Error(lostLeaseReason ?? "Dream lease lost before key-file identification");
168094
+ }
167711
168095
  await identifyKeyFiles({
167712
168096
  db: args.db,
167713
168097
  client: args.client,
@@ -167717,8 +168101,13 @@ async function runDream(args) {
167717
168101
  holderId,
167718
168102
  deadline,
167719
168103
  config: args.experimentalPinKeyFiles,
167720
- fallbackModels: args.fallbackModels
168104
+ fallbackModels: args.fallbackModels,
168105
+ onLeaseLost: markLeaseLost,
168106
+ isLeaseLost: () => lostLease
167721
168107
  });
168108
+ if (!verifyLeaseStillHeld("after key-file identification")) {
168109
+ throw new Error(lostLeaseReason ?? "Dream lease lost after key-file identification");
168110
+ }
167722
168111
  result.tasks.push({
167723
168112
  name: "key files",
167724
168113
  durationMs: Date.now() - kfStart,
@@ -167734,6 +168123,8 @@ async function runDream(args) {
167734
168123
  error: errorDescription.brief
167735
168124
  });
167736
168125
  }
168126
+ if (lostLease)
168127
+ recordLeaseLostTask("key-file identification");
167737
168128
  }
167738
168129
  } finally {
167739
168130
  releaseLease(args.db, holderId);
@@ -167774,7 +168165,7 @@ async function runDream(args) {
167774
168165
  "circuit-breaker"
167775
168166
  ]);
167776
168167
  const hasSuccessfulTask = result.tasks.some((t) => !t.error && !POST_TASK_NAMES.has(t.name));
167777
- if (hasSuccessfulTask) {
168168
+ if (hasSuccessfulTask && !lostLease) {
167778
168169
  setDreamState(args.db, `last_dream_at:${args.projectIdentity}`, String(result.finishedAt));
167779
168170
  setDreamState(args.db, "last_dream_at", String(result.finishedAt));
167780
168171
  }
@@ -167823,9 +168214,11 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
167823
168214
  try {
167824
168215
  if (!renewLease(args.db, args.holderId)) {
167825
168216
  log("[dreamer] smart notes: lease renewal failed — aborting");
168217
+ args.onLeaseLost?.("smart notes");
167826
168218
  abortController.abort();
167827
168219
  }
167828
- } catch {
168220
+ } catch (error51) {
168221
+ args.onLeaseLost?.("smart notes", error51);
167829
168222
  abortController.abort();
167830
168223
  }
167831
168224
  }, 60000);
@@ -170073,6 +170466,15 @@ init_project_identity();
170073
170466
  init_logger();
170074
170467
  await init_storage();
170075
170468
 
170469
+ // src/hooks/magic-context/cache-busting-signals.ts
170470
+ function canConsumeDeferredOnThisPass(args) {
170471
+ if (args.justAwaitedPublication)
170472
+ return true;
170473
+ if (args.activeRunBlocksMaterialization)
170474
+ return false;
170475
+ return args.schedulerDecision === "execute" || args.contextPercentage >= FORCE_MATERIALIZE_PERCENTAGE;
170476
+ }
170477
+
170076
170478
  // src/hooks/magic-context/caveman-cleanup.ts
170077
170479
  init_shared();
170078
170480
  init_caveman();
@@ -170820,6 +171222,9 @@ function clearCompressorCooldown(sessionId) {
170820
171222
  async function runCompartmentPhase(args) {
170821
171223
  let pendingCompartmentInjection = args.pendingCompartmentInjection;
170822
171224
  let compartmentInProgress = args.sessionMeta.compartmentInProgress;
171225
+ let published = false;
171226
+ let justAwaitedPublication = false;
171227
+ let rebuiltHistoryThisPass = false;
170823
171228
  let lastCompartmentEnd = null;
170824
171229
  let rawMessageCount = null;
170825
171230
  let cachedProtectedTailStart = null;
@@ -170847,13 +171252,19 @@ async function runCompartmentPhase(args) {
170847
171252
  sessionLog(args.sessionId, reason);
170848
171253
  const timeoutMs = args.historianTimeoutMs ?? 120000;
170849
171254
  const timeout = new Promise((resolve4) => setTimeout(() => resolve4("timeout"), timeoutMs));
170850
- const result = await Promise.race([activeRun.then(() => "done"), timeout]);
171255
+ const result = await Promise.race([activeRun.promise.then(() => "done"), timeout]);
170851
171256
  if (result === "timeout") {
170852
171257
  sessionLog(args.sessionId, `transform: compartment await timed out after ${timeoutMs}ms — proceeding without waiting`);
170853
171258
  return "timed_out";
170854
171259
  }
170855
171260
  sessionLog(args.sessionId, "transform: compartment agent completed, refreshing compartment coverage");
170856
- pendingCompartmentInjection = prepareCompartmentInjection(args.db, args.resolvedSessionId, args.messages, args.cacheAlreadyBusting ?? false, args.projectPath, args.injectionBudgetTokens, args.experimentalTemporalAwareness);
171261
+ justAwaitedPublication = activeRun.published;
171262
+ published = published || activeRun.published;
171263
+ const historyReprepareShouldBust = activeRun.published && args.deferredHistoryRefreshSessions.has(args.sessionId);
171264
+ pendingCompartmentInjection = prepareCompartmentInjection(args.db, args.resolvedSessionId, args.messages, historyReprepareShouldBust, args.projectPath, args.injectionBudgetTokens, args.experimentalTemporalAwareness);
171265
+ if (historyReprepareShouldBust) {
171266
+ rebuiltHistoryThisPass = true;
171267
+ }
170857
171268
  return "completed";
170858
171269
  }
170859
171270
  if (args.canRunCompartments && args.sessionMeta.compartmentInProgress && !getActiveCompartmentRun(args.sessionId)) {
@@ -170885,7 +171296,8 @@ async function runCompartmentPhase(args) {
170885
171296
  compressorMaxMergeDepth: args.compressorMaxMergeDepth,
170886
171297
  memoryEnabled: args.memoryEnabled,
170887
171298
  autoPromote: args.autoPromote,
170888
- onInjectionCacheCleared: args.onInjectionCacheCleared
171299
+ onCompartmentStatePublished: args.onCompartmentStatePublished,
171300
+ preserveInjectionCacheUntilConsumed: true
170889
171301
  });
170890
171302
  compartmentInProgress = true;
170891
171303
  }
@@ -170913,7 +171325,8 @@ async function runCompartmentPhase(args) {
170913
171325
  compressorMaxMergeDepth: args.compressorMaxMergeDepth,
170914
171326
  memoryEnabled: args.memoryEnabled,
170915
171327
  autoPromote: args.autoPromote,
170916
- onInjectionCacheCleared: args.onInjectionCacheCleared
171328
+ onCompartmentStatePublished: args.onCompartmentStatePublished,
171329
+ preserveInjectionCacheUntilConsumed: true
170917
171330
  });
170918
171331
  activeRun = getActiveCompartmentRun(args.sessionId);
170919
171332
  } else if (!activeRun && hasEligibleHistoryForCompartment()) {
@@ -170933,7 +171346,7 @@ async function runCompartmentPhase(args) {
170933
171346
  }
170934
171347
  }
170935
171348
  }
170936
- if (args.cacheAlreadyBusting && args.historyBudgetTokens && args.historyBudgetTokens > 0 && args.client && !compartmentInProgress && !awaitedCompartmentRun && !isCompressorOnCooldown(args.sessionId, args.compressorCooldownMs ?? DEFAULT_COMPRESSOR_COOLDOWN_MS)) {
171349
+ if (args.safeForBackgroundCompression && !args.suppressBackgroundCompressionThisPass && args.historyBudgetTokens && args.historyBudgetTokens > 0 && args.client && !compartmentInProgress && !awaitedCompartmentRun && !isCompressorOnCooldown(args.sessionId, args.compressorCooldownMs ?? DEFAULT_COMPRESSOR_COOLDOWN_MS)) {
170937
171350
  markCompressorRun(args.sessionId);
170938
171351
  const compressorPromise = runCompressionPassIfNeeded({
170939
171352
  client: args.client,
@@ -170947,6 +171360,9 @@ async function runCompartmentPhase(args) {
170947
171360
  maxMergeDepth: args.compressorMaxMergeDepth
170948
171361
  }).then((compressed) => {
170949
171362
  if (compressed) {
171363
+ markActiveCompartmentRunPublished(args.sessionId);
171364
+ published = true;
171365
+ args.deferredHistoryRefreshSessions.add(args.sessionId);
170950
171366
  sessionLog(args.sessionId, "independent compressor completed in background — compressed history will appear on next cache-busting pass");
170951
171367
  }
170952
171368
  }).catch((error51) => {
@@ -170954,7 +171370,14 @@ async function runCompartmentPhase(args) {
170954
171370
  });
170955
171371
  registerActiveCompartmentRun(args.sessionId, compressorPromise);
170956
171372
  }
170957
- return { pendingCompartmentInjection, awaitedCompartmentRun, compartmentInProgress };
171373
+ return {
171374
+ pendingCompartmentInjection,
171375
+ awaitedCompartmentRun,
171376
+ compartmentInProgress,
171377
+ published,
171378
+ justAwaitedPublication,
171379
+ rebuiltHistoryThisPass
171380
+ };
170958
171381
  }
170959
171382
 
170960
171383
  // src/hooks/magic-context/transform-context-state.ts
@@ -172316,7 +172739,10 @@ function clearAutoSearchForSession(sessionId) {
172316
172739
  }
172317
172740
 
172318
172741
  // src/hooks/magic-context/transform-postprocess-phase.ts
172319
- await init_compartment_runner();
172742
+ await __promiseAll([
172743
+ init_compaction_marker_manager(),
172744
+ init_compartment_runner()
172745
+ ]);
172320
172746
 
172321
172747
  // src/hooks/magic-context/heuristic-cleanup.ts
172322
172748
  init_shared();
@@ -172590,7 +173016,7 @@ function isVisibleNoteReadPart(part) {
172590
173016
  }
172591
173017
 
172592
173018
  // src/hooks/magic-context/todo-view.ts
172593
- import { createHash as createHash4 } from "node:crypto";
173019
+ import { createHash as createHash5 } from "node:crypto";
172594
173020
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
172595
173021
  var TITLE_DONE_STATUSES = new Set(["completed"]);
172596
173022
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -172635,7 +173061,7 @@ function buildSyntheticTodoPart(stateJson) {
172635
173061
  };
172636
173062
  }
172637
173063
  function computeSyntheticCallId(stateJson) {
172638
- const hash2 = createHash4("sha256").update(stateJson).digest("hex").slice(0, 16);
173064
+ const hash2 = createHash5("sha256").update(stateJson).digest("hex").slice(0, 16);
172639
173065
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
172640
173066
  }
172641
173067
  function parseTodoState(stateJson) {
@@ -172676,26 +173102,34 @@ function logTransformTiming(sessionId, stage, startMs, extra) {
172676
173102
  }
172677
173103
 
172678
173104
  // src/hooks/magic-context/transform-postprocess-phase.ts
173105
+ var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
173106
+ var degradedCacheCountBySession = new Map;
173107
+ function resetDegradedCacheCount(sessionId) {
173108
+ degradedCacheCountBySession.delete(sessionId);
173109
+ }
172679
173110
  async function runPostTransformPhase(args) {
172680
173111
  let didMutateFromPendingOperations = false;
172681
173112
  const isExplicitFlush = args.pendingMaterializationSessions.has(args.sessionId);
173113
+ const deferredMaterializationWasPending = args.deferredMaterializationSessions.has(args.sessionId);
172682
173114
  const alreadyRanThisTurn = args.currentTurnId !== null && args.lastHeuristicsTurnId.get(args.sessionId) === args.currentTurnId;
172683
173115
  const forceMaterialization = args.fullFeatureMode && args.contextUsage.percentage >= args.forceMaterializationPercentage;
172684
173116
  const activeCompartmentRun = args.canRunCompartments ? getActiveCompartmentRun(args.sessionId) : undefined;
172685
173117
  const compartmentRunning = args.canRunCompartments && !args.awaitedCompartmentRun && activeCompartmentRun !== undefined;
172686
173118
  const emergencyBypassCompartmentGate = forceMaterialization;
172687
- const shouldReadPendingOps = isExplicitFlush || args.schedulerDecision === "execute" || forceMaterialization || compartmentRunning;
173119
+ const deferredMaterialize = args.canConsumeDeferredLate && deferredMaterializationWasPending;
173120
+ const materializationRequested = isExplicitFlush || deferredMaterialize;
173121
+ const shouldReadPendingOps = materializationRequested || args.schedulerDecision === "execute" || forceMaterialization || compartmentRunning;
172688
173122
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
172689
173123
  const hasPendingUserOps = pendingOps.length > 0;
172690
- const shouldApplyPendingOps = (args.schedulerDecision === "execute" || isExplicitFlush || forceMaterialization) && (!compartmentRunning || emergencyBypassCompartmentGate);
172691
- const shouldRunHeuristics = (!compartmentRunning || emergencyBypassCompartmentGate) && (isExplicitFlush || forceMaterialization || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
173124
+ const shouldApplyPendingOps = (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization) && (!compartmentRunning || emergencyBypassCompartmentGate);
173125
+ const shouldRunHeuristics = (!compartmentRunning || emergencyBypassCompartmentGate) && (materializationRequested || forceMaterialization || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
172692
173126
  const isCacheBustingPass = shouldApplyPendingOps || shouldRunHeuristics;
172693
173127
  if (shouldRunHeuristics) {
172694
173128
  const subagentRerun = !args.fullFeatureMode && alreadyRanThisTurn && args.schedulerDecision === "execute" && !isExplicitFlush && !forceMaterialization;
172695
- const reason = isExplicitFlush ? "explicit_flush" : forceMaterialization ? `force_materialization (${args.contextUsage.percentage.toFixed(1)}% >= ${args.forceMaterializationPercentage}%)` : subagentRerun ? `scheduler_execute_subagent_rerun (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})` : `scheduler_execute (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
173129
+ const reason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : forceMaterialization ? `force_materialization (${args.contextUsage.percentage.toFixed(1)}% >= ${args.forceMaterializationPercentage}%)` : subagentRerun ? `scheduler_execute_subagent_rerun (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})` : `scheduler_execute (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
172696
173130
  sessionLog(args.sessionId, `heuristics WILL RUN — reason=${reason}, context=${args.contextUsage.percentage.toFixed(1)}%, turn=${args.currentTurnId}`);
172697
173131
  }
172698
- if (alreadyRanThisTurn && args.schedulerDecision === "execute" && !isExplicitFlush && args.fullFeatureMode) {
173132
+ if (alreadyRanThisTurn && args.schedulerDecision === "execute" && !materializationRequested && args.fullFeatureMode) {
172699
173133
  sessionLog(args.sessionId, `transform: skipping heuristics (already ran for turn ${args.currentTurnId})`);
172700
173134
  }
172701
173135
  if (compartmentRunning && hasPendingUserOps) {
@@ -172705,9 +173139,11 @@ async function runPostTransformPhase(args) {
172705
173139
  sessionLog(args.sessionId, "transform: deferring pending ops — compartment agent in progress");
172706
173140
  }
172707
173141
  }
173142
+ let explicitMaterializedSuccessfully = false;
173143
+ let deferredMaterializedSuccessfully = false;
172708
173144
  try {
172709
173145
  if (shouldApplyPendingOps) {
172710
- const applyReason = isExplicitFlush ? "explicit_flush" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
173146
+ const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
172711
173147
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
172712
173148
  const pendingCountBefore = pendingOps.length;
172713
173149
  const tApply = performance.now();
@@ -172769,7 +173205,7 @@ async function runPostTransformPhase(args) {
172769
173205
  args.lastHeuristicsTurnId.set(args.sessionId, args.currentTurnId);
172770
173206
  }
172771
173207
  }
172772
- if (args.schedulerDecision === "execute" && !isExplicitFlush) {
173208
+ if (args.schedulerDecision === "execute" && !materializationRequested) {
172773
173209
  updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
172774
173210
  }
172775
173211
  args.batch?.finalize();
@@ -172777,6 +173213,12 @@ async function runPostTransformPhase(args) {
172777
173213
  if (args.sessionMeta.lastTransformError !== null) {
172778
173214
  updateSessionMeta(args.db, args.sessionId, { lastTransformError: null });
172779
173215
  }
173216
+ if (shouldRunHeuristics) {
173217
+ if (isExplicitFlush)
173218
+ explicitMaterializedSuccessfully = true;
173219
+ if (deferredMaterialize)
173220
+ deferredMaterializedSuccessfully = true;
173221
+ }
172780
173222
  } catch (error51) {
172781
173223
  sessionLog(args.sessionId, "transform failed applying pending operations:", error51);
172782
173224
  updateSessionMeta(args.db, args.sessionId, { lastTransformError: getErrorMessage(error51) });
@@ -172935,6 +173377,45 @@ async function runPostTransformPhase(args) {
172935
173377
  }
172936
173378
  }
172937
173379
  }
173380
+ const explicitRebuildHappened = args.historyRefreshExplicitBeforePrepare && args.rebuiltHistoryFromInitialPrepare;
173381
+ const materializationSatisfied = !deferredMaterializationWasPending || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
173382
+ const historyWasConsumedThisPass = args.historyRebuiltThisPass && (args.canConsumeDeferredLate || args.phaseJustAwaitedPublication || explicitRebuildHappened) && materializationSatisfied;
173383
+ if (args.compartmentInjectionRebuiltFromDb && args.pendingCompartmentInjection) {
173384
+ if (args.pendingCompartmentInjection.compartmentEndMessageId === null) {
173385
+ const nextCount = (degradedCacheCountBySession.get(args.sessionId) ?? 0) + 1;
173386
+ degradedCacheCountBySession.set(args.sessionId, nextCount);
173387
+ if (nextCount === DEGRADE_CACHE_WARNING_THRESHOLD) {
173388
+ sessionLog(args.sessionId, `WARNING: compartment injection cache has rebuilt with a degraded null boundary ${nextCount} consecutive times; investigate missing boundary messages`);
173389
+ }
173390
+ } else {
173391
+ degradedCacheCountBySession.delete(args.sessionId);
173392
+ }
173393
+ }
173394
+ let suppressV12HistoryDrain = false;
173395
+ if (historyWasConsumedThisPass && args.deferredHistoryRefreshSessions.has(args.sessionId)) {
173396
+ const pending = getPendingCompactionMarkerState(args.db, args.sessionId);
173397
+ if (pending) {
173398
+ const outcome = applyDeferredCompactionMarker(args.db, args.sessionId, pending, args.sessionDirectory);
173399
+ switch (outcome.kind) {
173400
+ case "applied":
173401
+ case "already-current":
173402
+ case "stale-skip":
173403
+ clearPendingCompactionMarkerStateIf(args.db, args.sessionId, pending);
173404
+ break;
173405
+ case "retryable-failure":
173406
+ sessionLog(args.sessionId, "compaction-marker drain: retryable failure; preserving deferred history refresh signal", outcome.error);
173407
+ suppressV12HistoryDrain = true;
173408
+ break;
173409
+ }
173410
+ }
173411
+ }
173412
+ const deferredHistoryDrainEligible = historyWasConsumedThisPass && !suppressV12HistoryDrain;
173413
+ if (deferredHistoryDrainEligible) {
173414
+ args.deferredHistoryRefreshSessions.delete(args.sessionId);
173415
+ }
173416
+ if (explicitMaterializedSuccessfully || deferredMaterializedSuccessfully) {
173417
+ args.deferredMaterializationSessions.delete(args.sessionId);
173418
+ }
172938
173419
  if (args.fullFeatureMode && args.autoSearch?.enabled && args.projectPath) {
172939
173420
  const visibleMemoryIds = getVisibleMemoryIds(args.db, args.sessionId) ?? undefined;
172940
173421
  try {
@@ -172957,6 +173438,7 @@ async function runPostTransformPhase(args) {
172957
173438
  sessionLog(args.sessionId, "auto-search runner failed:", error51);
172958
173439
  }
172959
173440
  }
173441
+ return { explicitMaterializedSuccessfully, deferredMaterializedSuccessfully };
172960
173442
  }
172961
173443
 
172962
173444
  // src/hooks/magic-context/nudge-placement-store.ts
@@ -173030,6 +173512,8 @@ function findLastAssistantModel(messages) {
173030
173512
  function createTransform(deps) {
173031
173513
  const loadedSessions = new Set;
173032
173514
  const lastEmergencyNotificationCount = new Map;
173515
+ const deferredHistoryRefreshSessions = deps.deferredHistoryRefreshSessions ?? new Set;
173516
+ const deferredMaterializationSessions = deps.deferredMaterializationSessions ?? new Set;
173033
173517
  return async (_input, output) => {
173034
173518
  const startTime = performance.now();
173035
173519
  const messages = output.messages;
@@ -173138,7 +173622,17 @@ function createTransform(deps) {
173138
173622
  }
173139
173623
  const historyBudgetTokens = resolveHistoryBudgetTokens(deps.historyBudgetPercentage, contextUsageEarly, deps.executeThresholdPercentage, deps.getModelKey?.(sessionId), deps.executeThresholdTokens);
173140
173624
  const schedulerDecisionEarly = resolveSchedulerDecision(deps.scheduler, sessionMeta, contextUsageEarly, sessionId, deps.getModelKey?.(sessionId));
173141
- const isCacheBusting = deps.historyRefreshSessions.has(sessionId);
173625
+ const historyRefreshExplicitBeforePrepare = deps.historyRefreshSessions.has(sessionId);
173626
+ const earlyActiveRunBlocksMaterialization = (getActiveCompartmentRun(sessionId) !== undefined || sessionMeta.compartmentInProgress) && contextUsageEarly.percentage < FORCE_MATERIALIZE_PERCENTAGE;
173627
+ const canConsumeDeferredEarly = canConsumeDeferredOnThisPass({
173628
+ schedulerDecision: schedulerDecisionEarly,
173629
+ contextPercentage: contextUsageEarly.percentage,
173630
+ justAwaitedPublication: false,
173631
+ activeRunBlocksMaterialization: earlyActiveRunBlocksMaterialization
173632
+ });
173633
+ const consumingDeferredEarly = canConsumeDeferredEarly && deferredHistoryRefreshSessions.has(sessionId);
173634
+ const isCacheBusting = historyRefreshExplicitBeforePrepare || consumingDeferredEarly;
173635
+ const historyBustThisPass = isCacheBusting;
173142
173636
  if (historianFailureState.failureCount === 0) {
173143
173637
  lastEmergencyNotificationCount.delete(sessionId);
173144
173638
  }
@@ -173178,9 +173672,10 @@ function createTransform(deps) {
173178
173672
  compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
173179
173673
  memoryEnabled: deps.memoryConfig?.enabled,
173180
173674
  autoPromote: deps.memoryConfig?.autoPromote,
173181
- onInjectionCacheCleared: (sid) => {
173182
- deps.historyRefreshSessions.add(sid);
173183
- deps.pendingMaterializationSessions.add(sid);
173675
+ preserveInjectionCacheUntilConsumed: true,
173676
+ onCompartmentStatePublished: (sid) => {
173677
+ deferredHistoryRefreshSessions.add(sid);
173678
+ deferredMaterializationSessions.add(sid);
173184
173679
  }
173185
173680
  });
173186
173681
  skipCompartmentAwaitForThisPass = true;
@@ -173213,11 +173708,15 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
173213
173708
  logTransformTiming(sessionId, "emergencyRecoveryBlock", tFirstPass);
173214
173709
  const projectIdentity = deps.memoryConfig?.enabled ? resolveProjectIdentity(compartmentDirectory || process.cwd()) : undefined;
173215
173710
  let pendingCompartmentInjection = null;
173711
+ let rebuiltHistoryFromInitialPrepare = false;
173216
173712
  if (fullFeatureMode) {
173217
173713
  const tInj = performance.now();
173218
173714
  pendingCompartmentInjection = prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectIdentity, deps.memoryConfig?.injectionBudgetTokens, deps.experimentalTemporalAwareness);
173219
173715
  logTransformTiming(sessionId, "prepareCompartmentInjection", tInj);
173220
173716
  if (isCacheBusting) {
173717
+ rebuiltHistoryFromInitialPrepare = true;
173718
+ }
173719
+ if (historyRefreshExplicitBeforePrepare) {
173221
173720
  deps.historyRefreshSessions.delete(sessionId);
173222
173721
  }
173223
173722
  }
@@ -173344,7 +173843,9 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
173344
173843
  projectPath: projectIdentity,
173345
173844
  injectionBudgetTokens: deps.memoryConfig?.injectionBudgetTokens,
173346
173845
  getNotificationParams: rawGetNotifParams ? () => rawGetNotifParams(sessionId) : undefined,
173347
- cacheAlreadyBusting: isCacheBusting || schedulerDecisionEarly === "execute",
173846
+ safeForBackgroundCompression: isCacheBusting || schedulerDecisionEarly === "execute",
173847
+ suppressBackgroundCompressionThisPass: historyBustThisPass,
173848
+ deferredHistoryRefreshSessions,
173348
173849
  skipAwaitForThisPass: skipCompartmentAwaitForThisPass,
173349
173850
  experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
173350
173851
  experimentalUserMemories: deps.experimentalUserMemories,
@@ -173355,9 +173856,9 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
173355
173856
  compressorCooldownMs: deps.compressorCooldownMs,
173356
173857
  memoryEnabled: deps.memoryConfig?.enabled,
173357
173858
  autoPromote: deps.memoryConfig?.autoPromote,
173358
- onInjectionCacheCleared: (sid) => {
173359
- deps.historyRefreshSessions.add(sid);
173360
- deps.pendingMaterializationSessions.add(sid);
173859
+ onCompartmentStatePublished: (sid) => {
173860
+ deferredHistoryRefreshSessions.add(sid);
173861
+ deferredMaterializationSessions.add(sid);
173361
173862
  }
173362
173863
  });
173363
173864
  pendingCompartmentInjection = compartmentPhase.pendingCompartmentInjection;
@@ -173365,6 +173866,15 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
173365
173866
  const compartmentInProgress = compartmentPhase.compartmentInProgress;
173366
173867
  sessionMeta = { ...sessionMeta, compartmentInProgress };
173367
173868
  logTransformTiming(sessionId, "compartmentPhase", tCompartmentPhase);
173869
+ const lateActiveRunBlocksMaterialization = getActiveCompartmentRun(sessionId) !== undefined && contextUsageEarly.percentage < FORCE_MATERIALIZE_PERCENTAGE;
173870
+ const canConsumeDeferredLate = canConsumeDeferredOnThisPass({
173871
+ schedulerDecision: schedulerDecisionEarly,
173872
+ contextPercentage: contextUsageEarly.percentage,
173873
+ justAwaitedPublication: compartmentPhase.justAwaitedPublication,
173874
+ activeRunBlocksMaterialization: lateActiveRunBlocksMaterialization
173875
+ });
173876
+ const wasEmergencyBlock = contextUsageEarly.percentage >= FORCE_MATERIALIZE_PERCENTAGE && compartmentPhase.justAwaitedPublication;
173877
+ const historyRebuiltThisPass = wasEmergencyBlock ? compartmentPhase.rebuiltHistoryThisPass : rebuiltHistoryFromInitialPrepare || compartmentPhase.rebuiltHistoryThisPass;
173368
173878
  const tPostProcess = performance.now();
173369
173879
  await runPostTransformPhase({
173370
173880
  sessionId,
@@ -173380,10 +173890,18 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
173380
173890
  fullFeatureMode,
173381
173891
  canRunCompartments,
173382
173892
  awaitedCompartmentRun,
173893
+ phaseJustAwaitedPublication: compartmentPhase.justAwaitedPublication,
173383
173894
  compartmentInProgress,
173895
+ historyRefreshExplicitBeforePrepare,
173896
+ compartmentInjectionRebuiltFromDb: pendingCompartmentInjection?.rebuiltFromDb === true,
173897
+ rebuiltHistoryFromInitialPrepare,
173898
+ historyRebuiltThisPass,
173899
+ canConsumeDeferredLate,
173384
173900
  sessionMeta,
173385
173901
  currentTurnId,
173386
173902
  pendingMaterializationSessions: deps.pendingMaterializationSessions,
173903
+ deferredHistoryRefreshSessions,
173904
+ deferredMaterializationSessions,
173387
173905
  lastHeuristicsTurnId: deps.lastHeuristicsTurnId,
173388
173906
  autoDropToolAge: deps.autoDropToolAge,
173389
173907
  dropToolStructure: deps.dropToolStructure ?? true,
@@ -173397,6 +173915,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
173397
173915
  forceMaterializationPercentage: FORCE_MATERIALIZE_PERCENTAGE,
173398
173916
  hasRecentReduceCall,
173399
173917
  projectPath: deps.projectPath,
173918
+ sessionDirectory,
173400
173919
  autoSearch: deps.autoSearch,
173401
173920
  cavemanTextCompression: deps.ctxReduceEnabled === false && !reducedMode ? deps.cavemanTextCompression : undefined,
173402
173921
  liveProviderID
@@ -173804,6 +174323,15 @@ function createEventHandler2(deps) {
173804
174323
  } catch (error51) {
173805
174324
  sessionLog(sessionId, "event session.compacted marker cleanup failed:", error51);
173806
174325
  }
174326
+ try {
174327
+ const pending = getPendingCompactionMarkerState(deps.db, sessionId);
174328
+ if (pending) {
174329
+ clearPendingCompactionMarkerStateIf(deps.db, sessionId, pending);
174330
+ }
174331
+ } catch (error51) {
174332
+ sessionLog(sessionId, "event session.compacted pending-marker cleanup failed:", error51);
174333
+ }
174334
+ resetDegradedCacheCount(sessionId);
173807
174335
  clearMessageTokensCache(sessionId);
173808
174336
  deps.onSessionCacheInvalidated?.(sessionId);
173809
174337
  return;
@@ -173820,6 +174348,7 @@ function createEventHandler2(deps) {
173820
174348
  } catch (error51) {
173821
174349
  sessionLog(sessionId, "event session.deleted persistence failed:", error51);
173822
174350
  }
174351
+ resetDegradedCacheCount(sessionId);
173823
174352
  deps.onSessionCacheInvalidated?.(sessionId);
173824
174353
  deps.onSessionDeleted?.(sessionId);
173825
174354
  deps.contextUsageMap.delete(sessionId);
@@ -174028,6 +174557,10 @@ function applyStickySnapshotCache(sessionId, fresh) {
174028
174557
  cache.delete(sessionId);
174029
174558
  return fresh;
174030
174559
  }
174560
+ if (!hasInFlightEvidence(fresh)) {
174561
+ cache.delete(sessionId);
174562
+ return fresh;
174563
+ }
174031
174564
  return {
174032
174565
  ...fresh,
174033
174566
  usagePercentage: cached2.snapshot.usagePercentage,
@@ -174041,6 +174574,9 @@ function applyStickySnapshotCache(sessionId, fresh) {
174041
174574
  toolDefinitionTokens: cached2.snapshot.toolDefinitionTokens
174042
174575
  };
174043
174576
  }
174577
+ function hasInFlightEvidence(snapshot) {
174578
+ return snapshot.compartmentInProgress || snapshot.historianRunning || snapshot.pendingOpsCount > 0;
174579
+ }
174044
174580
  function clearSidebarSnapshotCache(sessionId) {
174045
174581
  cache.delete(sessionId);
174046
174582
  }
@@ -174141,8 +174677,10 @@ function createEventHook(args) {
174141
174677
  args.recentReduceBySession.delete(sessionId);
174142
174678
  args.toolUsageSinceUserTurn.delete(sessionId);
174143
174679
  args.historyRefreshSessions.delete(sessionId);
174680
+ args.deferredHistoryRefreshSessions.delete(sessionId);
174144
174681
  args.systemPromptRefreshSessions.delete(sessionId);
174145
174682
  args.pendingMaterializationSessions.delete(sessionId);
174683
+ args.deferredMaterializationSessions.delete(sessionId);
174146
174684
  args.lastHeuristicsTurnId.delete(sessionId);
174147
174685
  args.commitSeenLastPass?.delete(sessionId);
174148
174686
  clearNoteNudgeState(args.db, sessionId);
@@ -174203,9 +174741,9 @@ function createToolExecuteAfterHook(args) {
174203
174741
  init_send_session_notification();
174204
174742
 
174205
174743
  // src/hooks/magic-context/system-prompt-hash.ts
174206
- import { createHash as createHash5 } from "node:crypto";
174744
+ import { createHash as createHash6 } from "node:crypto";
174207
174745
  import { existsSync as existsSync13, readFileSync as readFileSync9, realpathSync } from "node:fs";
174208
- import { join as join22, resolve as resolve4, sep } from "node:path";
174746
+ import { join as join21, resolve as resolve4, sep } from "node:path";
174209
174747
 
174210
174748
  // src/agents/magic-context-prompt.ts
174211
174749
  function getToolHistoryGuidance(dropToolStructure) {
@@ -174318,7 +174856,7 @@ var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
174318
174856
  function readProjectDocs(directory) {
174319
174857
  const sections = [];
174320
174858
  for (const filename of DOC_FILES) {
174321
- const filePath = join22(directory, filename);
174859
+ const filePath = join21(directory, filename);
174322
174860
  try {
174323
174861
  if (existsSync13(filePath)) {
174324
174862
  const content = readFileSync9(filePath, "utf-8").trim();
@@ -174508,7 +175046,7 @@ ${sections.join(`
174508
175046
  `);
174509
175047
  if (systemContent.length === 0)
174510
175048
  return;
174511
- const currentHash = createHash5("md5").update(systemContent).digest("hex");
175049
+ const currentHash = createHash6("md5").update(systemContent).digest("hex");
174512
175050
  if (!sessionMetaEarly) {
174513
175051
  return;
174514
175052
  }
@@ -174593,8 +175131,21 @@ function createMagicContextHook(deps) {
174593
175131
  const historianFallbackModels = resolveFallbackChain(HISTORIAN_AGENT, deps.config.historian?.fallback_models);
174594
175132
  const nudgePlacements = createNudgePlacementStore(db);
174595
175133
  const historyRefreshSessions = deps.liveSessionState?.historyRefreshSessions ?? new Set;
175134
+ const deferredHistoryRefreshSessions = deps.liveSessionState?.deferredHistoryRefreshSessions ?? new Set;
175135
+ try {
175136
+ const sessionsWithPending = getSessionsWithPendingMarker(db);
175137
+ if (sessionsWithPending.length > 0) {
175138
+ for (const sid of sessionsWithPending) {
175139
+ deferredHistoryRefreshSessions.add(sid);
175140
+ }
175141
+ log(`[magic-context] rehydrated ${sessionsWithPending.length} session(s) with pending compaction-marker drain at hook init`);
175142
+ }
175143
+ } catch (error51) {
175144
+ log("[magic-context] hook init: pending-marker rehydration failed:", error51);
175145
+ }
174596
175146
  const systemPromptRefreshSessions = deps.liveSessionState?.systemPromptRefreshSessions ?? new Set;
174597
175147
  const pendingMaterializationSessions = deps.liveSessionState?.pendingMaterializationSessions ?? new Set;
175148
+ const deferredMaterializationSessions = deps.liveSessionState?.deferredMaterializationSessions ?? new Set;
174598
175149
  const lastHeuristicsTurnId = new Map;
174599
175150
  const commitSeenLastPass = new Map;
174600
175151
  const variantBySession = deps.liveSessionState?.variantBySession ?? new Map;
@@ -174635,7 +175186,9 @@ function createMagicContextHook(deps) {
174635
175186
  dropToolStructure: deps.config.drop_tool_structure ?? true,
174636
175187
  clearReasoningAge: deps.config.clear_reasoning_age ?? 50,
174637
175188
  historyRefreshSessions,
175189
+ deferredHistoryRefreshSessions,
174638
175190
  pendingMaterializationSessions,
175191
+ deferredMaterializationSessions,
174639
175192
  lastHeuristicsTurnId,
174640
175193
  commitSeenLastPass,
174641
175194
  client: deps.client,
@@ -174708,7 +175261,7 @@ function createMagicContextHook(deps) {
174708
175261
  return;
174709
175262
  }
174710
175263
  try {
174711
- checkScheduleAndEnqueue(db, dreaming.schedule);
175264
+ checkScheduleAndEnqueue(db, dreaming.schedule, projectPath);
174712
175265
  lastScheduleCheckMs = now;
174713
175266
  } catch (error51) {
174714
175267
  log("[dreamer] scheduled enqueue check failed:", error51);
@@ -174729,7 +175282,8 @@ function createMagicContextHook(deps) {
174729
175282
  token_budget: deps.config.dreamer.pin_key_files.token_budget,
174730
175283
  min_reads: deps.config.dreamer.pin_key_files.min_reads
174731
175284
  } : undefined,
174732
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, deps.config.dreamer?.fallback_models)
175285
+ fallbackModels: resolveFallbackChain(DREAMER_AGENT, deps.config.dreamer?.fallback_models),
175286
+ projectIdentity: projectPath
174733
175287
  }).catch((error51) => {
174734
175288
  log("[dreamer] scheduled queue processing failed:", error51);
174735
175289
  });
@@ -174773,9 +175327,12 @@ function createMagicContextHook(deps) {
174773
175327
  historianTwoPass: deps.config.historian?.two_pass === true,
174774
175328
  memoryEnabled: deps.config.memory?.enabled,
174775
175329
  autoPromote: deps.config.memory?.auto_promote ?? true,
174776
- onInjectionCacheCleared: (sid) => {
175330
+ onCompartmentStatePublished: (sid) => {
174777
175331
  historyRefreshSessions.add(sid);
174778
175332
  pendingMaterializationSessions.add(sid);
175333
+ },
175334
+ onDeferredMarkerPending: (sid) => {
175335
+ deferredHistoryRefreshSessions.add(sid);
174779
175336
  }
174780
175337
  }, options),
174781
175338
  sendNotification: async (sessionId, text, params) => {
@@ -174841,8 +175398,10 @@ function createMagicContextHook(deps) {
174841
175398
  recentReduceBySession,
174842
175399
  toolUsageSinceUserTurn,
174843
175400
  historyRefreshSessions,
175401
+ deferredHistoryRefreshSessions,
174844
175402
  systemPromptRefreshSessions,
174845
175403
  pendingMaterializationSessions,
175404
+ deferredMaterializationSessions,
174846
175405
  lastHeuristicsTurnId,
174847
175406
  commitSeenLastPass,
174848
175407
  client: deps.client,
@@ -175536,7 +176095,7 @@ function registerRpcHandlers(rpcServer, args) {
175536
176095
  memoryEnabled: config2.memory?.enabled,
175537
176096
  autoPromote: config2.memory?.auto_promote ?? true,
175538
176097
  getNotificationParams: () => getNotificationParams(sessionId),
175539
- onInjectionCacheCleared: (sid) => {
176098
+ onCompartmentStatePublished: (sid) => {
175540
176099
  liveSessionState.historyRefreshSessions.add(sid);
175541
176100
  liveSessionState.pendingMaterializationSessions.add(sid);
175542
176101
  }
@@ -175547,8 +176106,9 @@ function registerRpcHandlers(rpcServer, args) {
175547
176106
  });
175548
176107
  return { ok: true };
175549
176108
  });
175550
- rpcServer.handle("pending-notifications", async () => {
175551
- const notifications = drainNotifications();
176109
+ rpcServer.handle("pending-notifications", async (params) => {
176110
+ const lastReceivedId = Number(params.lastReceivedId ?? 0);
176111
+ const notifications = drainNotifications(Number.isFinite(lastReceivedId) ? lastReceivedId : 0);
175552
176112
  return { messages: notifications };
175553
176113
  });
175554
176114
  }
@@ -176506,19 +177066,68 @@ init_models_dev_cache();
176506
177066
 
176507
177067
  // src/shared/rpc-server.ts
176508
177068
  init_logger();
176509
- import { mkdirSync as mkdirSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "node:fs";
177069
+ import {
177070
+ mkdirSync as mkdirSync8,
177071
+ readdirSync,
177072
+ readFileSync as readFileSync10,
177073
+ renameSync as renameSync2,
177074
+ unlinkSync as unlinkSync3,
177075
+ writeFileSync as writeFileSync7
177076
+ } from "node:fs";
176510
177077
  import { createServer } from "node:http";
176511
- import { dirname as dirname6 } from "node:path";
177078
+ import { dirname as dirname7 } from "node:path";
176512
177079
 
176513
177080
  // src/shared/rpc-utils.ts
176514
- import { createHash as createHash6 } from "node:crypto";
176515
- import { join as join23 } from "node:path";
177081
+ import { createHash as createHash7 } from "node:crypto";
177082
+ import { join as join22 } from "node:path";
176516
177083
  function projectHash(directory) {
176517
177084
  const normalized = directory.replace(/\/+$/, "");
176518
- return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
177085
+ return createHash7("sha256").update(normalized).digest("hex").slice(0, 16);
177086
+ }
177087
+ function rpcPortDir(storageDir, directory) {
177088
+ return join22(storageDir, "rpc", projectHash(directory));
177089
+ }
177090
+ function rpcPortFilePath(storageDir, directory, pid = process.pid) {
177091
+ return join22(rpcPortDir(storageDir, directory), `port-${pid}.json`);
177092
+ }
177093
+ function isPidAlive(pid) {
177094
+ if (!Number.isInteger(pid) || pid <= 0)
177095
+ return false;
177096
+ try {
177097
+ process.kill(pid, 0);
177098
+ return true;
177099
+ } catch (err) {
177100
+ return err.code === "EPERM";
177101
+ }
177102
+ }
177103
+ function parseRpcPortFile(content, fallbackPid = 0) {
177104
+ const trimmed = content.trim();
177105
+ if (!trimmed)
177106
+ return null;
177107
+ if (trimmed.startsWith("{")) {
177108
+ try {
177109
+ const parsed = JSON.parse(trimmed);
177110
+ const port2 = Number(parsed.port);
177111
+ const pid = Number(parsed.pid);
177112
+ const startedAt = Number(parsed.started_at);
177113
+ if (!isValidPort(port2) || !Number.isInteger(pid) || pid <= 0)
177114
+ return null;
177115
+ return {
177116
+ port: port2,
177117
+ pid,
177118
+ started_at: Number.isFinite(startedAt) ? startedAt : 0
177119
+ };
177120
+ } catch {
177121
+ return null;
177122
+ }
177123
+ }
177124
+ const port = Number.parseInt(trimmed, 10);
177125
+ if (!isValidPort(port))
177126
+ return null;
177127
+ return { port, pid: fallbackPid, started_at: 0 };
176519
177128
  }
176520
- function rpcPortFilePath(storageDir, directory) {
176521
- return join23(storageDir, "rpc", projectHash(directory), "port");
177129
+ function isValidPort(port) {
177130
+ return Number.isInteger(port) && port > 0 && port <= 65535;
176522
177131
  }
176523
177132
 
176524
177133
  // src/shared/rpc-server.ts
@@ -176527,8 +177136,11 @@ class MagicContextRpcServer {
176527
177136
  port = 0;
176528
177137
  handlers = new Map;
176529
177138
  portFilePath;
177139
+ portDir;
177140
+ startedAt = Date.now();
176530
177141
  constructor(storageDir, directory) {
176531
177142
  this.portFilePath = rpcPortFilePath(storageDir, directory);
177143
+ this.portDir = rpcPortDir(storageDir, directory);
176532
177144
  }
176533
177145
  handle(method, handler) {
176534
177146
  this.handlers.set(method, handler);
@@ -176549,10 +177161,15 @@ class MagicContextRpcServer {
176549
177161
  this.port = addr.port;
176550
177162
  this.server = server;
176551
177163
  try {
176552
- const dir = dirname6(this.portFilePath);
176553
- mkdirSync7(dir, { recursive: true });
177164
+ this.warnIfOtherLiveInstance();
177165
+ const dir = dirname7(this.portFilePath);
177166
+ mkdirSync8(dir, { recursive: true });
176554
177167
  const tmpPath = `${this.portFilePath}.tmp`;
176555
- writeFileSync7(tmpPath, String(this.port), "utf-8");
177168
+ writeFileSync7(tmpPath, JSON.stringify({
177169
+ port: this.port,
177170
+ pid: process.pid,
177171
+ started_at: this.startedAt
177172
+ }), "utf-8");
176556
177173
  renameSync2(tmpPath, this.portFilePath);
176557
177174
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
176558
177175
  } catch (err) {
@@ -176563,6 +177180,19 @@ class MagicContextRpcServer {
176563
177180
  server.unref();
176564
177181
  });
176565
177182
  }
177183
+ warnIfOtherLiveInstance() {
177184
+ try {
177185
+ for (const entry of readdirSync(this.portDir)) {
177186
+ if (!entry.startsWith("port-") || !entry.endsWith(".json"))
177187
+ continue;
177188
+ const record2 = parseRpcPortFile(readFileSync10(`${this.portDir}/${entry}`, "utf-8"));
177189
+ if (!record2 || record2.pid === process.pid || !isPidAlive(record2.pid))
177190
+ continue;
177191
+ log(`[rpc] another Magic Context RPC server is active for this project (pid ${record2.pid}, port ${record2.port}); starting separate instance on a new port`);
177192
+ return;
177193
+ }
177194
+ } catch {}
177195
+ }
176566
177196
  stop() {
176567
177197
  if (this.server) {
176568
177198
  this.server.close();