@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.
@@ -11277,7 +11277,7 @@ function finalize(ctx, schema) {
11277
11277
  result.$schema = "http://json-schema.org/draft-07/schema#";
11278
11278
  } else if (ctx.target === "draft-04") {
11279
11279
  result.$schema = "http://json-schema.org/draft-04/schema#";
11280
- } else if (ctx.target === "openapi-3.0") {} else {}
11280
+ } else if (ctx.target === "openapi-3.0") {}
11281
11281
  if (ctx.external?.uri) {
11282
11282
  const id = ctx.external.registry.get(schema)?.id;
11283
11283
  if (!id)
@@ -11538,7 +11538,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
11538
11538
  if (val === undefined) {
11539
11539
  if (ctx.unrepresentable === "throw") {
11540
11540
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
11541
- } else {}
11541
+ }
11542
11542
  } else if (typeof val === "bigint") {
11543
11543
  if (ctx.unrepresentable === "throw") {
11544
11544
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -14891,10 +14891,63 @@ var init_magic_context = __esm(() => {
14891
14891
  });
14892
14892
  });
14893
14893
 
14894
- // ../plugin/src/shared/logger.ts
14895
- import * as fs from "node:fs";
14894
+ // ../plugin/src/shared/harness.ts
14895
+ function setHarness(value) {
14896
+ if (harnessLocked && currentHarness !== value) {
14897
+ throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
14898
+ }
14899
+ currentHarness = value;
14900
+ harnessLocked = true;
14901
+ }
14902
+ function getHarness() {
14903
+ return currentHarness;
14904
+ }
14905
+ var currentHarness = "opencode", harnessLocked = false;
14906
+
14907
+ // ../plugin/src/shared/data-path.ts
14896
14908
  import * as os from "node:os";
14897
14909
  import * as path from "node:path";
14910
+ function getDataDir() {
14911
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
14912
+ }
14913
+ function getMagicContextTempDir(harness = getHarness()) {
14914
+ return path.join(os.tmpdir(), harness, "magic-context");
14915
+ }
14916
+ function getMagicContextLogPath(harness = getHarness()) {
14917
+ return path.join(getMagicContextTempDir(harness), "magic-context.log");
14918
+ }
14919
+ function getProjectMagicContextDir(directory) {
14920
+ return path.join(directory, ".opencode", "magic-context");
14921
+ }
14922
+ function getProjectMagicContextHistorianDir(directory) {
14923
+ return path.join(getProjectMagicContextDir(directory), "historian");
14924
+ }
14925
+ function getOpenCodeStorageDir() {
14926
+ return path.join(getDataDir(), "opencode", "storage");
14927
+ }
14928
+ function getMagicContextStorageDir() {
14929
+ return path.join(getDataDir(), "cortexkit", "magic-context");
14930
+ }
14931
+ function getLegacyOpenCodeMagicContextStorageDir() {
14932
+ return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
14933
+ }
14934
+ function getCacheDir() {
14935
+ return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
14936
+ }
14937
+ var init_data_path = () => {};
14938
+
14939
+ // ../plugin/src/shared/logger.ts
14940
+ import * as fs from "node:fs";
14941
+ import * as path2 from "node:path";
14942
+ function ensureDir(filePath) {
14943
+ const dir = path2.dirname(filePath);
14944
+ if (dir === lastEnsuredDir)
14945
+ return;
14946
+ try {
14947
+ fs.mkdirSync(dir, { recursive: true });
14948
+ lastEnsuredDir = dir;
14949
+ } catch {}
14950
+ }
14898
14951
  function flush() {
14899
14952
  if (flushTimer) {
14900
14953
  clearTimeout(flushTimer);
@@ -14905,6 +14958,8 @@ function flush() {
14905
14958
  const data = buffer.join("");
14906
14959
  buffer = [];
14907
14960
  try {
14961
+ const logFile = getMagicContextLogPath();
14962
+ ensureDir(logFile);
14908
14963
  fs.appendFileSync(logFile, data);
14909
14964
  } catch {}
14910
14965
  }
@@ -14935,9 +14990,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
14935
14990
  function sessionLog(sessionId, message, data) {
14936
14991
  log(`[magic-context][${sessionId}] ${message}`, data);
14937
14992
  }
14938
- var logFile, isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
14993
+ var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, lastEnsuredDir = null;
14939
14994
  var init_logger = __esm(() => {
14940
- logFile = path.join(os.tmpdir(), "magic-context.log");
14995
+ init_data_path();
14941
14996
  buffer = [];
14942
14997
  if (!isTestEnv) {
14943
14998
  process.on("exit", flush);
@@ -14961,30 +15016,47 @@ function cosineSimilarity(a, b) {
14961
15016
  return denominator === 0 ? 0 : dotProduct / denominator;
14962
15017
  }
14963
15018
 
14964
- // ../plugin/src/shared/data-path.ts
14965
- import * as os2 from "node:os";
14966
- import * as path2 from "node:path";
14967
- function getDataDir() {
14968
- return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
14969
- }
14970
- function getOpenCodeStorageDir() {
14971
- return path2.join(getDataDir(), "opencode", "storage");
15019
+ // ../plugin/src/features/magic-context/memory/normalize-hash.ts
15020
+ import { createHash } from "node:crypto";
15021
+ function normalizeMemoryContent(content) {
15022
+ return content.toLowerCase().replace(/\s+/g, " ").trim();
14972
15023
  }
14973
- function getMagicContextStorageDir() {
14974
- return path2.join(getDataDir(), "cortexkit", "magic-context");
15024
+ function computeNormalizedHash(content) {
15025
+ const normalized = normalizeMemoryContent(content);
15026
+ return createHash("md5").update(normalized).digest("hex");
14975
15027
  }
14976
- function getLegacyOpenCodeMagicContextStorageDir() {
14977
- return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15028
+ var init_normalize_hash = () => {};
15029
+
15030
+ // ../plugin/src/features/magic-context/memory/embedding-identity.ts
15031
+ function normalizeEndpoint(endpoint) {
15032
+ return endpoint?.trim().replace(/\/+$/, "") ?? "";
14978
15033
  }
14979
- function getCacheDir() {
14980
- return process.env.XDG_CACHE_HOME ?? path2.join(os2.homedir(), ".cache");
15034
+ function getEmbeddingProviderIdentity(config2) {
15035
+ if (config2.provider === "off") {
15036
+ return "embedding-provider:off";
15037
+ }
15038
+ const identityInput = config2.provider === "openai-compatible" ? {
15039
+ provider: "openai-compatible",
15040
+ model: config2.model.trim(),
15041
+ endpoint: normalizeEndpoint(config2.endpoint),
15042
+ apiKeyPresent: Boolean(config2.api_key?.trim())
15043
+ } : {
15044
+ provider: "local",
15045
+ model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
15046
+ endpoint: "",
15047
+ apiKeyPresent: false
15048
+ };
15049
+ return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
14981
15050
  }
14982
- var init_data_path = () => {};
15051
+ var init_embedding_identity = __esm(() => {
15052
+ init_magic_context();
15053
+ init_normalize_hash();
15054
+ });
14983
15055
 
14984
15056
  // ../plugin/src/features/magic-context/memory/embedding-local.ts
14985
- import { mkdirSync } from "node:fs";
15057
+ import { mkdirSync as mkdirSync2 } from "node:fs";
14986
15058
  import { open, stat, unlink, writeFile } from "node:fs/promises";
14987
- import { join as join3 } from "node:path";
15059
+ import { join as join2 } from "node:path";
14988
15060
  async function acquireModelLoadLock(lockPath) {
14989
15061
  const waitStart = Date.now();
14990
15062
  while (true) {
@@ -15099,7 +15171,7 @@ class LocalEmbeddingProvider {
15099
15171
  initPromise = null;
15100
15172
  constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
15101
15173
  this.model = model;
15102
- this.modelId = `local:${model}`;
15174
+ this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
15103
15175
  }
15104
15176
  async initialize() {
15105
15177
  if (this.pipeline) {
@@ -15118,15 +15190,15 @@ class LocalEmbeddingProvider {
15118
15190
  if (LogLevel && "ERROR" in LogLevel) {
15119
15191
  env.logLevel = LogLevel.ERROR;
15120
15192
  }
15121
- const modelCacheDir = join3(getMagicContextStorageDir(), "models");
15193
+ const modelCacheDir = join2(getMagicContextStorageDir(), "models");
15122
15194
  try {
15123
- mkdirSync(modelCacheDir, { recursive: true });
15195
+ mkdirSync2(modelCacheDir, { recursive: true });
15124
15196
  env.cacheDir = modelCacheDir;
15125
15197
  } catch {
15126
15198
  log("[magic-context] could not create model cache dir, using library default");
15127
15199
  }
15128
15200
  const createPipeline = transformersModule.pipeline;
15129
- const lockPath = join3(modelCacheDir, ".load.lock");
15201
+ const lockPath = join2(modelCacheDir, ".load.lock");
15130
15202
  const releaseLock = await acquireModelLoadLock(lockPath);
15131
15203
  const stopHeartbeat = startLockHeartbeat(lockPath);
15132
15204
  try {
@@ -15241,12 +15313,13 @@ var init_embedding_local = __esm(() => {
15241
15313
  init_magic_context();
15242
15314
  init_data_path();
15243
15315
  init_logger();
15316
+ init_embedding_identity();
15244
15317
  STALE_LOCK_MS = 3 * 60000;
15245
15318
  MAX_LOCK_WAIT_MS = 5 * 60000;
15246
15319
  });
15247
15320
 
15248
15321
  // ../plugin/src/features/magic-context/memory/embedding-openai.ts
15249
- function normalizeEndpoint(endpoint) {
15322
+ function normalizeEndpoint2(endpoint) {
15250
15323
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
15251
15324
  }
15252
15325
 
@@ -15261,10 +15334,15 @@ class OpenAICompatibleEmbeddingProvider {
15261
15334
  openLogged = false;
15262
15335
  halfOpenProbeInFlight = false;
15263
15336
  constructor(options) {
15264
- this.endpoint = normalizeEndpoint(options.endpoint);
15337
+ this.endpoint = normalizeEndpoint2(options.endpoint);
15265
15338
  this.model = options.model?.trim() ?? "";
15266
15339
  this.apiKey = options.apiKey?.trim() ?? "";
15267
- this.modelId = `openai-compat:${this.endpoint}:${this.model}`;
15340
+ this.modelId = getEmbeddingProviderIdentity({
15341
+ provider: "openai-compatible",
15342
+ endpoint: this.endpoint,
15343
+ model: this.model,
15344
+ ...this.apiKey ? { api_key: this.apiKey } : {}
15345
+ });
15268
15346
  }
15269
15347
  async initialize() {
15270
15348
  if (this.initialized)
@@ -15446,20 +15524,10 @@ class OpenAICompatibleEmbeddingProvider {
15446
15524
  var FAILURE_THRESHOLD = 3, FAILURE_WINDOW_MS = 60000, OPEN_DURATION_MS, FETCH_TIMEOUT_MS = 30000;
15447
15525
  var init_embedding_openai = __esm(() => {
15448
15526
  init_logger();
15527
+ init_embedding_identity();
15449
15528
  OPEN_DURATION_MS = 5 * 60000;
15450
15529
  });
15451
15530
 
15452
- // ../plugin/src/features/magic-context/memory/normalize-hash.ts
15453
- import { createHash } from "node:crypto";
15454
- function normalizeMemoryContent(content) {
15455
- return content.toLowerCase().replace(/\s+/g, " ").trim();
15456
- }
15457
- function computeNormalizedHash(content) {
15458
- const normalized = normalizeMemoryContent(content);
15459
- return createHash("md5").update(normalized).digest("hex");
15460
- }
15461
- var init_normalize_hash = () => {};
15462
-
15463
15531
  // ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
15464
15532
  var exports_storage_memory_embeddings = {};
15465
15533
  __export(exports_storage_memory_embeddings, {
@@ -15604,17 +15672,8 @@ function resolveEmbeddingConfig(config2) {
15604
15672
  }
15605
15673
  return { provider: "off" };
15606
15674
  }
15607
- function resolveModelId(config2) {
15608
- if (config2.provider === "off") {
15609
- return "off";
15610
- }
15611
- if (config2.provider === "openai-compatible") {
15612
- const endpoint = config2.endpoint.trim();
15613
- const model = config2.model.trim();
15614
- const keyHash = config2.api_key ? computeNormalizedHash(config2.api_key) : "nokey";
15615
- return `openai-compat:${endpoint}:${model}:${keyHash}`;
15616
- }
15617
- return config2.model.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL;
15675
+ function resolveProviderIdentity(config2) {
15676
+ return getEmbeddingProviderIdentity(config2);
15618
15677
  }
15619
15678
  function createProvider(config2) {
15620
15679
  if (config2.provider === "off") {
@@ -15638,10 +15697,10 @@ function getOrCreateProvider() {
15638
15697
  }
15639
15698
  function initializeEmbedding(config2) {
15640
15699
  const nextConfig = resolveEmbeddingConfig(config2);
15641
- const nextModelId = resolveModelId(nextConfig);
15700
+ const nextProviderIdentity = resolveProviderIdentity(nextConfig);
15642
15701
  const previousProvider = provider;
15643
- const previousModelId = previousProvider?.modelId ?? resolveModelId(embeddingConfig);
15644
- if (previousModelId === nextModelId) {
15702
+ const previousProviderIdentity = previousProvider?.modelId ?? resolveProviderIdentity(embeddingConfig);
15703
+ if (previousProviderIdentity === nextProviderIdentity) {
15645
15704
  embeddingConfig = nextConfig;
15646
15705
  return;
15647
15706
  }
@@ -15789,9 +15848,9 @@ var DEFAULT_EMBEDDING_CONFIG, embeddingConfig, provider = null, loadUnembeddedMe
15789
15848
  var init_embedding = __esm(() => {
15790
15849
  init_magic_context();
15791
15850
  init_logger();
15851
+ init_embedding_identity();
15792
15852
  init_embedding_local();
15793
15853
  init_embedding_openai();
15794
- init_normalize_hash();
15795
15854
  init_storage_memory_embeddings();
15796
15855
  DEFAULT_EMBEDDING_CONFIG = {
15797
15856
  provider: "local",
@@ -15983,7 +16042,7 @@ var exports_native_binding = {};
15983
16042
  __export(exports_native_binding, {
15984
16043
  resolveBetterSqliteNativeBinding: () => resolveBetterSqliteNativeBinding
15985
16044
  });
15986
- import { existsSync, mkdirSync as mkdirSync2, writeFileSync } from "node:fs";
16045
+ import { existsSync, mkdirSync as mkdirSync3, writeFileSync } from "node:fs";
15987
16046
  import { createRequire } from "node:module";
15988
16047
  import * as path3 from "node:path";
15989
16048
  function logInfo(message) {
@@ -16083,7 +16142,7 @@ async function resolveBetterSqliteNativeBinding() {
16083
16142
  }
16084
16143
  logWarn(`cached binary at ${cachedPath} has wrong ABI (${cachedProbe.actual ?? "unknown"} != ${expected}); refetching`);
16085
16144
  }
16086
- mkdirSync2(path3.dirname(cachedPath), { recursive: true });
16145
+ mkdirSync3(path3.dirname(cachedPath), { recursive: true });
16087
16146
  const nodeFileBytes = await downloadElectronPrebuild(pkgVersion, expected);
16088
16147
  writeFileSync(cachedPath, nodeFileBytes);
16089
16148
  logInfo(`cached Electron prebuild at ${cachedPath} (${(nodeFileBytes.length / 1024).toFixed(1)} KB)`);
@@ -23749,10 +23808,10 @@ var require_stringify = __commonJS((exports, module) => {
23749
23808
  replacer = null;
23750
23809
  indent = EMPTY;
23751
23810
  };
23752
- var join7 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
23811
+ var join6 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
23753
23812
  var join_content = (inside, value, gap) => {
23754
23813
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
23755
- return join7(comment, inside, gap);
23814
+ return join6(comment, inside, gap);
23756
23815
  };
23757
23816
  var stringify_string = (holder, key, value) => {
23758
23817
  const raw = get_raw_string_literal(holder, key);
@@ -23774,13 +23833,13 @@ var require_stringify = __commonJS((exports, module) => {
23774
23833
  if (i !== 0) {
23775
23834
  inside += COMMA;
23776
23835
  }
23777
- const before = join7(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
23836
+ const before = join6(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
23778
23837
  inside += before || LF + deeper_gap;
23779
23838
  inside += stringify(i, value, deeper_gap) || STR_NULL;
23780
23839
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
23781
23840
  after_comma = process_comments(value, AFTER(i), deeper_gap);
23782
23841
  }
23783
- inside += join7(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23842
+ inside += join6(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23784
23843
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
23785
23844
  };
23786
23845
  var object_stringify = (value, gap) => {
@@ -23801,13 +23860,13 @@ var require_stringify = __commonJS((exports, module) => {
23801
23860
  inside += COMMA;
23802
23861
  }
23803
23862
  first = false;
23804
- const before = join7(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
23863
+ const before = join6(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
23805
23864
  inside += before || LF + deeper_gap;
23806
23865
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
23807
23866
  after_comma = process_comments(value, AFTER(key), deeper_gap);
23808
23867
  };
23809
23868
  keys.forEach(iteratee);
23810
- inside += join7(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23869
+ inside += join6(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
23811
23870
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
23812
23871
  };
23813
23872
  function stringify(key, holder, gap) {
@@ -23908,9 +23967,9 @@ __export(exports_read_session_db, {
23908
23967
  findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
23909
23968
  closeReadOnlySessionDb: () => closeReadOnlySessionDb
23910
23969
  });
23911
- import { join as join8 } from "node:path";
23970
+ import { join as join7 } from "node:path";
23912
23971
  function getOpenCodeDbPath() {
23913
- return join8(getDataDir(), "opencode", "opencode.db");
23972
+ return join7(getDataDir(), "opencode", "opencode.db");
23914
23973
  }
23915
23974
  function closeCachedReadOnlyDb() {
23916
23975
  if (!cachedReadOnlyDb) {
@@ -24005,8 +24064,8 @@ init_embedding();
24005
24064
 
24006
24065
  // ../plugin/src/features/magic-context/storage-db.ts
24007
24066
  init_data_path();
24008
- import { copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
24009
- import { join as join6 } from "node:path";
24067
+ import { copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync4 } from "node:fs";
24068
+ import { join as join5 } from "node:path";
24010
24069
 
24011
24070
  // ../plugin/src/shared/error-message.ts
24012
24071
  function getErrorMessage(error51) {
@@ -24223,6 +24282,9 @@ var MIGRATIONS = [
24223
24282
  embedding BLOB NOT NULL,
24224
24283
  model_id TEXT NOT NULL,
24225
24284
  created_at INTEGER NOT NULL,
24285
+ -- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
24286
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
24287
+ -- every connection and v12 cleans historical orphan rows.
24226
24288
  FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
24227
24289
  );
24228
24290
 
@@ -24362,6 +24424,29 @@ var MIGRATIONS = [
24362
24424
  db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
24363
24425
  }
24364
24426
  }
24427
+ },
24428
+ {
24429
+ version: 12,
24430
+ description: "Clean orphan rows from FK-cascade embedding tables",
24431
+ up: (db) => {
24432
+ const hasTable = (name) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
24433
+ const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
24434
+ WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
24435
+ log(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
24436
+ const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
24437
+ WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
24438
+ log(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
24439
+ }
24440
+ },
24441
+ {
24442
+ version: 13,
24443
+ description: "Add pending_compaction_marker_state column for deferred marker drain",
24444
+ up: (db) => {
24445
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
24446
+ if (!cols.some((c) => c.name === "pending_compaction_marker_state")) {
24447
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
24448
+ }
24449
+ }
24365
24450
  }
24366
24451
  ];
24367
24452
  function ensureMigrationsTable(db) {
@@ -157021,6 +157106,28 @@ function mergeCommitHashes(existing, next) {
157021
157106
  return merged;
157022
157107
  }
157023
157108
 
157109
+ // ../plugin/src/shared/stable-json.ts
157110
+ function stableStringify(value, seen = new WeakSet) {
157111
+ if (value === undefined)
157112
+ return "undefined";
157113
+ if (value === null || typeof value !== "object")
157114
+ return JSON.stringify(value) ?? String(value);
157115
+ if (seen.has(value))
157116
+ return '"[Circular]"';
157117
+ seen.add(value);
157118
+ if (Array.isArray(value)) {
157119
+ return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
157120
+ }
157121
+ const entries = Object.entries(value).sort(([a], [b]) => {
157122
+ if (a < b)
157123
+ return -1;
157124
+ if (a > b)
157125
+ return 1;
157126
+ return 0;
157127
+ });
157128
+ return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
157129
+ }
157130
+
157024
157131
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
157025
157132
  var measurements = new Map;
157026
157133
  var fingerprints = new Map;
@@ -157056,11 +157163,11 @@ function loadToolDefinitionMeasurements(db) {
157056
157163
  init_data_path();
157057
157164
  init_logger();
157058
157165
  import { existsSync as existsSync2 } from "node:fs";
157059
- import { join as join5 } from "node:path";
157166
+ import { join as join4 } from "node:path";
157060
157167
  var LEASE_DURATION_MS = 5 * 60 * 1000;
157061
157168
  var LEASE_RENEWAL_MS = 60 * 1000;
157062
157169
  function resolveOpencodeDbPath() {
157063
- return join5(getDataDir(), "opencode", "opencode.db");
157170
+ return join4(getDataDir(), "opencode", "opencode.db");
157064
157171
  }
157065
157172
  function ensureBackfillStateTable(db) {
157066
157173
  db.exec(`
@@ -157303,20 +157410,20 @@ var persistenceByDatabase = new WeakMap;
157303
157410
  var persistenceErrorByDatabase = new WeakMap;
157304
157411
  function resolveDatabasePath() {
157305
157412
  const dbDir = getMagicContextStorageDir();
157306
- return { dbDir, dbPath: join6(dbDir, "context.db") };
157413
+ return { dbDir, dbPath: join5(dbDir, "context.db") };
157307
157414
  }
157308
157415
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
157309
157416
  if (existsSync3(targetDbPath))
157310
157417
  return;
157311
157418
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
157312
- const legacyDbPath = join6(legacyDir, "context.db");
157419
+ const legacyDbPath = join5(legacyDir, "context.db");
157313
157420
  if (!existsSync3(legacyDbPath))
157314
157421
  return;
157315
157422
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
157316
- mkdirSync3(targetDbDir, { recursive: true });
157423
+ mkdirSync4(targetDbDir, { recursive: true });
157317
157424
  for (const suffix of ["", "-wal", "-shm"]) {
157318
157425
  const src = `${legacyDbPath}${suffix}`;
157319
- const dst = join6(targetDbDir, `context.db${suffix}`);
157426
+ const dst = join5(targetDbDir, `context.db${suffix}`);
157320
157427
  if (existsSync3(src)) {
157321
157428
  try {
157322
157429
  copyFileSync(src, dst);
@@ -157325,8 +157432,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
157325
157432
  }
157326
157433
  }
157327
157434
  }
157328
- const legacyModelsDir = join6(legacyDir, "models");
157329
- const targetModelsDir = join6(targetDbDir, "models");
157435
+ const legacyModelsDir = join5(legacyDir, "models");
157436
+ const targetModelsDir = join5(targetDbDir, "models");
157330
157437
  if (existsSync3(legacyModelsDir) && !existsSync3(targetModelsDir)) {
157331
157438
  try {
157332
157439
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
@@ -157336,9 +157443,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
157336
157443
  }
157337
157444
  }
157338
157445
  function initializeDatabase(db) {
157446
+ db.exec("PRAGMA foreign_keys=ON");
157339
157447
  db.exec("PRAGMA journal_mode=WAL");
157340
157448
  db.exec("PRAGMA busy_timeout=5000");
157341
- db.exec("PRAGMA foreign_keys=ON");
157342
157449
  db.exec(`
157343
157450
  CREATE TABLE IF NOT EXISTS tags (
157344
157451
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -157436,6 +157543,9 @@ function initializeDatabase(db) {
157436
157543
  );
157437
157544
 
157438
157545
  CREATE TABLE IF NOT EXISTS memory_embeddings (
157546
+ -- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
157547
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
157548
+ -- every connection and v12 cleans historical orphan rows.
157439
157549
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
157440
157550
  embedding BLOB NOT NULL,
157441
157551
  model_id TEXT
@@ -157543,7 +157653,13 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
157543
157653
  system_prompt_hash TEXT DEFAULT '',
157544
157654
  memory_block_cache TEXT DEFAULT '',
157545
157655
  memory_block_count INTEGER DEFAULT 0,
157546
- memory_block_ids TEXT DEFAULT ''
157656
+ memory_block_ids TEXT DEFAULT '',
157657
+ -- pending_compaction_marker_state: intentionally NULLABLE without a
157658
+ -- default. Absence of a deferred marker is SQL NULL; presence is a
157659
+ -- valid JSON blob written via setPendingCompactionMarkerState.
157660
+ -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
157661
+ -- != empty-string defensively. Plan v6 section 3.
157662
+ pending_compaction_marker_state TEXT
157547
157663
  );
157548
157664
 
157549
157665
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -157631,6 +157747,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
157631
157747
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
157632
157748
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
157633
157749
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
157750
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
157634
157751
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
157635
157752
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
157636
157753
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -157720,7 +157837,7 @@ function openDatabase() {
157720
157837
  }
157721
157838
  try {
157722
157839
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
157723
- mkdirSync3(dbDir, { recursive: true });
157840
+ mkdirSync4(dbDir, { recursive: true });
157724
157841
  const db = new Database(dbPath);
157725
157842
  initializeDatabase(db);
157726
157843
  runMigrations(db);
@@ -157742,20 +157859,6 @@ function openDatabase() {
157742
157859
  }
157743
157860
  }
157744
157861
 
157745
- // ../plugin/src/shared/harness.ts
157746
- var currentHarness = "opencode";
157747
- var harnessLocked = false;
157748
- function setHarness(value) {
157749
- if (harnessLocked && currentHarness !== value) {
157750
- throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
157751
- }
157752
- currentHarness = value;
157753
- harnessLocked = true;
157754
- }
157755
- function getHarness() {
157756
- return currentHarness;
157757
- }
157758
-
157759
157862
  // src/subagent-entry.ts
157760
157863
  init_logger();
157761
157864
 
@@ -157763,12 +157866,12 @@ init_logger();
157763
157866
  init_magic_context();
157764
157867
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
157765
157868
  import { homedir as homedir3 } from "node:os";
157766
- import { join as join7 } from "node:path";
157869
+ import { join as join6 } from "node:path";
157767
157870
 
157768
157871
  // ../plugin/src/config/variable.ts
157769
157872
  import { existsSync as existsSync4, readFileSync } from "node:fs";
157770
157873
  import { homedir as homedir2 } from "node:os";
157771
- import { dirname as dirname2, isAbsolute, resolve } from "node:path";
157874
+ import { dirname as dirname3, isAbsolute, resolve } from "node:path";
157772
157875
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
157773
157876
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
157774
157877
  function substituteConfigVariables(input) {
@@ -157787,7 +157890,7 @@ function substituteConfigVariables(input) {
157787
157890
  if (fileMatches.length === 0) {
157788
157891
  return { text, warnings };
157789
157892
  }
157790
- const configDir = input.configPath ? dirname2(input.configPath) : process.cwd();
157893
+ const configDir = input.configPath ? dirname3(input.configPath) : process.cwd();
157791
157894
  let output = "";
157792
157895
  let cursor = 0;
157793
157896
  for (const match of fileMatches) {
@@ -157831,12 +157934,12 @@ function substituteConfigVariables(input) {
157831
157934
  var import_comment_json = __toESM(require_src2(), 1);
157832
157935
  var CONFIG_FILE_NAME = "magic-context";
157833
157936
  function getProjectConfigPaths(cwd) {
157834
- const basePath = join7(cwd, ".pi", CONFIG_FILE_NAME);
157937
+ const basePath = join6(cwd, ".pi", CONFIG_FILE_NAME);
157835
157938
  return [`${basePath}.jsonc`, `${basePath}.json`];
157836
157939
  }
157837
157940
  function getUserConfigPaths() {
157838
157941
  const home = process.env.HOME ?? homedir3();
157839
- const basePath = join7(home, ".pi", "agent", CONFIG_FILE_NAME);
157942
+ const basePath = join6(home, ".pi", "agent", CONFIG_FILE_NAME);
157840
157943
  return [`${basePath}.jsonc`, `${basePath}.json`];
157841
157944
  }
157842
157945
  function resolveFirstExisting(paths) {
@@ -164293,6 +164396,28 @@ function setStrippedPlaceholderIds(db, sessionId, ids) {
164293
164396
  const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
164294
164397
  db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
164295
164398
  }
164399
+ function isPendingCompactionMarker(value) {
164400
+ return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
164401
+ }
164402
+ function getPendingCompactionMarkerState(db, sessionId) {
164403
+ const row = db.prepare("SELECT pending_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
164404
+ const raw = row?.pending_compaction_marker_state;
164405
+ if (raw === null || raw === undefined || raw === "")
164406
+ return null;
164407
+ try {
164408
+ const parsed = JSON.parse(raw);
164409
+ if (isPendingCompactionMarker(parsed)) {
164410
+ return parsed;
164411
+ }
164412
+ } catch {}
164413
+ return null;
164414
+ }
164415
+ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
164416
+ const expectedBlob = stableStringify(expected);
164417
+ const result = db.prepare(`UPDATE session_meta SET pending_compaction_marker_state = NULL
164418
+ WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
164419
+ return result.changes > 0;
164420
+ }
164296
164421
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
164297
164422
  init_logger();
164298
164423
  await init_read_session_db();
@@ -166209,16 +166334,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
166209
166334
  function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
166210
166335
  const cached2 = injectionCache.get(sessionId);
166211
166336
  if (!isCacheBusting && cached2) {
166212
- if (cached2.compartmentEndMessageId.length > 0) {
166213
- const cutoffIndex2 = messages.findIndex((message) => message.info.id === cached2.compartmentEndMessageId);
166214
- if (cutoffIndex2 >= 0) {
166215
- const remaining = messages.slice(cutoffIndex2 + 1);
166216
- messages.splice(0, messages.length, ...remaining);
166217
- } else {
166218
- sessionLog(sessionId, `compartment injection: cached boundary ${cached2.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
166337
+ if (cached2.kind === "empty") {
166338
+ return null;
166339
+ }
166340
+ const prepared = cached2.injection;
166341
+ if (prepared.compartmentEndMessageId === null) {
166342
+ sessionLog(sessionId, "compartment injection cache in degraded mode (null boundary), forcing rebuild");
166343
+ } else {
166344
+ if (prepared.compartmentEndMessageId.length > 0) {
166345
+ const cutoffIndex2 = messages.findIndex((message) => message.info.id === prepared.compartmentEndMessageId);
166346
+ if (cutoffIndex2 >= 0) {
166347
+ const remaining = messages.slice(cutoffIndex2 + 1);
166348
+ messages.splice(0, messages.length, ...remaining);
166349
+ } else {
166350
+ sessionLog(sessionId, `compartment injection: cached boundary ${prepared.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
166351
+ }
166219
166352
  }
166353
+ return { ...prepared, rebuiltFromDb: false };
166220
166354
  }
166221
- return cached2;
166222
166355
  }
166223
166356
  const compartments = getCompartments(db, sessionId);
166224
166357
  const facts = getSessionFacts(db, sessionId);
@@ -166250,7 +166383,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166250
166383
  }
166251
166384
  }
166252
166385
  if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
166253
- injectionCache.delete(sessionId);
166386
+ injectionCache.set(sessionId, {
166387
+ kind: "empty",
166388
+ compartmentEndMessageId: "",
166389
+ renderedBytes: 0
166390
+ });
166254
166391
  return null;
166255
166392
  }
166256
166393
  let dateRanges;
@@ -166283,9 +166420,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166283
166420
  compartmentCount: 0,
166284
166421
  skippedVisibleMessages: 0,
166285
166422
  factCount: facts.length,
166286
- memoryCount
166423
+ memoryCount,
166424
+ rebuiltFromDb: true
166287
166425
  };
166288
- injectionCache.set(sessionId, result2);
166426
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
166289
166427
  return result2;
166290
166428
  }
166291
166429
  const lastCompartment = compartments[compartments.length - 1];
@@ -166303,9 +166441,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166303
166441
  compartmentCount: compartments.length,
166304
166442
  skippedVisibleMessages: 0,
166305
166443
  factCount: facts.length,
166306
- memoryCount
166444
+ memoryCount,
166445
+ rebuiltFromDb: true
166307
166446
  };
166308
- injectionCache.set(sessionId, result2);
166447
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
166309
166448
  return result2;
166310
166449
  }
166311
166450
  let skippedVisibleMessages = 0;
@@ -166314,17 +166453,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
166314
166453
  skippedVisibleMessages = cutoffIndex + 1;
166315
166454
  const remaining = messages.slice(cutoffIndex + 1);
166316
166455
  messages.splice(0, messages.length, ...remaining);
166456
+ } else {
166457
+ sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
166317
166458
  }
166318
166459
  const result = {
166319
166460
  block,
166320
166461
  compartmentEndMessage: lastEnd,
166321
- compartmentEndMessageId: lastEndMessageId,
166462
+ compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
166322
166463
  compartmentCount: compartments.length,
166323
166464
  skippedVisibleMessages,
166324
166465
  factCount: facts.length,
166325
- memoryCount
166466
+ memoryCount,
166467
+ rebuiltFromDb: true
166326
166468
  };
166327
- injectionCache.set(sessionId, result);
166469
+ injectionCache.set(sessionId, { kind: "populated", injection: result });
166328
166470
  return result;
166329
166471
  }
166330
166472