@cortexkit/pi-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.
package/dist/index.js CHANGED
@@ -11277,7 +11277,7 @@ function finalize(ctx, schema) {
11277
11277
  result.$schema = "http://json-schema.org/draft-07/schema#";
11278
11278
  } else if (ctx.target === "draft-04") {
11279
11279
  result.$schema = "http://json-schema.org/draft-04/schema#";
11280
- } else if (ctx.target === "openapi-3.0") {} else {}
11280
+ } else if (ctx.target === "openapi-3.0") {}
11281
11281
  if (ctx.external?.uri) {
11282
11282
  const id = ctx.external.registry.get(schema)?.id;
11283
11283
  if (!id)
@@ -11538,7 +11538,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
11538
11538
  if (val === undefined) {
11539
11539
  if (ctx.unrepresentable === "throw") {
11540
11540
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
11541
- } else {}
11541
+ }
11542
11542
  } else if (typeof val === "bigint") {
11543
11543
  if (ctx.unrepresentable === "throw") {
11544
11544
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -14891,10 +14891,63 @@ var init_magic_context = __esm(() => {
14891
14891
  });
14892
14892
  });
14893
14893
 
14894
- // ../plugin/src/shared/logger.ts
14895
- import * as fs from "node:fs";
14894
+ // ../plugin/src/shared/harness.ts
14895
+ function setHarness(value) {
14896
+ if (harnessLocked && currentHarness !== value) {
14897
+ throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
14898
+ }
14899
+ currentHarness = value;
14900
+ harnessLocked = true;
14901
+ }
14902
+ function getHarness() {
14903
+ return currentHarness;
14904
+ }
14905
+ var currentHarness = "opencode", harnessLocked = false;
14906
+
14907
+ // ../plugin/src/shared/data-path.ts
14896
14908
  import * as os from "node:os";
14897
14909
  import * as path from "node:path";
14910
+ function getDataDir() {
14911
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
14912
+ }
14913
+ function getMagicContextTempDir(harness = getHarness()) {
14914
+ return path.join(os.tmpdir(), harness, "magic-context");
14915
+ }
14916
+ function getMagicContextLogPath(harness = getHarness()) {
14917
+ return path.join(getMagicContextTempDir(harness), "magic-context.log");
14918
+ }
14919
+ function getProjectMagicContextDir(directory) {
14920
+ return path.join(directory, ".opencode", "magic-context");
14921
+ }
14922
+ function getProjectMagicContextHistorianDir(directory) {
14923
+ return path.join(getProjectMagicContextDir(directory), "historian");
14924
+ }
14925
+ function getOpenCodeStorageDir() {
14926
+ return path.join(getDataDir(), "opencode", "storage");
14927
+ }
14928
+ function getMagicContextStorageDir() {
14929
+ return path.join(getDataDir(), "cortexkit", "magic-context");
14930
+ }
14931
+ function getLegacyOpenCodeMagicContextStorageDir() {
14932
+ return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
14933
+ }
14934
+ function getCacheDir() {
14935
+ return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
14936
+ }
14937
+ var init_data_path = () => {};
14938
+
14939
+ // ../plugin/src/shared/logger.ts
14940
+ import * as fs from "node:fs";
14941
+ import * as path2 from "node:path";
14942
+ function ensureDir(filePath) {
14943
+ const dir = path2.dirname(filePath);
14944
+ if (dir === lastEnsuredDir)
14945
+ return;
14946
+ try {
14947
+ fs.mkdirSync(dir, { recursive: true });
14948
+ lastEnsuredDir = dir;
14949
+ } catch {}
14950
+ }
14898
14951
  function flush() {
14899
14952
  if (flushTimer) {
14900
14953
  clearTimeout(flushTimer);
@@ -14905,6 +14958,8 @@ function flush() {
14905
14958
  const data = buffer.join("");
14906
14959
  buffer = [];
14907
14960
  try {
14961
+ const logFile = getMagicContextLogPath();
14962
+ ensureDir(logFile);
14908
14963
  fs.appendFileSync(logFile, data);
14909
14964
  } catch {}
14910
14965
  }
@@ -14935,9 +14990,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
14935
14990
  function sessionLog(sessionId, message, data) {
14936
14991
  log(`[magic-context][${sessionId}] ${message}`, data);
14937
14992
  }
14938
- var logFile, isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
14993
+ var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, lastEnsuredDir = null;
14939
14994
  var init_logger = __esm(() => {
14940
- logFile = path.join(os.tmpdir(), "magic-context.log");
14995
+ init_data_path();
14941
14996
  buffer = [];
14942
14997
  if (!isTestEnv) {
14943
14998
  process.on("exit", flush);
@@ -14961,30 +15016,47 @@ function cosineSimilarity(a, b) {
14961
15016
  return denominator === 0 ? 0 : dotProduct / denominator;
14962
15017
  }
14963
15018
 
14964
- // ../plugin/src/shared/data-path.ts
14965
- import * as os2 from "node:os";
14966
- import * as path2 from "node:path";
14967
- function getDataDir() {
14968
- return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
14969
- }
14970
- function getOpenCodeStorageDir() {
14971
- return path2.join(getDataDir(), "opencode", "storage");
15019
+ // ../plugin/src/features/magic-context/memory/normalize-hash.ts
15020
+ import { createHash } from "node:crypto";
15021
+ function normalizeMemoryContent(content) {
15022
+ return content.toLowerCase().replace(/\s+/g, " ").trim();
14972
15023
  }
14973
- function getMagicContextStorageDir() {
14974
- return path2.join(getDataDir(), "cortexkit", "magic-context");
15024
+ function computeNormalizedHash(content) {
15025
+ const normalized = normalizeMemoryContent(content);
15026
+ return createHash("md5").update(normalized).digest("hex");
14975
15027
  }
14976
- function getLegacyOpenCodeMagicContextStorageDir() {
14977
- return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15028
+ var init_normalize_hash = () => {};
15029
+
15030
+ // ../plugin/src/features/magic-context/memory/embedding-identity.ts
15031
+ function normalizeEndpoint(endpoint) {
15032
+ return endpoint?.trim().replace(/\/+$/, "") ?? "";
14978
15033
  }
14979
- function getCacheDir() {
14980
- return process.env.XDG_CACHE_HOME ?? path2.join(os2.homedir(), ".cache");
15034
+ function getEmbeddingProviderIdentity(config2) {
15035
+ if (config2.provider === "off") {
15036
+ return "embedding-provider:off";
15037
+ }
15038
+ const identityInput = config2.provider === "openai-compatible" ? {
15039
+ provider: "openai-compatible",
15040
+ model: config2.model.trim(),
15041
+ endpoint: normalizeEndpoint(config2.endpoint),
15042
+ apiKeyPresent: Boolean(config2.api_key?.trim())
15043
+ } : {
15044
+ provider: "local",
15045
+ model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
15046
+ endpoint: "",
15047
+ apiKeyPresent: false
15048
+ };
15049
+ return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
14981
15050
  }
14982
- var init_data_path = () => {};
15051
+ var init_embedding_identity = __esm(() => {
15052
+ init_magic_context();
15053
+ init_normalize_hash();
15054
+ });
14983
15055
 
14984
15056
  // ../plugin/src/features/magic-context/memory/embedding-local.ts
14985
- import { mkdirSync } from "node:fs";
15057
+ import { mkdirSync as mkdirSync2 } from "node:fs";
14986
15058
  import { open, stat, unlink, writeFile } from "node:fs/promises";
14987
- import { join as join3 } from "node:path";
15059
+ import { join as join2 } from "node:path";
14988
15060
  async function acquireModelLoadLock(lockPath) {
14989
15061
  const waitStart = Date.now();
14990
15062
  while (true) {
@@ -15099,7 +15171,7 @@ class LocalEmbeddingProvider {
15099
15171
  initPromise = null;
15100
15172
  constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
15101
15173
  this.model = model;
15102
- this.modelId = `local:${model}`;
15174
+ this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
15103
15175
  }
15104
15176
  async initialize() {
15105
15177
  if (this.pipeline) {
@@ -15118,15 +15190,15 @@ class LocalEmbeddingProvider {
15118
15190
  if (LogLevel && "ERROR" in LogLevel) {
15119
15191
  env.logLevel = LogLevel.ERROR;
15120
15192
  }
15121
- const modelCacheDir = join3(getMagicContextStorageDir(), "models");
15193
+ const modelCacheDir = join2(getMagicContextStorageDir(), "models");
15122
15194
  try {
15123
- mkdirSync(modelCacheDir, { recursive: true });
15195
+ mkdirSync2(modelCacheDir, { recursive: true });
15124
15196
  env.cacheDir = modelCacheDir;
15125
15197
  } catch {
15126
15198
  log("[magic-context] could not create model cache dir, using library default");
15127
15199
  }
15128
15200
  const createPipeline = transformersModule.pipeline;
15129
- const lockPath = join3(modelCacheDir, ".load.lock");
15201
+ const lockPath = join2(modelCacheDir, ".load.lock");
15130
15202
  const releaseLock = await acquireModelLoadLock(lockPath);
15131
15203
  const stopHeartbeat = startLockHeartbeat(lockPath);
15132
15204
  try {
@@ -15241,12 +15313,13 @@ var init_embedding_local = __esm(() => {
15241
15313
  init_magic_context();
15242
15314
  init_data_path();
15243
15315
  init_logger();
15316
+ init_embedding_identity();
15244
15317
  STALE_LOCK_MS = 3 * 60000;
15245
15318
  MAX_LOCK_WAIT_MS = 5 * 60000;
15246
15319
  });
15247
15320
 
15248
15321
  // ../plugin/src/features/magic-context/memory/embedding-openai.ts
15249
- function normalizeEndpoint(endpoint) {
15322
+ function normalizeEndpoint2(endpoint) {
15250
15323
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
15251
15324
  }
15252
15325
 
@@ -15261,10 +15334,15 @@ class OpenAICompatibleEmbeddingProvider {
15261
15334
  openLogged = false;
15262
15335
  halfOpenProbeInFlight = false;
15263
15336
  constructor(options) {
15264
- this.endpoint = normalizeEndpoint(options.endpoint);
15337
+ this.endpoint = normalizeEndpoint2(options.endpoint);
15265
15338
  this.model = options.model?.trim() ?? "";
15266
15339
  this.apiKey = options.apiKey?.trim() ?? "";
15267
- this.modelId = `openai-compat:${this.endpoint}:${this.model}`;
15340
+ this.modelId = getEmbeddingProviderIdentity({
15341
+ provider: "openai-compatible",
15342
+ endpoint: this.endpoint,
15343
+ model: this.model,
15344
+ ...this.apiKey ? { api_key: this.apiKey } : {}
15345
+ });
15268
15346
  }
15269
15347
  async initialize() {
15270
15348
  if (this.initialized)
@@ -15446,20 +15524,10 @@ class OpenAICompatibleEmbeddingProvider {
15446
15524
  var FAILURE_THRESHOLD = 3, FAILURE_WINDOW_MS = 60000, OPEN_DURATION_MS, FETCH_TIMEOUT_MS = 30000;
15447
15525
  var init_embedding_openai = __esm(() => {
15448
15526
  init_logger();
15527
+ init_embedding_identity();
15449
15528
  OPEN_DURATION_MS = 5 * 60000;
15450
15529
  });
15451
15530
 
15452
- // ../plugin/src/features/magic-context/memory/normalize-hash.ts
15453
- import { createHash } from "node:crypto";
15454
- function normalizeMemoryContent(content) {
15455
- return content.toLowerCase().replace(/\s+/g, " ").trim();
15456
- }
15457
- function computeNormalizedHash(content) {
15458
- const normalized = normalizeMemoryContent(content);
15459
- return createHash("md5").update(normalized).digest("hex");
15460
- }
15461
- var init_normalize_hash = () => {};
15462
-
15463
15531
  // ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
15464
15532
  var exports_storage_memory_embeddings = {};
15465
15533
  __export(exports_storage_memory_embeddings, {
@@ -15604,17 +15672,8 @@ function resolveEmbeddingConfig(config2) {
15604
15672
  }
15605
15673
  return { provider: "off" };
15606
15674
  }
15607
- function resolveModelId(config2) {
15608
- if (config2.provider === "off") {
15609
- return "off";
15610
- }
15611
- if (config2.provider === "openai-compatible") {
15612
- const endpoint = config2.endpoint.trim();
15613
- const model = config2.model.trim();
15614
- const keyHash = config2.api_key ? computeNormalizedHash(config2.api_key) : "nokey";
15615
- return `openai-compat:${endpoint}:${model}:${keyHash}`;
15616
- }
15617
- return config2.model.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL;
15675
+ function resolveProviderIdentity(config2) {
15676
+ return getEmbeddingProviderIdentity(config2);
15618
15677
  }
15619
15678
  function createProvider(config2) {
15620
15679
  if (config2.provider === "off") {
@@ -15638,10 +15697,10 @@ function getOrCreateProvider() {
15638
15697
  }
15639
15698
  function initializeEmbedding(config2) {
15640
15699
  const nextConfig = resolveEmbeddingConfig(config2);
15641
- const nextModelId = resolveModelId(nextConfig);
15700
+ const nextProviderIdentity = resolveProviderIdentity(nextConfig);
15642
15701
  const previousProvider = provider;
15643
- const previousModelId = previousProvider?.modelId ?? resolveModelId(embeddingConfig);
15644
- if (previousModelId === nextModelId) {
15702
+ const previousProviderIdentity = previousProvider?.modelId ?? resolveProviderIdentity(embeddingConfig);
15703
+ if (previousProviderIdentity === nextProviderIdentity) {
15645
15704
  embeddingConfig = nextConfig;
15646
15705
  return;
15647
15706
  }
@@ -15789,9 +15848,9 @@ var DEFAULT_EMBEDDING_CONFIG, embeddingConfig, provider = null, loadUnembeddedMe
15789
15848
  var init_embedding = __esm(() => {
15790
15849
  init_magic_context();
15791
15850
  init_logger();
15851
+ init_embedding_identity();
15792
15852
  init_embedding_local();
15793
15853
  init_embedding_openai();
15794
- init_normalize_hash();
15795
15854
  init_storage_memory_embeddings();
15796
15855
  DEFAULT_EMBEDDING_CONFIG = {
15797
15856
  provider: "local",
@@ -15983,7 +16042,7 @@ var exports_native_binding = {};
15983
16042
  __export(exports_native_binding, {
15984
16043
  resolveBetterSqliteNativeBinding: () => resolveBetterSqliteNativeBinding
15985
16044
  });
15986
- import { existsSync, mkdirSync as mkdirSync2, writeFileSync } from "node:fs";
16045
+ import { existsSync, mkdirSync as mkdirSync3, writeFileSync } from "node:fs";
15987
16046
  import { createRequire } from "node:module";
15988
16047
  import * as path4 from "node:path";
15989
16048
  function logInfo(message) {
@@ -16083,7 +16142,7 @@ async function resolveBetterSqliteNativeBinding() {
16083
16142
  }
16084
16143
  logWarn(`cached binary at ${cachedPath} has wrong ABI (${cachedProbe.actual ?? "unknown"} != ${expected}); refetching`);
16085
16144
  }
16086
- mkdirSync2(path4.dirname(cachedPath), { recursive: true });
16145
+ mkdirSync3(path4.dirname(cachedPath), { recursive: true });
16087
16146
  const nodeFileBytes = await downloadElectronPrebuild(pkgVersion, expected);
16088
16147
  writeFileSync(cachedPath, nodeFileBytes);
16089
16148
  logInfo(`cached Electron prebuild at ${cachedPath} (${(nodeFileBytes.length / 1024).toFixed(1)} KB)`);
@@ -16151,9 +16210,9 @@ __export(exports_read_session_db, {
16151
16210
  findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
16152
16211
  closeReadOnlySessionDb: () => closeReadOnlySessionDb
16153
16212
  });
16154
- import { join as join5 } from "node:path";
16213
+ import { join as join4 } from "node:path";
16155
16214
  function getOpenCodeDbPath() {
16156
- return join5(getDataDir(), "opencode", "opencode.db");
16215
+ return join4(getDataDir(), "opencode", "opencode.db");
16157
16216
  }
16158
16217
  function closeCachedReadOnlyDb() {
16159
16218
  if (!cachedReadOnlyDb) {
@@ -16251,21 +16310,22 @@ __export(exports_rpc_notifications, {
16251
16310
  drainNotifications: () => drainNotifications
16252
16311
  });
16253
16312
  function pushNotification(type, payload, sessionId) {
16254
- queue2.push({ type, payload, sessionId });
16313
+ queue2.push({ id: nextNotificationId++, type, payload, sessionId });
16255
16314
  if (queue2.length > 100) {
16256
16315
  queue2 = queue2.slice(-50);
16257
16316
  }
16258
16317
  }
16259
- function drainNotifications() {
16318
+ function drainNotifications(lastReceivedId = 0) {
16260
16319
  lastDrainAt = Date.now();
16261
- const result = queue2;
16262
- queue2 = [];
16263
- return result;
16320
+ if (lastReceivedId > 0) {
16321
+ queue2 = queue2.filter((notification) => notification.id > lastReceivedId);
16322
+ }
16323
+ return [...queue2];
16264
16324
  }
16265
16325
  function isTuiConnected() {
16266
16326
  return lastDrainAt > 0 && Date.now() - lastDrainAt < TUI_CONNECTED_WINDOW_MS;
16267
16327
  }
16268
- var queue2, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
16328
+ var queue2, nextNotificationId = 1, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
16269
16329
  var init_rpc_notifications = __esm(() => {
16270
16330
  queue2 = [];
16271
16331
  });
@@ -23877,10 +23937,10 @@ var require_stringify = __commonJS((exports, module) => {
23877
23937
  replacer = null;
23878
23938
  indent = EMPTY;
23879
23939
  };
23880
- var join13 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
23940
+ var join12 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
23881
23941
  var join_content = (inside, value, gap) => {
23882
23942
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
23883
- return join13(comment, inside, gap);
23943
+ return join12(comment, inside, gap);
23884
23944
  };
23885
23945
  var stringify_string = (holder, key, value) => {
23886
23946
  const raw = get_raw_string_literal(holder, key);
@@ -23902,13 +23962,13 @@ var require_stringify = __commonJS((exports, module) => {
23902
23962
  if (i !== 0) {
23903
23963
  inside += COMMA;
23904
23964
  }
23905
- const before = join13(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
23965
+ const before = join12(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
23906
23966
  inside += before || LF + deeper_gap;
23907
23967
  inside += stringify(i, value, deeper_gap) || STR_NULL;
23908
23968
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
23909
23969
  after_comma = process_comments(value, AFTER(i), deeper_gap);
23910
23970
  }
23911
- inside += join13(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23971
+ inside += join12(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23912
23972
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
23913
23973
  };
23914
23974
  var object_stringify = (value, gap) => {
@@ -23929,13 +23989,13 @@ var require_stringify = __commonJS((exports, module) => {
23929
23989
  inside += COMMA;
23930
23990
  }
23931
23991
  first = false;
23932
- const before = join13(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
23992
+ const before = join12(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
23933
23993
  inside += before || LF + deeper_gap;
23934
23994
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
23935
23995
  after_comma = process_comments(value, AFTER(key), deeper_gap);
23936
23996
  };
23937
23997
  keys.forEach(iteratee);
23938
- inside += join13(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23998
+ inside += join12(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23939
23999
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
23940
24000
  };
23941
24001
  function stringify(key, holder, gap) {
@@ -24030,7 +24090,7 @@ var require_src2 = __commonJS((exports, module) => {
24030
24090
  // src/index.ts
24031
24091
  init_embedding();
24032
24092
  import { createRequire as createRequire2 } from "node:module";
24033
- import { join as join15 } from "node:path";
24093
+ import { join as join14 } from "node:path";
24034
24094
 
24035
24095
  // ../plugin/src/features/magic-context/memory/project-identity.ts
24036
24096
  import { execSync } from "node:child_process";
@@ -157083,21 +157143,6 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
157083
157143
  toolOnlyRanges
157084
157144
  };
157085
157145
  }
157086
-
157087
- // ../plugin/src/shared/harness.ts
157088
- var currentHarness = "opencode";
157089
- var harnessLocked = false;
157090
- function setHarness(value) {
157091
- if (harnessLocked && currentHarness !== value) {
157092
- throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
157093
- }
157094
- currentHarness = value;
157095
- harnessLocked = true;
157096
- }
157097
- function getHarness() {
157098
- return currentHarness;
157099
- }
157100
-
157101
157146
  // ../plugin/src/features/magic-context/compression-depth-storage.ts
157102
157147
  var incrementDepthStatements = new WeakMap;
157103
157148
  var totalDepthStatements = new WeakMap;
@@ -157773,8 +157818,8 @@ function escapeXmlContent(s) {
157773
157818
  }
157774
157819
  // ../plugin/src/features/magic-context/storage-db.ts
157775
157820
  init_data_path();
157776
- import { copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
157777
- import { join as join7 } from "node:path";
157821
+ import { copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync4 } from "node:fs";
157822
+ import { join as join6 } from "node:path";
157778
157823
 
157779
157824
  // ../plugin/src/shared/error-message.ts
157780
157825
  function getErrorMessage(error51) {
@@ -157991,6 +158036,9 @@ var MIGRATIONS = [
157991
158036
  embedding BLOB NOT NULL,
157992
158037
  model_id TEXT NOT NULL,
157993
158038
  created_at INTEGER NOT NULL,
158039
+ -- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
158040
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
158041
+ -- every connection and v12 cleans historical orphan rows.
157994
158042
  FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
157995
158043
  );
157996
158044
 
@@ -158130,6 +158178,29 @@ var MIGRATIONS = [
158130
158178
  db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
158131
158179
  }
158132
158180
  }
158181
+ },
158182
+ {
158183
+ version: 12,
158184
+ description: "Clean orphan rows from FK-cascade embedding tables",
158185
+ up: (db) => {
158186
+ const hasTable = (name2) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
158187
+ const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
158188
+ WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
158189
+ log(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
158190
+ const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
158191
+ WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
158192
+ log(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
158193
+ }
158194
+ },
158195
+ {
158196
+ version: 13,
158197
+ description: "Add pending_compaction_marker_state column for deferred marker drain",
158198
+ up: (db) => {
158199
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
158200
+ if (!cols.some((c) => c.name === "pending_compaction_marker_state")) {
158201
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
158202
+ }
158203
+ }
158133
158204
  }
158134
158205
  ];
158135
158206
  function ensureMigrationsTable(db) {
@@ -158197,6 +158268,28 @@ function runMigrations(db) {
158197
158268
  log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
158198
158269
  }
158199
158270
 
158271
+ // ../plugin/src/shared/stable-json.ts
158272
+ function stableStringify(value, seen = new WeakSet) {
158273
+ if (value === undefined)
158274
+ return "undefined";
158275
+ if (value === null || typeof value !== "object")
158276
+ return JSON.stringify(value) ?? String(value);
158277
+ if (seen.has(value))
158278
+ return '"[Circular]"';
158279
+ seen.add(value);
158280
+ if (Array.isArray(value)) {
158281
+ return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
158282
+ }
158283
+ const entries = Object.entries(value).sort(([a], [b]) => {
158284
+ if (a < b)
158285
+ return -1;
158286
+ if (a > b)
158287
+ return 1;
158288
+ return 0;
158289
+ });
158290
+ return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
158291
+ }
158292
+
158200
158293
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
158201
158294
  var measurements = new Map;
158202
158295
  var fingerprints = new Map;
@@ -158232,11 +158325,11 @@ function loadToolDefinitionMeasurements(db) {
158232
158325
  init_data_path();
158233
158326
  init_logger();
158234
158327
  import { existsSync as existsSync2 } from "node:fs";
158235
- import { join as join6 } from "node:path";
158328
+ import { join as join5 } from "node:path";
158236
158329
  var LEASE_DURATION_MS = 5 * 60 * 1000;
158237
158330
  var LEASE_RENEWAL_MS = 60 * 1000;
158238
158331
  function resolveOpencodeDbPath() {
158239
- return join6(getDataDir(), "opencode", "opencode.db");
158332
+ return join5(getDataDir(), "opencode", "opencode.db");
158240
158333
  }
158241
158334
  function ensureBackfillStateTable(db) {
158242
158335
  db.exec(`
@@ -158479,20 +158572,20 @@ var persistenceByDatabase = new WeakMap;
158479
158572
  var persistenceErrorByDatabase = new WeakMap;
158480
158573
  function resolveDatabasePath() {
158481
158574
  const dbDir = getMagicContextStorageDir();
158482
- return { dbDir, dbPath: join7(dbDir, "context.db") };
158575
+ return { dbDir, dbPath: join6(dbDir, "context.db") };
158483
158576
  }
158484
158577
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158485
158578
  if (existsSync3(targetDbPath))
158486
158579
  return;
158487
158580
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
158488
- const legacyDbPath = join7(legacyDir, "context.db");
158581
+ const legacyDbPath = join6(legacyDir, "context.db");
158489
158582
  if (!existsSync3(legacyDbPath))
158490
158583
  return;
158491
158584
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
158492
- mkdirSync3(targetDbDir, { recursive: true });
158585
+ mkdirSync4(targetDbDir, { recursive: true });
158493
158586
  for (const suffix of ["", "-wal", "-shm"]) {
158494
158587
  const src = `${legacyDbPath}${suffix}`;
158495
- const dst = join7(targetDbDir, `context.db${suffix}`);
158588
+ const dst = join6(targetDbDir, `context.db${suffix}`);
158496
158589
  if (existsSync3(src)) {
158497
158590
  try {
158498
158591
  copyFileSync(src, dst);
@@ -158501,8 +158594,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158501
158594
  }
158502
158595
  }
158503
158596
  }
158504
- const legacyModelsDir = join7(legacyDir, "models");
158505
- const targetModelsDir = join7(targetDbDir, "models");
158597
+ const legacyModelsDir = join6(legacyDir, "models");
158598
+ const targetModelsDir = join6(targetDbDir, "models");
158506
158599
  if (existsSync3(legacyModelsDir) && !existsSync3(targetModelsDir)) {
158507
158600
  try {
158508
158601
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
@@ -158512,9 +158605,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158512
158605
  }
158513
158606
  }
158514
158607
  function initializeDatabase(db) {
158608
+ db.exec("PRAGMA foreign_keys=ON");
158515
158609
  db.exec("PRAGMA journal_mode=WAL");
158516
158610
  db.exec("PRAGMA busy_timeout=5000");
158517
- db.exec("PRAGMA foreign_keys=ON");
158518
158611
  db.exec(`
158519
158612
  CREATE TABLE IF NOT EXISTS tags (
158520
158613
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -158612,6 +158705,9 @@ function initializeDatabase(db) {
158612
158705
  );
158613
158706
 
158614
158707
  CREATE TABLE IF NOT EXISTS memory_embeddings (
158708
+ -- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
158709
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
158710
+ -- every connection and v12 cleans historical orphan rows.
158615
158711
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
158616
158712
  embedding BLOB NOT NULL,
158617
158713
  model_id TEXT
@@ -158719,7 +158815,13 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158719
158815
  system_prompt_hash TEXT DEFAULT '',
158720
158816
  memory_block_cache TEXT DEFAULT '',
158721
158817
  memory_block_count INTEGER DEFAULT 0,
158722
- memory_block_ids TEXT DEFAULT ''
158818
+ memory_block_ids TEXT DEFAULT '',
158819
+ -- pending_compaction_marker_state: intentionally NULLABLE without a
158820
+ -- default. Absence of a deferred marker is SQL NULL; presence is a
158821
+ -- valid JSON blob written via setPendingCompactionMarkerState.
158822
+ -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
158823
+ -- != empty-string defensively. Plan v6 section 3.
158824
+ pending_compaction_marker_state TEXT
158723
158825
  );
158724
158826
 
158725
158827
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -158807,6 +158909,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158807
158909
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
158808
158910
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
158809
158911
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
158912
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
158810
158913
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
158811
158914
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
158812
158915
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -158896,7 +158999,7 @@ function openDatabase() {
158896
158999
  }
158897
159000
  try {
158898
159001
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
158899
- mkdirSync3(dbDir, { recursive: true });
159002
+ mkdirSync4(dbDir, { recursive: true });
158900
159003
  const db = new Database(dbPath);
158901
159004
  initializeDatabase(db);
158902
159005
  runMigrations(db);
@@ -159201,6 +159304,28 @@ function setStrippedPlaceholderIds(db, sessionId, ids) {
159201
159304
  const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
159202
159305
  db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
159203
159306
  }
159307
+ function isPendingCompactionMarker(value) {
159308
+ return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
159309
+ }
159310
+ function getPendingCompactionMarkerState(db, sessionId) {
159311
+ const row = db.prepare("SELECT pending_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
159312
+ const raw = row?.pending_compaction_marker_state;
159313
+ if (raw === null || raw === undefined || raw === "")
159314
+ return null;
159315
+ try {
159316
+ const parsed = JSON.parse(raw);
159317
+ if (isPendingCompactionMarker(parsed)) {
159318
+ return parsed;
159319
+ }
159320
+ } catch {}
159321
+ return null;
159322
+ }
159323
+ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
159324
+ const expectedBlob = stableStringify(expected);
159325
+ const result = db.prepare(`UPDATE session_meta SET pending_compaction_marker_state = NULL
159326
+ WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
159327
+ return result.changes > 0;
159328
+ }
159204
159329
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
159205
159330
  init_logger();
159206
159331
  await init_read_session_db();
@@ -159798,7 +159923,7 @@ init_data_path();
159798
159923
  import { createHash as createHash3 } from "node:crypto";
159799
159924
  import { existsSync as existsSync4, readFileSync } from "node:fs";
159800
159925
  import { homedir as homedir2, platform } from "node:os";
159801
- import { join as join8 } from "node:path";
159926
+ import { join as join7 } from "node:path";
159802
159927
 
159803
159928
  // ../plugin/src/shared/jsonc-parser.ts
159804
159929
  function stripJsonComments(content) {
@@ -159913,15 +160038,15 @@ function getModelsJsonPath() {
159913
160038
  const cacheBase = getCacheDir();
159914
160039
  const source = process.env.OPENCODE_MODELS_URL?.trim();
159915
160040
  const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
159916
- return join8(cacheBase, "opencode", filename);
160041
+ return join7(cacheBase, "opencode", filename);
159917
160042
  }
159918
160043
  function getOpencodeConfigPath() {
159919
160044
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
159920
- const configDir = envDir ? envDir : platform() === "win32" ? join8(homedir2(), ".config", "opencode") : join8(process.env.XDG_CONFIG_HOME || join8(homedir2(), ".config"), "opencode");
159921
- const jsonc = join8(configDir, "opencode.jsonc");
160045
+ const configDir = envDir ? envDir : platform() === "win32" ? join7(homedir2(), ".config", "opencode") : join7(process.env.XDG_CONFIG_HOME || join7(homedir2(), ".config"), "opencode");
160046
+ const jsonc = join7(configDir, "opencode.jsonc");
159922
160047
  if (existsSync4(jsonc))
159923
160048
  return jsonc;
159924
- const json2 = join8(configDir, "opencode.json");
160049
+ const json2 = join7(configDir, "opencode.json");
159925
160050
  if (existsSync4(json2))
159926
160051
  return json2;
159927
160052
  return null;
@@ -160297,12 +160422,12 @@ init_logger();
160297
160422
  // src/subagent-runner.ts
160298
160423
  import * as childProcess from "node:child_process";
160299
160424
  import { existsSync as existsSync5 } from "node:fs";
160300
- import { dirname as dirname2, resolve as resolvePath } from "node:path";
160425
+ import { dirname as dirname3, resolve as resolvePath } from "node:path";
160301
160426
  import { createInterface } from "node:readline";
160302
160427
  import { fileURLToPath } from "node:url";
160303
160428
  function resolveSubagentEntryPath() {
160304
160429
  try {
160305
- const here = dirname2(fileURLToPath(import.meta.url));
160430
+ const here = dirname3(fileURLToPath(import.meta.url));
160306
160431
  const candidate = resolvePath(here, "subagent-entry.js");
160307
160432
  if (existsSync5(candidate))
160308
160433
  return candidate;
@@ -160744,69 +160869,6 @@ ${sidekickText}
160744
160869
  });
160745
160870
  }
160746
160871
 
160747
- // ../plugin/src/features/magic-context/dreamer/queue.ts
160748
- function enqueueDream(db, projectIdentity, reason, force = false) {
160749
- const now = Date.now();
160750
- return db.transaction(() => {
160751
- const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
160752
- db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
160753
- const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
160754
- if (existing) {
160755
- return null;
160756
- }
160757
- const result = db.prepare("INSERT INTO dream_queue (project_path, reason, enqueued_at) VALUES (?, ?, ?)").run(projectIdentity, reason, now);
160758
- return {
160759
- id: Number(result.lastInsertRowid),
160760
- projectIdentity,
160761
- reason,
160762
- enqueuedAt: now,
160763
- startedAt: null
160764
- };
160765
- })();
160766
- }
160767
- function peekQueue(db, projectIdentity) {
160768
- const row = projectIdentity ? db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL AND project_path = ? ORDER BY enqueued_at ASC LIMIT 1").get(projectIdentity) : db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL ORDER BY enqueued_at ASC LIMIT 1").get();
160769
- if (!row)
160770
- return null;
160771
- return {
160772
- id: row.id,
160773
- projectIdentity: row.project_path,
160774
- reason: row.reason,
160775
- enqueuedAt: row.enqueued_at,
160776
- startedAt: null
160777
- };
160778
- }
160779
- function dequeueNext(db, projectIdentity) {
160780
- const now = Date.now();
160781
- return db.transaction(() => {
160782
- const entry = peekQueue(db, projectIdentity);
160783
- if (!entry)
160784
- return null;
160785
- const result = db.prepare("UPDATE dream_queue SET started_at = ? WHERE id = ? AND started_at IS NULL").run(now, entry.id);
160786
- if (result.changes === 0)
160787
- return null;
160788
- return { ...entry, startedAt: now };
160789
- })();
160790
- }
160791
- function removeDreamEntry(db, id) {
160792
- db.prepare("DELETE FROM dream_queue WHERE id = ?").run(id);
160793
- }
160794
- function resetDreamEntry(db, id) {
160795
- db.prepare("UPDATE dream_queue SET started_at = NULL, retry_count = COALESCE(retry_count, 0) + 1 WHERE id = ?").run(id);
160796
- }
160797
- function getEntryRetryCount(db, id) {
160798
- const row = db.prepare("SELECT retry_count FROM dream_queue WHERE id = ?").get(id);
160799
- return row?.retry_count ?? 0;
160800
- }
160801
- function clearStaleEntries(db, maxAgeMs) {
160802
- const cutoff = Date.now() - maxAgeMs;
160803
- const result = db.prepare("DELETE FROM dream_queue WHERE started_at IS NOT NULL AND started_at < ?").run(cutoff);
160804
- return result.changes;
160805
- }
160806
-
160807
- // src/commands/ctx-dream.ts
160808
- init_logger();
160809
-
160810
160872
  // ../plugin/src/features/magic-context/dreamer/storage-dream-state.ts
160811
160873
  var getDreamStateStatements = new WeakMap;
160812
160874
  var setDreamStateStatements = new WeakMap;
@@ -160902,9 +160964,84 @@ function releaseLease(db, holderId) {
160902
160964
  deleteDreamState(db, LEASE_EXPIRY_KEY);
160903
160965
  })();
160904
160966
  }
160967
+
160968
+ // ../plugin/src/features/magic-context/dreamer/queue.ts
160969
+ function hasActiveDreamLease(db) {
160970
+ try {
160971
+ return isLeaseActive(db);
160972
+ } catch (error51) {
160973
+ if (String(error51).includes("no such table: dream_state")) {
160974
+ return false;
160975
+ }
160976
+ throw error51;
160977
+ }
160978
+ }
160979
+ function enqueueDream(db, projectIdentity, reason, force = false) {
160980
+ const now = Date.now();
160981
+ return db.transaction(() => {
160982
+ if (!hasActiveDreamLease(db)) {
160983
+ const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
160984
+ db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
160985
+ }
160986
+ const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
160987
+ if (existing) {
160988
+ return null;
160989
+ }
160990
+ const result = db.prepare("INSERT INTO dream_queue (project_path, reason, enqueued_at) VALUES (?, ?, ?)").run(projectIdentity, reason, now);
160991
+ return {
160992
+ id: Number(result.lastInsertRowid),
160993
+ projectIdentity,
160994
+ reason,
160995
+ enqueuedAt: now,
160996
+ startedAt: null
160997
+ };
160998
+ })();
160999
+ }
161000
+ function peekQueue(db, projectIdentity) {
161001
+ const row = projectIdentity ? db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL AND project_path = ? ORDER BY enqueued_at ASC LIMIT 1").get(projectIdentity) : db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL ORDER BY enqueued_at ASC LIMIT 1").get();
161002
+ if (!row)
161003
+ return null;
161004
+ return {
161005
+ id: row.id,
161006
+ projectIdentity: row.project_path,
161007
+ reason: row.reason,
161008
+ enqueuedAt: row.enqueued_at,
161009
+ startedAt: null
161010
+ };
161011
+ }
161012
+ function dequeueNext(db, projectIdentity) {
161013
+ const now = Date.now();
161014
+ return db.transaction(() => {
161015
+ const entry = peekQueue(db, projectIdentity);
161016
+ if (!entry)
161017
+ return null;
161018
+ const result = db.prepare("UPDATE dream_queue SET started_at = ? WHERE id = ? AND started_at IS NULL").run(now, entry.id);
161019
+ if (result.changes === 0)
161020
+ return null;
161021
+ return { ...entry, startedAt: now };
161022
+ })();
161023
+ }
161024
+ function removeDreamEntry(db, id) {
161025
+ db.prepare("DELETE FROM dream_queue WHERE id = ?").run(id);
161026
+ }
161027
+ function resetDreamEntry(db, id) {
161028
+ db.prepare("UPDATE dream_queue SET started_at = NULL, retry_count = COALESCE(retry_count, 0) + 1 WHERE id = ?").run(id);
161029
+ }
161030
+ function getEntryRetryCount(db, id) {
161031
+ const row = db.prepare("SELECT retry_count FROM dream_queue WHERE id = ?").get(id);
161032
+ return row?.retry_count ?? 0;
161033
+ }
161034
+ function clearStaleEntries(db, maxAgeMs) {
161035
+ const cutoff = Date.now() - maxAgeMs;
161036
+ const result = db.prepare("DELETE FROM dream_queue WHERE started_at IS NOT NULL AND started_at < ?").run(cutoff);
161037
+ return result.changes;
161038
+ }
161039
+
161040
+ // src/commands/ctx-dream.ts
161041
+ init_logger();
160905
161042
  // ../plugin/src/features/magic-context/dreamer/runner.ts
160906
161043
  import { existsSync as existsSync6 } from "node:fs";
160907
- import { join as join9 } from "node:path";
161044
+ import { join as join8 } from "node:path";
160908
161045
 
160909
161046
  // ../plugin/src/shared/index.ts
160910
161047
  init_logger();
@@ -162442,7 +162579,7 @@ function logWithStackHead(message, stackHead) {
162442
162579
  log(message, stackHead ? { stackHead } : undefined);
162443
162580
  }
162444
162581
  function getOpenCodeDbPath2() {
162445
- return join9(getDataDir(), "opencode", "opencode.db");
162582
+ return join8(getDataDir(), "opencode", "opencode.db");
162446
162583
  }
162447
162584
  function openOpenCodeDb() {
162448
162585
  const dbPath = getOpenCodeDbPath2();
@@ -162517,9 +162654,11 @@ async function identifyKeyFilesForSession(args) {
162517
162654
  try {
162518
162655
  if (!renewLease(args.db, args.holderId)) {
162519
162656
  log(`[key-files][${args.sessionId}] lease renewal failed — aborting`);
162657
+ args.onLeaseLost?.(`key-files:${args.sessionId}`);
162520
162658
  abortController.abort();
162521
162659
  }
162522
- } catch {
162660
+ } catch (error51) {
162661
+ args.onLeaseLost?.(`key-files:${args.sessionId}`, error51);
162523
162662
  abortController.abort();
162524
162663
  }
162525
162664
  }, 60000);
@@ -162574,6 +162713,10 @@ async function identifyKeyFilesForSession(args) {
162574
162713
  log(`[key-files][${args.sessionId}] could not parse agent output — using heuristic fallback`);
162575
162714
  applyHeuristicFallback();
162576
162715
  } catch (error51) {
162716
+ if (args.isLeaseLost?.() || abortController.signal.aborted) {
162717
+ log(`[key-files][${args.sessionId}] lease lost during identification — skipping heuristic fallback`);
162718
+ throw error51;
162719
+ }
162577
162720
  log(`[key-files][${args.sessionId}] identification failed: ${getErrorMessage(error51)} — using heuristic fallback`);
162578
162721
  try {
162579
162722
  applyHeuristicFallback();
@@ -162614,6 +162757,10 @@ async function identifyKeyFiles(args) {
162614
162757
  }
162615
162758
  log(`[key-files] evaluating ${sessionIds.length} active session(s) for ${args.projectIdentity}`);
162616
162759
  for (const sessionId of sessionIds) {
162760
+ if (args.isLeaseLost?.()) {
162761
+ log("[key-files] lease lost — stopping key-file identification");
162762
+ break;
162763
+ }
162617
162764
  if (Date.now() > args.deadline) {
162618
162765
  log("[key-files] deadline reached — stopping key-file identification");
162619
162766
  break;
@@ -162625,6 +162772,8 @@ async function identifyKeyFiles(args) {
162625
162772
  sessionDirectory: args.sessionDirectory,
162626
162773
  holderId: args.holderId,
162627
162774
  fallbackModels: args.fallbackModels,
162775
+ onLeaseLost: args.onLeaseLost,
162776
+ isLeaseLost: args.isLeaseLost,
162628
162777
  deadline: args.deadline,
162629
162778
  sessionId,
162630
162779
  config: args.config
@@ -162681,8 +162830,50 @@ async function runDream(args) {
162681
162830
  let lastErrorSignature = null;
162682
162831
  let consecutiveSameErrorFailures = 0;
162683
162832
  let circuitBreakerTripped = false;
162833
+ let lostLease = false;
162834
+ let lostLeaseReason = null;
162835
+ let lostLeaseRecorded = false;
162836
+ const markLeaseLost = (phase, error51) => {
162837
+ const detail = error51 ? `: ${getErrorMessage(error51)}` : "";
162838
+ lostLeaseReason = `Dream lease lost during ${phase}${detail}`;
162839
+ if (!lostLease) {
162840
+ log(`[dreamer] FATAL: ${lostLeaseReason}; aborting all remaining dream work`);
162841
+ } else {
162842
+ log(`[dreamer] FATAL: ${lostLeaseReason}; dream work is already aborting`);
162843
+ }
162844
+ lostLease = true;
162845
+ };
162846
+ const recordLeaseLostTask = (phase) => {
162847
+ if (lostLeaseRecorded)
162848
+ return;
162849
+ lostLeaseRecorded = true;
162850
+ result.tasks.push({
162851
+ name: "lease-lost",
162852
+ durationMs: 0,
162853
+ result: "",
162854
+ error: lostLeaseReason ?? `Dream lease lost during ${phase}; aborted remaining work`
162855
+ });
162856
+ };
162857
+ const verifyLeaseStillHeld = (phase) => {
162858
+ if (lostLease)
162859
+ return false;
162860
+ try {
162861
+ if (!renewLease(args.db, holderId)) {
162862
+ markLeaseLost(phase);
162863
+ return false;
162864
+ }
162865
+ return true;
162866
+ } catch (error51) {
162867
+ markLeaseLost(phase, error51);
162868
+ return false;
162869
+ }
162870
+ };
162684
162871
  try {
162685
162872
  for (const taskName of args.tasks) {
162873
+ if (!verifyLeaseStillHeld(`before task ${taskName}`)) {
162874
+ recordLeaseLostTask(`before task ${taskName}`);
162875
+ break;
162876
+ }
162686
162877
  if (Date.now() > deadline) {
162687
162878
  log(`[dreamer] deadline reached, stopping after ${result.tasks.length} tasks`);
162688
162879
  break;
@@ -162695,18 +162886,20 @@ async function runDream(args) {
162695
162886
  try {
162696
162887
  if (!renewLease(args.db, holderId)) {
162697
162888
  log(`[dreamer] task ${taskName}: lease renewal failed — aborting LLM call`);
162889
+ markLeaseLost(`task ${taskName} lease renewal`);
162698
162890
  taskAbortController.abort();
162699
162891
  }
162700
162892
  } catch (err) {
162701
162893
  log(`[dreamer] task ${taskName}: lease renewal threw — aborting LLM call: ${err}`);
162894
+ markLeaseLost(`task ${taskName} lease renewal`, err);
162702
162895
  taskAbortController.abort();
162703
162896
  }
162704
162897
  }, 60000);
162705
162898
  try {
162706
162899
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
162707
162900
  const existingDocs = taskName === "maintain-docs" ? {
162708
- architecture: existsSync6(join9(docsDir, "ARCHITECTURE.md")),
162709
- structure: existsSync6(join9(docsDir, "STRUCTURE.md"))
162901
+ architecture: existsSync6(join8(docsDir, "ARCHITECTURE.md")),
162902
+ structure: existsSync6(join8(docsDir, "STRUCTURE.md"))
162710
162903
  } : undefined;
162711
162904
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
162712
162905
  id: um.id,
@@ -162745,6 +162938,9 @@ async function runDream(args) {
162745
162938
  fallbackModels: args.fallbackModels,
162746
162939
  callContext: `dreamer:${taskName}`
162747
162940
  });
162941
+ if (lostLease) {
162942
+ throw new Error(lostLeaseReason ?? `Dream lease lost during ${taskName}`);
162943
+ }
162748
162944
  const messagesResponse = await args.client.session.messages({
162749
162945
  path: { id: agentSessionId },
162750
162946
  query: { directory: args.sessionDirectory ?? args.projectIdentity }
@@ -162775,7 +162971,10 @@ async function runDream(args) {
162775
162971
  result: null,
162776
162972
  error: errorDescription.brief
162777
162973
  });
162778
- if (shouldSkipCircuitBreaker(error51, errorDescription.brief)) {
162974
+ if (lostLease) {
162975
+ lastErrorSignature = null;
162976
+ consecutiveSameErrorFailures = 0;
162977
+ } else if (shouldSkipCircuitBreaker(error51, errorDescription.brief)) {
162779
162978
  lastErrorSignature = null;
162780
162979
  consecutiveSameErrorFailures = 0;
162781
162980
  } else {
@@ -162808,11 +163007,18 @@ async function runDream(args) {
162808
163007
  });
162809
163008
  }
162810
163009
  }
163010
+ if (lostLease) {
163011
+ recordLeaseLostTask(`task ${taskName}`);
163012
+ break;
163013
+ }
162811
163014
  if (circuitBreakerTripped) {
162812
163015
  break;
162813
163016
  }
162814
163017
  }
162815
- if (circuitBreakerTripped) {
163018
+ if (lostLease) {
163019
+ log("[dreamer] lease lost: skipping all post-task phases");
163020
+ recordLeaseLostTask("post-task phases");
163021
+ } else if (circuitBreakerTripped) {
162816
163022
  log("[dreamer] circuit breaker: skipping post-task phases");
162817
163023
  result.tasks.push({
162818
163024
  name: "post-task-phases",
@@ -162821,9 +163027,12 @@ async function runDream(args) {
162821
163027
  error: "Skipped post-task phases after circuit breaker tripped; configure dreamer model/fallback_models in magic-context.jsonc."
162822
163028
  });
162823
163029
  }
162824
- if (!circuitBreakerTripped && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
163030
+ if (!circuitBreakerTripped && !lostLease && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
162825
163031
  const umStart = Date.now();
162826
163032
  try {
163033
+ if (!verifyLeaseStillHeld("before user-memory review")) {
163034
+ throw new Error(lostLeaseReason ?? "Dream lease lost before user-memory review");
163035
+ }
162827
163036
  const reviewResult = await reviewUserMemories({
162828
163037
  db: args.db,
162829
163038
  client: args.client,
@@ -162834,6 +163043,9 @@ async function runDream(args) {
162834
163043
  promotionThreshold: args.experimentalUserMemories.promotionThreshold,
162835
163044
  fallbackModels: args.fallbackModels
162836
163045
  });
163046
+ if (!verifyLeaseStillHeld("after user-memory review")) {
163047
+ throw new Error(lostLeaseReason ?? "Dream lease lost after user-memory review");
163048
+ }
162837
163049
  const umOutput = `promoted=${reviewResult.promoted} merged=${reviewResult.merged} dismissed=${reviewResult.dismissed} consumed=${reviewResult.candidatesConsumed}`;
162838
163050
  if (reviewResult.promoted > 0 || reviewResult.merged > 0 || reviewResult.dismissed > 0) {
162839
163051
  log(`[dreamer] user-memories: ${umOutput}`);
@@ -162853,9 +163065,14 @@ async function runDream(args) {
162853
163065
  error: errorDescription.brief
162854
163066
  });
162855
163067
  }
163068
+ if (lostLease)
163069
+ recordLeaseLostTask("user-memory review");
162856
163070
  }
162857
- if (!circuitBreakerTripped && Date.now() <= deadline) {
163071
+ if (!circuitBreakerTripped && !lostLease && Date.now() <= deadline) {
162858
163072
  try {
163073
+ if (!verifyLeaseStillHeld("before smart-note evaluation")) {
163074
+ throw new Error(lostLeaseReason ?? "Dream lease lost before smart-note evaluation");
163075
+ }
162859
163076
  await evaluateSmartNotes({
162860
163077
  db: args.db,
162861
163078
  client: args.client,
@@ -162865,16 +163082,26 @@ async function runDream(args) {
162865
163082
  holderId,
162866
163083
  deadline,
162867
163084
  result,
162868
- fallbackModels: args.fallbackModels
163085
+ fallbackModels: args.fallbackModels,
163086
+ onLeaseLost: markLeaseLost,
163087
+ isLeaseLost: () => lostLease
162869
163088
  });
163089
+ if (!verifyLeaseStillHeld("after smart-note evaluation")) {
163090
+ throw new Error(lostLeaseReason ?? "Dream lease lost after smart-note evaluation");
163091
+ }
162870
163092
  } catch (error51) {
162871
163093
  const errorDescription = describeError(error51);
162872
163094
  logWithStackHead(`[dreamer] smart note evaluation failed: ${errorDescription.brief}`, errorDescription.stackHead);
162873
163095
  }
163096
+ if (lostLease)
163097
+ recordLeaseLostTask("smart-note evaluation");
162874
163098
  }
162875
- if (!circuitBreakerTripped && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
163099
+ if (!circuitBreakerTripped && !lostLease && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
162876
163100
  const kfStart = Date.now();
162877
163101
  try {
163102
+ if (!verifyLeaseStillHeld("before key-file identification")) {
163103
+ throw new Error(lostLeaseReason ?? "Dream lease lost before key-file identification");
163104
+ }
162878
163105
  await identifyKeyFiles({
162879
163106
  db: args.db,
162880
163107
  client: args.client,
@@ -162884,8 +163111,13 @@ async function runDream(args) {
162884
163111
  holderId,
162885
163112
  deadline,
162886
163113
  config: args.experimentalPinKeyFiles,
162887
- fallbackModels: args.fallbackModels
163114
+ fallbackModels: args.fallbackModels,
163115
+ onLeaseLost: markLeaseLost,
163116
+ isLeaseLost: () => lostLease
162888
163117
  });
163118
+ if (!verifyLeaseStillHeld("after key-file identification")) {
163119
+ throw new Error(lostLeaseReason ?? "Dream lease lost after key-file identification");
163120
+ }
162889
163121
  result.tasks.push({
162890
163122
  name: "key files",
162891
163123
  durationMs: Date.now() - kfStart,
@@ -162901,6 +163133,8 @@ async function runDream(args) {
162901
163133
  error: errorDescription.brief
162902
163134
  });
162903
163135
  }
163136
+ if (lostLease)
163137
+ recordLeaseLostTask("key-file identification");
162904
163138
  }
162905
163139
  } finally {
162906
163140
  releaseLease(args.db, holderId);
@@ -162941,7 +163175,7 @@ async function runDream(args) {
162941
163175
  "circuit-breaker"
162942
163176
  ]);
162943
163177
  const hasSuccessfulTask = result.tasks.some((t) => !t.error && !POST_TASK_NAMES.has(t.name));
162944
- if (hasSuccessfulTask) {
163178
+ if (hasSuccessfulTask && !lostLease) {
162945
163179
  setDreamState(args.db, `last_dream_at:${args.projectIdentity}`, String(result.finishedAt));
162946
163180
  setDreamState(args.db, "last_dream_at", String(result.finishedAt));
162947
163181
  }
@@ -162990,9 +163224,11 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
162990
163224
  try {
162991
163225
  if (!renewLease(args.db, args.holderId)) {
162992
163226
  log("[dreamer] smart notes: lease renewal failed — aborting");
163227
+ args.onLeaseLost?.("smart notes");
162993
163228
  abortController.abort();
162994
163229
  }
162995
- } catch {
163230
+ } catch (error51) {
163231
+ args.onLeaseLost?.("smart notes", error51);
162996
163232
  abortController.abort();
162997
163233
  }
162998
163234
  }, 60000);
@@ -164271,7 +164507,6 @@ function createScheduler(config2) {
164271
164507
  }
164272
164508
  };
164273
164509
  }
164274
-
164275
164510
  // ../plugin/src/features/magic-context/tagger.ts
164276
164511
  var TOOL_COMPOSITE_KEY_SEP = "\x00";
164277
164512
  function makeToolCompositeKey(ownerMsgId, callId) {
@@ -165310,16 +165545,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
165310
165545
  function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
165311
165546
  const cached2 = injectionCache.get(sessionId);
165312
165547
  if (!isCacheBusting && cached2) {
165313
- if (cached2.compartmentEndMessageId.length > 0) {
165314
- const cutoffIndex2 = messages.findIndex((message) => message.info.id === cached2.compartmentEndMessageId);
165315
- if (cutoffIndex2 >= 0) {
165316
- const remaining = messages.slice(cutoffIndex2 + 1);
165317
- messages.splice(0, messages.length, ...remaining);
165318
- } else {
165319
- sessionLog(sessionId, `compartment injection: cached boundary ${cached2.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
165548
+ if (cached2.kind === "empty") {
165549
+ return null;
165550
+ }
165551
+ const prepared = cached2.injection;
165552
+ if (prepared.compartmentEndMessageId === null) {
165553
+ sessionLog(sessionId, "compartment injection cache in degraded mode (null boundary), forcing rebuild");
165554
+ } else {
165555
+ if (prepared.compartmentEndMessageId.length > 0) {
165556
+ const cutoffIndex2 = messages.findIndex((message) => message.info.id === prepared.compartmentEndMessageId);
165557
+ if (cutoffIndex2 >= 0) {
165558
+ const remaining = messages.slice(cutoffIndex2 + 1);
165559
+ messages.splice(0, messages.length, ...remaining);
165560
+ } else {
165561
+ sessionLog(sessionId, `compartment injection: cached boundary ${prepared.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
165562
+ }
165320
165563
  }
165564
+ return { ...prepared, rebuiltFromDb: false };
165321
165565
  }
165322
- return cached2;
165323
165566
  }
165324
165567
  const compartments = getCompartments(db, sessionId);
165325
165568
  const facts = getSessionFacts(db, sessionId);
@@ -165351,7 +165594,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165351
165594
  }
165352
165595
  }
165353
165596
  if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
165354
- injectionCache.delete(sessionId);
165597
+ injectionCache.set(sessionId, {
165598
+ kind: "empty",
165599
+ compartmentEndMessageId: "",
165600
+ renderedBytes: 0
165601
+ });
165355
165602
  return null;
165356
165603
  }
165357
165604
  let dateRanges;
@@ -165384,9 +165631,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165384
165631
  compartmentCount: 0,
165385
165632
  skippedVisibleMessages: 0,
165386
165633
  factCount: facts.length,
165387
- memoryCount
165634
+ memoryCount,
165635
+ rebuiltFromDb: true
165388
165636
  };
165389
- injectionCache.set(sessionId, result2);
165637
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
165390
165638
  return result2;
165391
165639
  }
165392
165640
  const lastCompartment = compartments[compartments.length - 1];
@@ -165404,9 +165652,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165404
165652
  compartmentCount: compartments.length,
165405
165653
  skippedVisibleMessages: 0,
165406
165654
  factCount: facts.length,
165407
- memoryCount
165655
+ memoryCount,
165656
+ rebuiltFromDb: true
165408
165657
  };
165409
- injectionCache.set(sessionId, result2);
165658
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
165410
165659
  return result2;
165411
165660
  }
165412
165661
  let skippedVisibleMessages = 0;
@@ -165415,17 +165664,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165415
165664
  skippedVisibleMessages = cutoffIndex + 1;
165416
165665
  const remaining = messages.slice(cutoffIndex + 1);
165417
165666
  messages.splice(0, messages.length, ...remaining);
165667
+ } else {
165668
+ sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
165418
165669
  }
165419
165670
  const result = {
165420
165671
  block,
165421
165672
  compartmentEndMessage: lastEnd,
165422
- compartmentEndMessageId: lastEndMessageId,
165673
+ compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
165423
165674
  compartmentCount: compartments.length,
165424
165675
  skippedVisibleMessages,
165425
165676
  factCount: facts.length,
165426
- memoryCount
165677
+ memoryCount,
165678
+ rebuiltFromDb: true
165427
165679
  };
165428
- injectionCache.set(sessionId, result);
165680
+ injectionCache.set(sessionId, { kind: "populated", injection: result });
165429
165681
  return result;
165430
165682
  }
165431
165683
 
@@ -166968,7 +167220,7 @@ function injectSessionHistoryIntoPi(db, sessionId, piMessages, isCacheBusting, p
166968
167220
  };
166969
167221
  }
166970
167222
  const boundaryId = prepared.compartmentEndMessageId;
166971
- const skippedVisible = boundaryId.length > 0 ? trimPiMessagesToBoundary(piMessages, entryIds, boundaryId) : prepared.skippedVisibleMessages;
167223
+ const skippedVisible = boundaryId != null && boundaryId.length > 0 ? trimPiMessagesToBoundary(piMessages, entryIds, boundaryId) : prepared.skippedVisibleMessages;
166972
167224
  const historyBlock = `<session-history>
166973
167225
  ${prepared.block}
166974
167226
  </session-history>`;
@@ -168018,17 +168270,17 @@ function getReducedRecompTokenBudget(currentBudget) {
168018
168270
  }
168019
168271
 
168020
168272
  // ../plugin/src/hooks/magic-context/historian-state-file.ts
168021
- import { mkdirSync as mkdirSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
168022
- import { tmpdir as tmpdir2 } from "node:os";
168023
- import { join as join10 } from "node:path";
168273
+ init_data_path();
168274
+ import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
168275
+ import { join as join9 } from "node:path";
168024
168276
  var HISTORIAN_STATE_INLINE_THRESHOLD = 30000;
168025
- var HISTORIAN_STATE_DIR = join10(tmpdir2(), "magic-context-historian");
168026
- function maybeWriteHistorianStateFile(sessionId, existingState) {
168277
+ function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
168027
168278
  if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
168028
168279
  return;
168029
168280
  try {
168030
- mkdirSync4(HISTORIAN_STATE_DIR, { recursive: true });
168031
- const path5 = join10(HISTORIAN_STATE_DIR, `state-${sessionId}-${Date.now()}.xml`);
168281
+ const dir = getProjectMagicContextHistorianDir(directory);
168282
+ mkdirSync5(dir, { recursive: true });
168283
+ const path5 = join9(dir, `state-${sessionId}-${Date.now()}.xml`);
168032
168284
  writeFileSync2(path5, existingState, "utf8");
168033
168285
  return path5;
168034
168286
  } catch {
@@ -168128,7 +168380,7 @@ async function runPiHistorian(deps) {
168128
168380
  const existingState = priorCompartments.length > 0 || priorFacts.length > 0 ? buildExistingStateXml(priorCompartments, priorFacts, memoryBlock) : memoryBlock ? `${memoryBlock}
168129
168381
 
168130
168382
  This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
168131
- stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
168383
+ stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, directory);
168132
168384
  if (stateFilePath) {
168133
168385
  sessionLog(sessionId, `pi-historian: existing state offloaded to file (${existingState.length} chars) → ${stateFilePath}`);
168134
168386
  }
@@ -170236,7 +170488,7 @@ init_logger();
170236
170488
  init_data_path();
170237
170489
  init_logger();
170238
170490
  await init_sqlite();
170239
- import { join as join11 } from "node:path";
170491
+ import { join as join10 } from "node:path";
170240
170492
  var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
170241
170493
  function randomBase62(length) {
170242
170494
  const chars = [];
@@ -170257,7 +170509,7 @@ function generatePartId(timestampMs, counter = 0n) {
170257
170509
  return generateId("prt", timestampMs, counter);
170258
170510
  }
170259
170511
  function getOpenCodeDbPath3() {
170260
- return join11(getDataDir(), "opencode", "opencode.db");
170512
+ return join10(getDataDir(), "opencode", "opencode.db");
170261
170513
  }
170262
170514
  var cachedWriteDb = null;
170263
170515
  var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
@@ -170815,11 +171067,13 @@ async function runCompressorPass2(args) {
170815
171067
  }
170816
171068
 
170817
171069
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
170818
- import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
170819
- import { tmpdir as tmpdir3 } from "node:os";
170820
- import { join as join12 } from "node:path";
171070
+ import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
171071
+ import { join as join11 } from "node:path";
170821
171072
  init_magic_context();
170822
- var HISTORIAN_RESPONSE_DUMP_DIR = join12(tmpdir3(), "magic-context-historian");
171073
+ init_data_path();
171074
+ function historianResponseDumpDir(directory) {
171075
+ return getProjectMagicContextHistorianDir(directory);
171076
+ }
170823
171077
  var MAX_HISTORIAN_RETRIES = 2;
170824
171078
  async function runValidatedHistorianPass(args) {
170825
171079
  const firstRun = await runHistorianPrompt({
@@ -170971,7 +171225,7 @@ async function runHistorianPrompt(args) {
170971
171225
  if (!result) {
170972
171226
  return { ok: false, error: "Historian returned no assistant output." };
170973
171227
  }
170974
- const dumpPath = dumpHistorianResponse(parentSessionId, dumpLabel ?? "historian-response", result);
171228
+ const dumpPath = dumpHistorianResponse(parentSessionId, sessionDirectory, dumpLabel ?? "historian-response", result);
170975
171229
  return { ok: true, result, dumpPath };
170976
171230
  } catch (modelError) {
170977
171231
  const desc = describeError(modelError);
@@ -171063,12 +171317,13 @@ function cleanupHistorianDump(sessionId, dumpPath) {
171063
171317
  });
171064
171318
  }
171065
171319
  }
171066
- function dumpHistorianResponse(sessionId, label, text) {
171320
+ function dumpHistorianResponse(sessionId, directory, label, text) {
171067
171321
  try {
171068
- mkdirSync5(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
171322
+ const dumpDir = historianResponseDumpDir(directory);
171323
+ mkdirSync6(dumpDir, { recursive: true });
171069
171324
  const safeSessionId = sanitizeDumpName(sessionId);
171070
171325
  const safeLabel = sanitizeDumpName(label);
171071
- const dumpPath = join12(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
171326
+ const dumpPath = join11(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
171072
171327
  writeFileSync3(dumpPath, text, "utf8");
171073
171328
  sessionLog(sessionId, "compartment agent: historian response dumped", {
171074
171329
  label,
@@ -171293,9 +171548,18 @@ async function executePartialRecompInternal(deps, range) {
171293
171548
  }
171294
171549
  setRecompPartialRange(db, sessionId, null);
171295
171550
  clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
171296
- clearInjectionCache(sessionId);
171297
- deps.onInjectionCacheCleared?.(sessionId);
171551
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
171552
+ clearInjectionCache(sessionId);
171553
+ }
171554
+ deps.onCompartmentStatePublished?.(sessionId);
171298
171555
  const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
171556
+ if (deps.experimentalCompactionMarkers && lastEnd > 0) {
171557
+ updateCompactionMarkerAfterPublication(db, sessionId, lastEnd, deps.directory);
171558
+ const stalePending = getPendingCompactionMarkerState(db, sessionId);
171559
+ if (stalePending) {
171560
+ clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
171561
+ }
171562
+ }
171299
171563
  return { compartmentCount: merged.length, lastEndMessage: lastEnd };
171300
171564
  };
171301
171565
  const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
@@ -171510,8 +171774,10 @@ async function executeContextRecompInternal(deps) {
171510
171774
  if (!promoted2)
171511
171775
  return null;
171512
171776
  clearCompressionDepth(db, sessionId);
171513
- clearInjectionCache(sessionId);
171514
- deps.onInjectionCacheCleared?.(sessionId);
171777
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
171778
+ clearInjectionCache(sessionId);
171779
+ }
171780
+ deps.onCompartmentStatePublished?.(sessionId);
171515
171781
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
171516
171782
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
171517
171783
  }
@@ -171521,6 +171787,10 @@ async function executeContextRecompInternal(deps) {
171521
171787
  }
171522
171788
  if (deps.experimentalCompactionMarkers && lastCompartmentEnd2 > 0) {
171523
171789
  updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
171790
+ const stalePending = getPendingCompactionMarkerState(db, sessionId);
171791
+ if (stalePending) {
171792
+ clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
171793
+ }
171524
171794
  }
171525
171795
  return [
171526
171796
  `Persisted ${promoted2.compartments.length} compartment${promoted2.compartments.length === 1 ? "" : "s"} from ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
@@ -171586,7 +171856,7 @@ Nothing was written.`;
171586
171856
 
171587
171857
  This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
171588
171858
  cleanupHistorianStateFile(currentStateFilePath);
171589
- currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
171859
+ currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, sessionDirectory);
171590
171860
  const prompt = buildCompartmentAgentPrompt(existingState, `Messages ${chunk.startIndex}-${chunk.endIndex}:
171591
171861
 
171592
171862
  ${chunk.text}`, { stateFilePath: currentStateFilePath });
@@ -171682,8 +171952,10 @@ Nothing was written.`;
171682
171952
  clearRecompStaging(db, sessionId);
171683
171953
  }
171684
171954
  clearCompressionDepth(db, sessionId);
171685
- clearInjectionCache(sessionId);
171686
- deps.onInjectionCacheCleared?.(sessionId);
171955
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
171956
+ clearInjectionCache(sessionId);
171957
+ }
171958
+ deps.onCompartmentStatePublished?.(sessionId);
171687
171959
  const finalCompartments = promoted?.compartments ?? candidateCompartments;
171688
171960
  const finalFacts = promoted?.facts ?? candidateFacts;
171689
171961
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
@@ -171732,21 +172004,39 @@ Staging data preserved for resume on next attempt.`;
171732
172004
 
171733
172005
  // ../plugin/src/hooks/magic-context/compartment-runner.ts
171734
172006
  var activeRuns = new Map;
172007
+ function markActiveCompartmentRunPublished(sessionId) {
172008
+ const activeRun = activeRuns.get(sessionId);
172009
+ if (activeRun)
172010
+ activeRun.published = true;
172011
+ }
172012
+ function withPublishedCallback(deps) {
172013
+ return {
172014
+ ...deps,
172015
+ onCompartmentStatePublished: (sid) => {
172016
+ markActiveCompartmentRunPublished(sid);
172017
+ deps.onCompartmentStatePublished?.(sid);
172018
+ }
172019
+ };
172020
+ }
171735
172021
  async function executeContextRecomp(deps, options = {}) {
171736
172022
  const { sessionId } = deps;
171737
172023
  if (activeRuns.has(sessionId)) {
171738
172024
  return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
171739
172025
  }
171740
- const promise2 = options.range ? executePartialRecompInternal(deps, options.range) : executeContextRecompInternal(deps);
171741
- activeRuns.set(sessionId, promise2.then(() => {
172026
+ const runnerDeps = withPublishedCallback(deps);
172027
+ const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
172028
+ const wrappedPromise = promise2.then(() => {
171742
172029
  return;
171743
172030
  }).catch((err) => {
171744
172031
  sessionLog(sessionId, "compartment agent: recomp unhandled rejection:", err);
171745
- }));
172032
+ });
172033
+ activeRuns.set(sessionId, { promise: wrappedPromise, published: false });
171746
172034
  try {
171747
172035
  return await promise2;
171748
172036
  } finally {
171749
- activeRuns.delete(sessionId);
172037
+ if (activeRuns.get(sessionId)?.promise === wrappedPromise) {
172038
+ activeRuns.delete(sessionId);
172039
+ }
171750
172040
  }
171751
172041
  }
171752
172042
 
@@ -172239,7 +172529,7 @@ import {
172239
172529
  // package.json
172240
172530
  var package_default = {
172241
172531
  name: "@cortexkit/pi-magic-context",
172242
- version: "0.18.0",
172532
+ version: "0.19.0",
172243
172533
  type: "module",
172244
172534
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
172245
172535
  main: "dist/index.js",
@@ -172778,12 +173068,12 @@ function readHistorianState(db, sessionId, meta3) {
172778
173068
  init_magic_context();
172779
173069
  import { existsSync as existsSync8, readFileSync as readFileSync3 } from "node:fs";
172780
173070
  import { homedir as homedir4 } from "node:os";
172781
- import { join as join13 } from "node:path";
173071
+ import { join as join12 } from "node:path";
172782
173072
 
172783
173073
  // ../plugin/src/config/variable.ts
172784
173074
  import { existsSync as existsSync7, readFileSync as readFileSync2 } from "node:fs";
172785
173075
  import { homedir as homedir3 } from "node:os";
172786
- import { dirname as dirname3, isAbsolute, resolve } from "node:path";
173076
+ import { dirname as dirname4, isAbsolute, resolve } from "node:path";
172787
173077
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
172788
173078
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
172789
173079
  function substituteConfigVariables(input) {
@@ -172802,7 +173092,7 @@ function substituteConfigVariables(input) {
172802
173092
  if (fileMatches.length === 0) {
172803
173093
  return { text, warnings };
172804
173094
  }
172805
- const configDir = input.configPath ? dirname3(input.configPath) : process.cwd();
173095
+ const configDir = input.configPath ? dirname4(input.configPath) : process.cwd();
172806
173096
  let output = "";
172807
173097
  let cursor = 0;
172808
173098
  for (const match of fileMatches) {
@@ -172846,12 +173136,12 @@ function substituteConfigVariables(input) {
172846
173136
  var import_comment_json = __toESM(require_src2(), 1);
172847
173137
  var CONFIG_FILE_NAME = "magic-context";
172848
173138
  function getProjectConfigPaths(cwd) {
172849
- const basePath = join13(cwd, ".pi", CONFIG_FILE_NAME);
173139
+ const basePath = join12(cwd, ".pi", CONFIG_FILE_NAME);
172850
173140
  return [`${basePath}.jsonc`, `${basePath}.json`];
172851
173141
  }
172852
173142
  function getUserConfigPaths() {
172853
173143
  const home = process.env.HOME ?? homedir4();
172854
- const basePath = join13(home, ".pi", "agent", CONFIG_FILE_NAME);
173144
+ const basePath = join12(home, ".pi", "agent", CONFIG_FILE_NAME);
172855
173145
  return [`${basePath}.jsonc`, `${basePath}.json`];
172856
173146
  }
172857
173147
  function resolveFirstExisting(paths) {
@@ -173008,7 +173298,7 @@ function stripTagPrefixFromAssistantMessage(message) {
173008
173298
  // src/system-prompt.ts
173009
173299
  import { createHash as createHash4 } from "node:crypto";
173010
173300
  import { existsSync as existsSync9, readFileSync as readFileSync4, realpathSync } from "node:fs";
173011
- import { join as join14, resolve as resolve2, sep } from "node:path";
173301
+ import { join as join13, resolve as resolve2, sep } from "node:path";
173012
173302
 
173013
173303
  // ../plugin/src/agents/magic-context-prompt.ts
173014
173304
  function getToolHistoryGuidance(dropToolStructure) {
@@ -173110,7 +173400,7 @@ var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
173110
173400
  function readProjectDocs(directory) {
173111
173401
  const sections = [];
173112
173402
  for (const filename of DOC_FILES) {
173113
- const filePath = join14(directory, filename);
173403
+ const filePath = join13(directory, filename);
173114
173404
  try {
173115
173405
  if (existsSync9(filePath)) {
173116
173406
  const content = readFileSync4(filePath, "utf-8").trim();
@@ -178341,7 +178631,7 @@ function resolveDreamerFromConfig(config2) {
178341
178631
  }
178342
178632
  async function src_default2(pi) {
178343
178633
  const storageDir = getMagicContextStorageDir();
178344
- const dbPath = join15(storageDir, "context.db");
178634
+ const dbPath = join14(storageDir, "context.db");
178345
178635
  let db;
178346
178636
  try {
178347
178637
  db = openDatabase();