@cortexkit/pi-magic-context 0.17.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
 
@@ -158111,6 +158159,48 @@ var MIGRATIONS = [
158111
158159
  WHERE type = 'tool' AND tool_owner_message_id IS NULL;
158112
158160
  `);
158113
158161
  }
158162
+ },
158163
+ {
158164
+ version: 11,
158165
+ description: "Add todo state synthesis columns to session_meta",
158166
+ up: (db) => {
158167
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
158168
+ if (!cols.some((c) => c.name === "last_todo_state")) {
158169
+ db.exec("ALTER TABLE session_meta ADD COLUMN last_todo_state TEXT DEFAULT ''");
158170
+ }
158171
+ if (!cols.some((c) => c.name === "todo_synthetic_call_id")) {
158172
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_call_id TEXT DEFAULT ''");
158173
+ }
158174
+ if (!cols.some((c) => c.name === "todo_synthetic_anchor_message_id")) {
158175
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_anchor_message_id TEXT DEFAULT ''");
158176
+ }
158177
+ if (!cols.some((c) => c.name === "todo_synthetic_state_json")) {
158178
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
158179
+ }
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
+ }
158114
158204
  }
158115
158205
  ];
158116
158206
  function ensureMigrationsTable(db) {
@@ -158178,6 +158268,28 @@ function runMigrations(db) {
158178
158268
  log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
158179
158269
  }
158180
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
+
158181
158293
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
158182
158294
  var measurements = new Map;
158183
158295
  var fingerprints = new Map;
@@ -158213,11 +158325,11 @@ function loadToolDefinitionMeasurements(db) {
158213
158325
  init_data_path();
158214
158326
  init_logger();
158215
158327
  import { existsSync as existsSync2 } from "node:fs";
158216
- import { join as join6 } from "node:path";
158328
+ import { join as join5 } from "node:path";
158217
158329
  var LEASE_DURATION_MS = 5 * 60 * 1000;
158218
158330
  var LEASE_RENEWAL_MS = 60 * 1000;
158219
158331
  function resolveOpencodeDbPath() {
158220
- return join6(getDataDir(), "opencode", "opencode.db");
158332
+ return join5(getDataDir(), "opencode", "opencode.db");
158221
158333
  }
158222
158334
  function ensureBackfillStateTable(db) {
158223
158335
  db.exec(`
@@ -158460,20 +158572,20 @@ var persistenceByDatabase = new WeakMap;
158460
158572
  var persistenceErrorByDatabase = new WeakMap;
158461
158573
  function resolveDatabasePath() {
158462
158574
  const dbDir = getMagicContextStorageDir();
158463
- return { dbDir, dbPath: join7(dbDir, "context.db") };
158575
+ return { dbDir, dbPath: join6(dbDir, "context.db") };
158464
158576
  }
158465
158577
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158466
158578
  if (existsSync3(targetDbPath))
158467
158579
  return;
158468
158580
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
158469
- const legacyDbPath = join7(legacyDir, "context.db");
158581
+ const legacyDbPath = join6(legacyDir, "context.db");
158470
158582
  if (!existsSync3(legacyDbPath))
158471
158583
  return;
158472
158584
  log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
158473
- mkdirSync3(targetDbDir, { recursive: true });
158585
+ mkdirSync4(targetDbDir, { recursive: true });
158474
158586
  for (const suffix of ["", "-wal", "-shm"]) {
158475
158587
  const src = `${legacyDbPath}${suffix}`;
158476
- const dst = join7(targetDbDir, `context.db${suffix}`);
158588
+ const dst = join6(targetDbDir, `context.db${suffix}`);
158477
158589
  if (existsSync3(src)) {
158478
158590
  try {
158479
158591
  copyFileSync(src, dst);
@@ -158482,8 +158594,8 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158482
158594
  }
158483
158595
  }
158484
158596
  }
158485
- const legacyModelsDir = join7(legacyDir, "models");
158486
- const targetModelsDir = join7(targetDbDir, "models");
158597
+ const legacyModelsDir = join6(legacyDir, "models");
158598
+ const targetModelsDir = join6(targetDbDir, "models");
158487
158599
  if (existsSync3(legacyModelsDir) && !existsSync3(targetModelsDir)) {
158488
158600
  try {
158489
158601
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
@@ -158493,9 +158605,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
158493
158605
  }
158494
158606
  }
158495
158607
  function initializeDatabase(db) {
158608
+ db.exec("PRAGMA foreign_keys=ON");
158496
158609
  db.exec("PRAGMA journal_mode=WAL");
158497
158610
  db.exec("PRAGMA busy_timeout=5000");
158498
- db.exec("PRAGMA foreign_keys=ON");
158499
158611
  db.exec(`
158500
158612
  CREATE TABLE IF NOT EXISTS tags (
158501
158613
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -158593,6 +158705,9 @@ function initializeDatabase(db) {
158593
158705
  );
158594
158706
 
158595
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.
158596
158711
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
158597
158712
  embedding BLOB NOT NULL,
158598
158713
  model_id TEXT
@@ -158685,6 +158800,10 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158685
158800
  note_nudge_trigger_message_id TEXT DEFAULT '',
158686
158801
  note_nudge_sticky_text TEXT DEFAULT '',
158687
158802
  note_nudge_sticky_message_id TEXT DEFAULT '',
158803
+ last_todo_state TEXT DEFAULT '',
158804
+ todo_synthetic_call_id TEXT DEFAULT '',
158805
+ todo_synthetic_anchor_message_id TEXT DEFAULT '',
158806
+ todo_synthetic_state_json TEXT DEFAULT '',
158688
158807
  is_subagent INTEGER DEFAULT 0,
158689
158808
  last_context_percentage REAL DEFAULT 0,
158690
158809
  last_input_tokens INTEGER DEFAULT 0,
@@ -158696,7 +158815,13 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158696
158815
  system_prompt_hash TEXT DEFAULT '',
158697
158816
  memory_block_cache TEXT DEFAULT '',
158698
158817
  memory_block_count INTEGER DEFAULT 0,
158699
- 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
158700
158825
  );
158701
158826
 
158702
158827
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -158749,6 +158874,10 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158749
158874
  ensureColumn(db, "session_meta", "note_nudge_trigger_message_id", "TEXT DEFAULT ''");
158750
158875
  ensureColumn(db, "session_meta", "note_nudge_sticky_text", "TEXT DEFAULT ''");
158751
158876
  ensureColumn(db, "session_meta", "note_nudge_sticky_message_id", "TEXT DEFAULT ''");
158877
+ ensureColumn(db, "session_meta", "last_todo_state", "TEXT DEFAULT ''");
158878
+ ensureColumn(db, "session_meta", "todo_synthetic_call_id", "TEXT DEFAULT ''");
158879
+ ensureColumn(db, "session_meta", "todo_synthetic_anchor_message_id", "TEXT DEFAULT ''");
158880
+ ensureColumn(db, "session_meta", "todo_synthetic_state_json", "TEXT DEFAULT ''");
158752
158881
  ensureColumn(db, "session_meta", "note_last_read_at", "INTEGER DEFAULT 0");
158753
158882
  ensureColumn(db, "session_meta", "times_execute_threshold_reached", "INTEGER DEFAULT 0");
158754
158883
  ensureColumn(db, "session_meta", "compartment_in_progress", "INTEGER DEFAULT 0");
@@ -158780,6 +158909,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
158780
158909
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
158781
158910
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
158782
158911
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
158912
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
158783
158913
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
158784
158914
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
158785
158915
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -158813,6 +158943,10 @@ function healNullTextColumns(db) {
158813
158943
  ["note_nudge_trigger_message_id", ""],
158814
158944
  ["note_nudge_sticky_text", ""],
158815
158945
  ["note_nudge_sticky_message_id", ""],
158946
+ ["last_todo_state", ""],
158947
+ ["todo_synthetic_call_id", ""],
158948
+ ["todo_synthetic_anchor_message_id", ""],
158949
+ ["todo_synthetic_state_json", ""],
158816
158950
  ["system_prompt_hash", ""],
158817
158951
  ["stripped_placeholder_ids", ""],
158818
158952
  ["memory_block_cache", ""],
@@ -158865,7 +158999,7 @@ function openDatabase() {
158865
158999
  }
158866
159000
  try {
158867
159001
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
158868
- mkdirSync3(dbDir, { recursive: true });
159002
+ mkdirSync4(dbDir, { recursive: true });
158869
159003
  const db = new Database(dbPath);
158870
159004
  initializeDatabase(db);
158871
159005
  runMigrations(db);
@@ -158906,7 +159040,8 @@ var META_COLUMNS = {
158906
159040
  systemPromptTokens: "system_prompt_tokens",
158907
159041
  conversationTokens: "conversation_tokens",
158908
159042
  toolCallTokens: "tool_call_tokens",
158909
- clearedReasoningThroughTag: "cleared_reasoning_through_tag"
159043
+ clearedReasoningThroughTag: "cleared_reasoning_through_tag",
159044
+ lastTodoState: "last_todo_state"
158910
159045
  };
158911
159046
  var BOOLEAN_META_KEYS = new Set(["isSubagent", "compartmentInProgress"]);
158912
159047
  function isStringOrNull(value) {
@@ -158919,7 +159054,7 @@ function isSessionMetaRow(row) {
158919
159054
  if (row === null || typeof row !== "object")
158920
159055
  return false;
158921
159056
  const r = row;
158922
- return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag);
159057
+ return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state);
158923
159058
  }
158924
159059
  function getDefaultSessionMeta(sessionId) {
158925
159060
  return {
@@ -158939,7 +159074,8 @@ function getDefaultSessionMeta(sessionId) {
158939
159074
  systemPromptTokens: 0,
158940
159075
  conversationTokens: 0,
158941
159076
  toolCallTokens: 0,
158942
- clearedReasoningThroughTag: 0
159077
+ clearedReasoningThroughTag: 0,
159078
+ lastTodoState: ""
158943
159079
  };
158944
159080
  }
158945
159081
  function ensureSessionMetaRow(db, sessionId) {
@@ -158951,6 +159087,7 @@ function toSessionMeta(row) {
158951
159087
  const transformErrorRaw = typeof row.last_transform_error === "string" ? row.last_transform_error : "";
158952
159088
  const cacheTtlRaw = typeof row.cache_ttl === "string" && row.cache_ttl.length > 0 ? row.cache_ttl : "5m";
158953
159089
  const systemPromptHashRaw = row.system_prompt_hash == null ? "" : row.system_prompt_hash;
159090
+ const lastTodoStateRaw = typeof row.last_todo_state === "string" ? row.last_todo_state : "";
158954
159091
  const numOrZero = (value) => typeof value === "number" ? value : 0;
158955
159092
  return {
158956
159093
  sessionId: row.session_id,
@@ -158969,7 +159106,8 @@ function toSessionMeta(row) {
158969
159106
  systemPromptTokens: numOrZero(row.system_prompt_tokens),
158970
159107
  conversationTokens: numOrZero(row.conversation_tokens),
158971
159108
  toolCallTokens: numOrZero(row.tool_call_tokens),
158972
- clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag)
159109
+ clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
159110
+ lastTodoState: lastTodoStateRaw
158973
159111
  };
158974
159112
  }
158975
159113
 
@@ -159166,6 +159304,28 @@ function setStrippedPlaceholderIds(db, sessionId, ids) {
159166
159304
  const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
159167
159305
  db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
159168
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
+ }
159169
159329
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
159170
159330
  init_logger();
159171
159331
  await init_read_session_db();
@@ -159185,7 +159345,7 @@ function resolveIsSubagentFromOpenCodeDb(sessionId) {
159185
159345
 
159186
159346
  // ../plugin/src/features/magic-context/storage-meta-session.ts
159187
159347
  function getOrCreateSessionMeta(db, sessionId) {
159188
- const result = db.prepare("SELECT session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, system_prompt_tokens, conversation_tokens, tool_call_tokens, cleared_reasoning_through_tag FROM session_meta WHERE session_id = ?").get(sessionId);
159348
+ const result = db.prepare("SELECT session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, system_prompt_tokens, conversation_tokens, tool_call_tokens, cleared_reasoning_through_tag, last_todo_state FROM session_meta WHERE session_id = ?").get(sessionId);
159189
159349
  if (isSessionMetaRow(result)) {
159190
159350
  return toSessionMeta(result);
159191
159351
  }
@@ -159751,14 +159911,118 @@ function expandFallbackChain(chain) {
159751
159911
  }
159752
159912
  return models;
159753
159913
  }
159914
+ function getAgentFallbackModels(agent) {
159915
+ const requirement = AGENT_MODEL_REQUIREMENTS[agent];
159916
+ if (!requirement)
159917
+ return;
159918
+ return expandFallbackChain(requirement.fallbackChain);
159919
+ }
159754
159920
 
159755
159921
  // ../plugin/src/shared/models-dev-cache.ts
159756
159922
  init_data_path();
159757
- init_logger();
159758
159923
  import { createHash as createHash3 } from "node:crypto";
159759
159924
  import { existsSync as existsSync4, readFileSync } from "node:fs";
159760
159925
  import { homedir as homedir2, platform } from "node:os";
159761
- import { join as join8 } from "node:path";
159926
+ import { join as join7 } from "node:path";
159927
+
159928
+ // ../plugin/src/shared/jsonc-parser.ts
159929
+ function stripJsonComments(content) {
159930
+ let result = "";
159931
+ let inString = false;
159932
+ let escaped = false;
159933
+ let inLineComment = false;
159934
+ let inBlockComment = false;
159935
+ for (let index = 0;index < content.length; index += 1) {
159936
+ const char = content[index];
159937
+ const next = content[index + 1];
159938
+ if (inLineComment) {
159939
+ if (char === `
159940
+ `) {
159941
+ inLineComment = false;
159942
+ result += char;
159943
+ }
159944
+ continue;
159945
+ }
159946
+ if (inBlockComment) {
159947
+ if (char === "*" && next === "/") {
159948
+ inBlockComment = false;
159949
+ index += 1;
159950
+ }
159951
+ continue;
159952
+ }
159953
+ if (inString) {
159954
+ result += char;
159955
+ if (escaped) {
159956
+ escaped = false;
159957
+ } else if (char === "\\") {
159958
+ escaped = true;
159959
+ } else if (char === '"') {
159960
+ inString = false;
159961
+ }
159962
+ continue;
159963
+ }
159964
+ if (char === '"') {
159965
+ inString = true;
159966
+ result += char;
159967
+ continue;
159968
+ }
159969
+ if (char === "/" && next === "/") {
159970
+ inLineComment = true;
159971
+ index += 1;
159972
+ continue;
159973
+ }
159974
+ if (char === "/" && next === "*") {
159975
+ inBlockComment = true;
159976
+ index += 1;
159977
+ continue;
159978
+ }
159979
+ result += char;
159980
+ }
159981
+ return result;
159982
+ }
159983
+ function stripTrailingCommas(content) {
159984
+ let result = "";
159985
+ let inString = false;
159986
+ let escaped = false;
159987
+ for (let index = 0;index < content.length; index += 1) {
159988
+ const char = content[index];
159989
+ if (inString) {
159990
+ result += char;
159991
+ if (escaped) {
159992
+ escaped = false;
159993
+ } else if (char === "\\") {
159994
+ escaped = true;
159995
+ } else if (char === '"') {
159996
+ inString = false;
159997
+ }
159998
+ continue;
159999
+ }
160000
+ if (char === '"') {
160001
+ inString = true;
160002
+ result += char;
160003
+ continue;
160004
+ }
160005
+ if (char === ",") {
160006
+ let lookahead = index + 1;
160007
+ while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
160008
+ lookahead += 1;
160009
+ }
160010
+ const next = content[lookahead];
160011
+ if (next === "}" || next === "]") {
160012
+ continue;
160013
+ }
160014
+ }
160015
+ result += char;
160016
+ }
160017
+ return result;
160018
+ }
160019
+ function parseJsonc(content) {
160020
+ const normalized = stripTrailingCommas(stripJsonComments(content));
160021
+ return JSON.parse(normalized);
160022
+ }
160023
+
160024
+ // ../plugin/src/shared/models-dev-cache.ts
160025
+ init_logger();
159762
160026
  var RELOAD_INTERVAL_MS = 5 * 60 * 1000;
159763
160027
  var apiCache = null;
159764
160028
  var recentlySeenApiSizes = new Set;
@@ -159774,15 +160038,15 @@ function getModelsJsonPath() {
159774
160038
  const cacheBase = getCacheDir();
159775
160039
  const source = process.env.OPENCODE_MODELS_URL?.trim();
159776
160040
  const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
159777
- return join8(cacheBase, "opencode", filename);
160041
+ return join7(cacheBase, "opencode", filename);
159778
160042
  }
159779
160043
  function getOpencodeConfigPath() {
159780
160044
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
159781
- const configDir = envDir ? envDir : platform() === "win32" ? join8(homedir2(), ".config", "opencode") : join8(process.env.XDG_CONFIG_HOME || join8(homedir2(), ".config"), "opencode");
159782
- 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");
159783
160047
  if (existsSync4(jsonc))
159784
160048
  return jsonc;
159785
- const json2 = join8(configDir, "opencode.json");
160049
+ const json2 = join7(configDir, "opencode.json");
159786
160050
  if (existsSync4(json2))
159787
160051
  return json2;
159788
160052
  return null;
@@ -159833,9 +160097,7 @@ function loadModelsDevMetadataFromFile() {
159833
160097
  try {
159834
160098
  const configPath = getOpencodeConfigPath();
159835
160099
  if (configPath && existsSync4(configPath)) {
159836
- let raw = readFileSync(configPath, "utf-8");
159837
- raw = raw.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/gm, (match) => match.startsWith('"') ? match : "");
159838
- const config2 = JSON.parse(raw);
160100
+ const config2 = parseJsonc(readFileSync(configPath, "utf-8"));
159839
160101
  if (config2.provider && typeof config2.provider === "object") {
159840
160102
  for (const [providerId, provider2] of Object.entries(config2.provider)) {
159841
160103
  if (!provider2?.models || typeof provider2.models !== "object")
@@ -160160,12 +160422,12 @@ init_logger();
160160
160422
  // src/subagent-runner.ts
160161
160423
  import * as childProcess from "node:child_process";
160162
160424
  import { existsSync as existsSync5 } from "node:fs";
160163
- import { dirname as dirname2, resolve as resolvePath } from "node:path";
160425
+ import { dirname as dirname3, resolve as resolvePath } from "node:path";
160164
160426
  import { createInterface } from "node:readline";
160165
160427
  import { fileURLToPath } from "node:url";
160166
160428
  function resolveSubagentEntryPath() {
160167
160429
  try {
160168
- const here = dirname2(fileURLToPath(import.meta.url));
160430
+ const here = dirname3(fileURLToPath(import.meta.url));
160169
160431
  const candidate = resolvePath(here, "subagent-entry.js");
160170
160432
  if (existsSync5(candidate))
160171
160433
  return candidate;
@@ -160607,69 +160869,6 @@ ${sidekickText}
160607
160869
  });
160608
160870
  }
160609
160871
 
160610
- // ../plugin/src/features/magic-context/dreamer/queue.ts
160611
- function enqueueDream(db, projectIdentity, reason, force = false) {
160612
- const now = Date.now();
160613
- return db.transaction(() => {
160614
- const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
160615
- db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
160616
- const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
160617
- if (existing) {
160618
- return null;
160619
- }
160620
- const result = db.prepare("INSERT INTO dream_queue (project_path, reason, enqueued_at) VALUES (?, ?, ?)").run(projectIdentity, reason, now);
160621
- return {
160622
- id: Number(result.lastInsertRowid),
160623
- projectIdentity,
160624
- reason,
160625
- enqueuedAt: now,
160626
- startedAt: null
160627
- };
160628
- })();
160629
- }
160630
- function peekQueue(db, projectIdentity) {
160631
- 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();
160632
- if (!row)
160633
- return null;
160634
- return {
160635
- id: row.id,
160636
- projectIdentity: row.project_path,
160637
- reason: row.reason,
160638
- enqueuedAt: row.enqueued_at,
160639
- startedAt: null
160640
- };
160641
- }
160642
- function dequeueNext(db, projectIdentity) {
160643
- const now = Date.now();
160644
- return db.transaction(() => {
160645
- const entry = peekQueue(db, projectIdentity);
160646
- if (!entry)
160647
- return null;
160648
- const result = db.prepare("UPDATE dream_queue SET started_at = ? WHERE id = ? AND started_at IS NULL").run(now, entry.id);
160649
- if (result.changes === 0)
160650
- return null;
160651
- return { ...entry, startedAt: now };
160652
- })();
160653
- }
160654
- function removeDreamEntry(db, id) {
160655
- db.prepare("DELETE FROM dream_queue WHERE id = ?").run(id);
160656
- }
160657
- function resetDreamEntry(db, id) {
160658
- db.prepare("UPDATE dream_queue SET started_at = NULL, retry_count = COALESCE(retry_count, 0) + 1 WHERE id = ?").run(id);
160659
- }
160660
- function getEntryRetryCount(db, id) {
160661
- const row = db.prepare("SELECT retry_count FROM dream_queue WHERE id = ?").get(id);
160662
- return row?.retry_count ?? 0;
160663
- }
160664
- function clearStaleEntries(db, maxAgeMs) {
160665
- const cutoff = Date.now() - maxAgeMs;
160666
- const result = db.prepare("DELETE FROM dream_queue WHERE started_at IS NOT NULL AND started_at < ?").run(cutoff);
160667
- return result.changes;
160668
- }
160669
-
160670
- // src/commands/ctx-dream.ts
160671
- init_logger();
160672
-
160673
160872
  // ../plugin/src/features/magic-context/dreamer/storage-dream-state.ts
160674
160873
  var getDreamStateStatements = new WeakMap;
160675
160874
  var setDreamStateStatements = new WeakMap;
@@ -160765,15 +160964,137 @@ function releaseLease(db, holderId) {
160765
160964
  deleteDreamState(db, LEASE_EXPIRY_KEY);
160766
160965
  })();
160767
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();
160768
161042
  // ../plugin/src/features/magic-context/dreamer/runner.ts
160769
161043
  import { existsSync as existsSync6 } from "node:fs";
160770
- import { join as join9 } from "node:path";
161044
+ import { join as join8 } from "node:path";
160771
161045
 
160772
161046
  // ../plugin/src/shared/index.ts
160773
161047
  init_logger();
160774
161048
 
160775
161049
  // ../plugin/src/shared/model-suggestion-retry.ts
160776
161050
  init_logger();
161051
+
161052
+ // ../plugin/src/shared/resolve-fallbacks.ts
161053
+ function resolveFallbackChain(agentName, userFallbacks) {
161054
+ const userList = normalizeUserFallbacks(userFallbacks);
161055
+ if (userList.length > 0) {
161056
+ return dedupe(userList.filter(isValidModelSpec));
161057
+ }
161058
+ const builtin = getAgentFallbackModels(agentName);
161059
+ if (!builtin || builtin.length === 0)
161060
+ return [];
161061
+ return dedupe(builtin.filter(isValidModelSpec));
161062
+ }
161063
+ function normalizeUserFallbacks(userFallbacks) {
161064
+ if (!userFallbacks)
161065
+ return [];
161066
+ if (typeof userFallbacks === "string") {
161067
+ const trimmed = userFallbacks.trim();
161068
+ return trimmed ? [trimmed] : [];
161069
+ }
161070
+ return userFallbacks.map((s) => s.trim()).filter((s) => s.length > 0);
161071
+ }
161072
+ function isValidModelSpec(spec) {
161073
+ const slash = spec.indexOf("/");
161074
+ return slash > 0 && slash < spec.length - 1;
161075
+ }
161076
+ function dedupe(list) {
161077
+ const seen = new Set;
161078
+ const out = [];
161079
+ for (const item of list) {
161080
+ if (seen.has(item))
161081
+ continue;
161082
+ seen.add(item);
161083
+ out.push(item);
161084
+ }
161085
+ return out;
161086
+ }
161087
+ function parseProviderModel(spec) {
161088
+ const slash = spec.indexOf("/");
161089
+ if (slash < 1 || slash >= spec.length - 1)
161090
+ return null;
161091
+ return {
161092
+ providerID: spec.slice(0, slash).trim(),
161093
+ modelID: spec.slice(slash + 1).trim()
161094
+ };
161095
+ }
161096
+
161097
+ // ../plugin/src/shared/model-suggestion-retry.ts
160777
161098
  function extractMessage(error51) {
160778
161099
  if (typeof error51 === "string")
160779
161100
  return error51;
@@ -160828,6 +161149,9 @@ function parseModelSuggestion(error51) {
160828
161149
  };
160829
161150
  }
160830
161151
  async function promptWithTimeout(client, args, timeoutMs, signal) {
161152
+ if (signal?.aborted) {
161153
+ throw new Error("prompt aborted by external signal");
161154
+ }
160831
161155
  const controller = new AbortController;
160832
161156
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
160833
161157
  const onExternalAbort = () => controller.abort();
@@ -160850,16 +161174,39 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
160850
161174
  signal?.removeEventListener("abort", onExternalAbort);
160851
161175
  }
160852
161176
  }
160853
- async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
160854
- const timeoutMs = options.timeoutMs ?? 300000;
161177
+ function isNonRetryable(error51, externalSignal) {
161178
+ if (externalSignal?.aborted)
161179
+ return true;
161180
+ if (error51 instanceof Error) {
161181
+ if (error51.name === "AbortError")
161182
+ return true;
161183
+ if (error51.message === "prompt aborted by external signal")
161184
+ return true;
161185
+ if (/^prompt timed out after \d+ms$/.test(error51.message))
161186
+ return true;
161187
+ }
161188
+ if (detectOverflow(error51).isOverflow)
161189
+ return true;
161190
+ return false;
161191
+ }
161192
+ function shortErr(error51) {
161193
+ if (error51 instanceof Error) {
161194
+ return error51.name && error51.name !== "Error" ? `${error51.name}: ${error51.message}` : error51.message;
161195
+ }
161196
+ return extractMessage(error51);
161197
+ }
161198
+ async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
160855
161199
  try {
160856
- await promptWithTimeout(client, args, timeoutMs, options.signal);
161200
+ await promptWithTimeout(client, args, timeoutMs, signal);
161201
+ return;
160857
161202
  } catch (error51) {
161203
+ if (isNonRetryable(error51, signal))
161204
+ throw error51;
160858
161205
  const suggestion = parseModelSuggestion(error51);
160859
161206
  if (!suggestion || !args.body.model) {
160860
161207
  throw error51;
160861
161208
  }
160862
- log("[model-suggestion-retry] Model not found, retrying with suggestion", {
161209
+ log(`[${callContext}] ${label}: model not found, retrying with suggestion`, {
160863
161210
  original: `${suggestion.providerID}/${suggestion.modelID}`,
160864
161211
  suggested: suggestion.suggestion
160865
161212
  });
@@ -160872,9 +161219,55 @@ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
160872
161219
  modelID: suggestion.suggestion
160873
161220
  }
160874
161221
  }
160875
- }, timeoutMs, options.signal);
161222
+ }, timeoutMs, signal);
160876
161223
  }
160877
161224
  }
161225
+ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
161226
+ const timeoutMs = options.timeoutMs ?? 300000;
161227
+ const callContext = options.callContext ?? "subagent";
161228
+ const fallbacks = options.fallbackModels ?? [];
161229
+ const explicitPrimaryLabel = args.body.model?.providerID && args.body.model.modelID ? `${args.body.model.providerID}/${args.body.model.modelID}` : "primary";
161230
+ let lastError = null;
161231
+ try {
161232
+ await attemptOnce(client, args, timeoutMs, options.signal, callContext, explicitPrimaryLabel);
161233
+ return;
161234
+ } catch (error51) {
161235
+ lastError = error51;
161236
+ if (isNonRetryable(error51, options.signal))
161237
+ throw error51;
161238
+ if (fallbacks.length === 0) {
161239
+ throw error51;
161240
+ }
161241
+ log(`[${callContext}] primary (${explicitPrimaryLabel}) failed: ${shortErr(error51)}; trying ${fallbacks.length} fallback(s)`);
161242
+ }
161243
+ for (let i = 0;i < fallbacks.length; i += 1) {
161244
+ const parsed = parseProviderModel(fallbacks[i]);
161245
+ if (!parsed) {
161246
+ log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
161247
+ continue;
161248
+ }
161249
+ const label = `${parsed.providerID}/${parsed.modelID}`;
161250
+ const attemptArgs = {
161251
+ ...args,
161252
+ body: { ...args.body, model: parsed }
161253
+ };
161254
+ try {
161255
+ await attemptOnce(client, attemptArgs, timeoutMs, options.signal, callContext, label);
161256
+ log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
161257
+ return;
161258
+ } catch (error51) {
161259
+ lastError = error51;
161260
+ if (isNonRetryable(error51, options.signal))
161261
+ throw error51;
161262
+ const remaining = fallbacks.length - i - 1;
161263
+ if (remaining > 0) {
161264
+ log(`[${callContext}] ${label} failed: ${shortErr(error51)}; ${remaining} fallback(s) left`);
161265
+ }
161266
+ }
161267
+ }
161268
+ log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; last error: ${shortErr(lastError)}`);
161269
+ throw lastError ?? new Error("All fallback models failed");
161270
+ }
160878
161271
  // ../plugin/src/shared/normalize-sdk-response.ts
160879
161272
  function normalizeSDKResponse(response, fallback, options) {
160880
161273
  if (response === null || response === undefined) {
@@ -162052,7 +162445,12 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
162052
162445
  system: DREAMER_SYSTEM_PROMPT,
162053
162446
  parts: [{ type: "text", text: prompt, synthetic: true }]
162054
162447
  }
162055
- }, { timeoutMs: Math.min(remainingMs, 5 * 60 * 1000), signal: abortController.signal });
162448
+ }, {
162449
+ timeoutMs: Math.min(remainingMs, 5 * 60 * 1000),
162450
+ signal: abortController.signal,
162451
+ fallbackModels: args.fallbackModels,
162452
+ callContext: "dreamer:user-memories"
162453
+ });
162056
162454
  const messagesResponse = await args.client.session.messages({
162057
162455
  path: { id: agentSessionId },
162058
162456
  query: { directory: args.sessionDirectory }
@@ -162111,7 +162509,8 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
162111
162509
  }
162112
162510
  return result;
162113
162511
  } catch (error51) {
162114
- log(`[dreamer] user-memories: review failed: ${getErrorMessage(error51)}`);
162512
+ const errorDescription = describeError(error51);
162513
+ log(`[dreamer] user-memories: review failed: ${errorDescription.brief}`, errorDescription.stackHead ? { stackHead: errorDescription.stackHead } : undefined);
162115
162514
  return result;
162116
162515
  } finally {
162117
162516
  clearInterval(leaseInterval);
@@ -162143,6 +162542,7 @@ function insertDreamRun(db, run) {
162143
162542
 
162144
162543
  // ../plugin/src/features/magic-context/dreamer/runner.ts
162145
162544
  var dreamProjectDirectories = new Map;
162545
+ var CIRCUIT_BREAKER_THRESHOLD = 3;
162146
162546
  function registerDreamProjectDirectory(projectIdentity, directory) {
162147
162547
  dreamProjectDirectories.set(projectIdentity, directory);
162148
162548
  }
@@ -162159,8 +162559,27 @@ function countNewIds(beforeIds, afterIds) {
162159
162559
  }
162160
162560
  return count;
162161
162561
  }
162562
+ function getCircuitBreakerSignature(error51, brief) {
162563
+ if (error51 instanceof Error && error51.name && error51.name !== "Error") {
162564
+ return error51.name;
162565
+ }
162566
+ const namedError = error51;
162567
+ if (namedError && typeof namedError === "object" && typeof namedError.name === "string" && namedError.name.length > 0 && namedError.name !== "Error") {
162568
+ return namedError.name;
162569
+ }
162570
+ return brief.split(":")[0]?.trim().split(/\s+/)[0] || brief || "unknown";
162571
+ }
162572
+ function shouldSkipCircuitBreaker(error51, brief) {
162573
+ const namedError = error51;
162574
+ const name2 = error51 instanceof Error ? error51.name : namedError && typeof namedError === "object" && typeof namedError.name === "string" ? namedError.name : "";
162575
+ const combined = `${name2} ${brief}`.toLowerCase();
162576
+ return name2 === "AbortError" || combined.includes("lease");
162577
+ }
162578
+ function logWithStackHead(message, stackHead) {
162579
+ log(message, stackHead ? { stackHead } : undefined);
162580
+ }
162162
162581
  function getOpenCodeDbPath2() {
162163
- return join9(getDataDir(), "opencode", "opencode.db");
162582
+ return join8(getDataDir(), "opencode", "opencode.db");
162164
162583
  }
162165
162584
  function openOpenCodeDb() {
162166
162585
  const dbPath = getOpenCodeDbPath2();
@@ -162235,9 +162654,11 @@ async function identifyKeyFilesForSession(args) {
162235
162654
  try {
162236
162655
  if (!renewLease(args.db, args.holderId)) {
162237
162656
  log(`[key-files][${args.sessionId}] lease renewal failed — aborting`);
162657
+ args.onLeaseLost?.(`key-files:${args.sessionId}`);
162238
162658
  abortController.abort();
162239
162659
  }
162240
- } catch {
162660
+ } catch (error51) {
162661
+ args.onLeaseLost?.(`key-files:${args.sessionId}`, error51);
162241
162662
  abortController.abort();
162242
162663
  }
162243
162664
  }, 60000);
@@ -162264,7 +162685,12 @@ async function identifyKeyFilesForSession(args) {
162264
162685
  system: KEY_FILES_SYSTEM_PROMPT,
162265
162686
  parts: [{ type: "text", text: prompt, synthetic: true }]
162266
162687
  }
162267
- }, { timeoutMs: Math.min(remainingMs, 300000), signal: abortController.signal });
162688
+ }, {
162689
+ timeoutMs: Math.min(remainingMs, 300000),
162690
+ signal: abortController.signal,
162691
+ fallbackModels: args.fallbackModels,
162692
+ callContext: `dreamer:key-files:${args.sessionId.slice(0, 12)}`
162693
+ });
162268
162694
  const messagesResponse = await args.client.session.messages({
162269
162695
  path: { id: agentSessionId },
162270
162696
  query: { directory: args.sessionDirectory }
@@ -162287,6 +162713,10 @@ async function identifyKeyFilesForSession(args) {
162287
162713
  log(`[key-files][${args.sessionId}] could not parse agent output — using heuristic fallback`);
162288
162714
  applyHeuristicFallback();
162289
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
+ }
162290
162720
  log(`[key-files][${args.sessionId}] identification failed: ${getErrorMessage(error51)} — using heuristic fallback`);
162291
162721
  try {
162292
162722
  applyHeuristicFallback();
@@ -162327,6 +162757,10 @@ async function identifyKeyFiles(args) {
162327
162757
  }
162328
162758
  log(`[key-files] evaluating ${sessionIds.length} active session(s) for ${args.projectIdentity}`);
162329
162759
  for (const sessionId of sessionIds) {
162760
+ if (args.isLeaseLost?.()) {
162761
+ log("[key-files] lease lost — stopping key-file identification");
162762
+ break;
162763
+ }
162330
162764
  if (Date.now() > args.deadline) {
162331
162765
  log("[key-files] deadline reached — stopping key-file identification");
162332
162766
  break;
@@ -162337,6 +162771,9 @@ async function identifyKeyFiles(args) {
162337
162771
  parentSessionId: args.parentSessionId,
162338
162772
  sessionDirectory: args.sessionDirectory,
162339
162773
  holderId: args.holderId,
162774
+ fallbackModels: args.fallbackModels,
162775
+ onLeaseLost: args.onLeaseLost,
162776
+ isLeaseLost: args.isLeaseLost,
162340
162777
  deadline: args.deadline,
162341
162778
  sessionId,
162342
162779
  config: args.config
@@ -162390,8 +162827,53 @@ async function runDream(args) {
162390
162827
  const deadline = startedAt + args.maxRuntimeMinutes * 60 * 1000;
162391
162828
  const lastDreamAt = getDreamState(args.db, `last_dream_at:${args.projectIdentity}`) ?? getDreamState(args.db, "last_dream_at");
162392
162829
  log(`[dreamer] last dream at: ${lastDreamAt ?? "never"} (project=${args.projectIdentity})`);
162830
+ let lastErrorSignature = null;
162831
+ let consecutiveSameErrorFailures = 0;
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
+ };
162393
162871
  try {
162394
162872
  for (const taskName of args.tasks) {
162873
+ if (!verifyLeaseStillHeld(`before task ${taskName}`)) {
162874
+ recordLeaseLostTask(`before task ${taskName}`);
162875
+ break;
162876
+ }
162395
162877
  if (Date.now() > deadline) {
162396
162878
  log(`[dreamer] deadline reached, stopping after ${result.tasks.length} tasks`);
162397
162879
  break;
@@ -162404,18 +162886,20 @@ async function runDream(args) {
162404
162886
  try {
162405
162887
  if (!renewLease(args.db, holderId)) {
162406
162888
  log(`[dreamer] task ${taskName}: lease renewal failed — aborting LLM call`);
162889
+ markLeaseLost(`task ${taskName} lease renewal`);
162407
162890
  taskAbortController.abort();
162408
162891
  }
162409
162892
  } catch (err) {
162410
162893
  log(`[dreamer] task ${taskName}: lease renewal threw — aborting LLM call: ${err}`);
162894
+ markLeaseLost(`task ${taskName} lease renewal`, err);
162411
162895
  taskAbortController.abort();
162412
162896
  }
162413
162897
  }, 60000);
162414
162898
  try {
162415
162899
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
162416
162900
  const existingDocs = taskName === "maintain-docs" ? {
162417
- architecture: existsSync6(join9(docsDir, "ARCHITECTURE.md")),
162418
- structure: existsSync6(join9(docsDir, "STRUCTURE.md"))
162901
+ architecture: existsSync6(join8(docsDir, "ARCHITECTURE.md")),
162902
+ structure: existsSync6(join8(docsDir, "STRUCTURE.md"))
162419
162903
  } : undefined;
162420
162904
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
162421
162905
  id: um.id,
@@ -162450,8 +162934,13 @@ async function runDream(args) {
162450
162934
  }
162451
162935
  }, {
162452
162936
  timeoutMs: args.taskTimeoutMinutes * 60 * 1000,
162453
- signal: taskAbortController.signal
162937
+ signal: taskAbortController.signal,
162938
+ fallbackModels: args.fallbackModels,
162939
+ callContext: `dreamer:${taskName}`
162454
162940
  });
162941
+ if (lostLease) {
162942
+ throw new Error(lostLeaseReason ?? `Dream lease lost during ${taskName}`);
162943
+ }
162455
162944
  const messagesResponse = await args.client.session.messages({
162456
162945
  path: { id: agentSessionId },
162457
162946
  query: { directory: args.sessionDirectory ?? args.projectIdentity }
@@ -162470,16 +162959,43 @@ async function runDream(args) {
162470
162959
  durationMs,
162471
162960
  result: taskResult
162472
162961
  });
162962
+ lastErrorSignature = null;
162963
+ consecutiveSameErrorFailures = 0;
162473
162964
  } catch (error51) {
162474
162965
  const durationMs = Date.now() - taskStartedAt;
162475
- const errorMsg = getErrorMessage(error51);
162476
- log(`[dreamer] task ${taskName}: failed after ${(durationMs / 1000).toFixed(1)}s — ${errorMsg}`);
162966
+ const errorDescription = describeError(error51);
162967
+ logWithStackHead(`[dreamer] task ${taskName}: failed after ${(durationMs / 1000).toFixed(1)}s — ${errorDescription.brief}`, errorDescription.stackHead);
162477
162968
  result.tasks.push({
162478
162969
  name: taskName,
162479
162970
  durationMs,
162480
162971
  result: null,
162481
- error: errorMsg
162972
+ error: errorDescription.brief
162482
162973
  });
162974
+ if (lostLease) {
162975
+ lastErrorSignature = null;
162976
+ consecutiveSameErrorFailures = 0;
162977
+ } else if (shouldSkipCircuitBreaker(error51, errorDescription.brief)) {
162978
+ lastErrorSignature = null;
162979
+ consecutiveSameErrorFailures = 0;
162980
+ } else {
162981
+ const signature = getCircuitBreakerSignature(error51, errorDescription.brief);
162982
+ if (signature === lastErrorSignature) {
162983
+ consecutiveSameErrorFailures += 1;
162984
+ } else {
162985
+ lastErrorSignature = signature;
162986
+ consecutiveSameErrorFailures = 1;
162987
+ }
162988
+ if (consecutiveSameErrorFailures >= CIRCUIT_BREAKER_THRESHOLD) {
162989
+ circuitBreakerTripped = true;
162990
+ log(`[dreamer] circuit breaker: ${consecutiveSameErrorFailures} consecutive ${signature} failures — aborting remaining tasks`);
162991
+ result.tasks.push({
162992
+ name: "circuit-breaker",
162993
+ durationMs: 0,
162994
+ result: "",
162995
+ error: `Aborted remaining tasks: ${consecutiveSameErrorFailures} consecutive ${signature} failures. Configure dreamer model/fallback_models in magic-context.jsonc.`
162996
+ });
162997
+ }
162998
+ }
162483
162999
  } finally {
162484
163000
  clearInterval(leaseRenewalInterval);
162485
163001
  if (agentSessionId) {
@@ -162491,10 +163007,32 @@ async function runDream(args) {
162491
163007
  });
162492
163008
  }
162493
163009
  }
163010
+ if (lostLease) {
163011
+ recordLeaseLostTask(`task ${taskName}`);
163012
+ break;
163013
+ }
163014
+ if (circuitBreakerTripped) {
163015
+ break;
163016
+ }
163017
+ }
163018
+ if (lostLease) {
163019
+ log("[dreamer] lease lost: skipping all post-task phases");
163020
+ recordLeaseLostTask("post-task phases");
163021
+ } else if (circuitBreakerTripped) {
163022
+ log("[dreamer] circuit breaker: skipping post-task phases");
163023
+ result.tasks.push({
163024
+ name: "post-task-phases",
163025
+ durationMs: 0,
163026
+ result: "",
163027
+ error: "Skipped post-task phases after circuit breaker tripped; configure dreamer model/fallback_models in magic-context.jsonc."
163028
+ });
162494
163029
  }
162495
- if (args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
163030
+ if (!circuitBreakerTripped && !lostLease && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
162496
163031
  const umStart = Date.now();
162497
163032
  try {
163033
+ if (!verifyLeaseStillHeld("before user-memory review")) {
163034
+ throw new Error(lostLeaseReason ?? "Dream lease lost before user-memory review");
163035
+ }
162498
163036
  const reviewResult = await reviewUserMemories({
162499
163037
  db: args.db,
162500
163038
  client: args.client,
@@ -162502,8 +163040,12 @@ async function runDream(args) {
162502
163040
  sessionDirectory: args.sessionDirectory,
162503
163041
  holderId,
162504
163042
  deadline,
162505
- promotionThreshold: args.experimentalUserMemories.promotionThreshold
163043
+ promotionThreshold: args.experimentalUserMemories.promotionThreshold,
163044
+ fallbackModels: args.fallbackModels
162506
163045
  });
163046
+ if (!verifyLeaseStillHeld("after user-memory review")) {
163047
+ throw new Error(lostLeaseReason ?? "Dream lease lost after user-memory review");
163048
+ }
162507
163049
  const umOutput = `promoted=${reviewResult.promoted} merged=${reviewResult.merged} dismissed=${reviewResult.dismissed} consumed=${reviewResult.candidatesConsumed}`;
162508
163050
  if (reviewResult.promoted > 0 || reviewResult.merged > 0 || reviewResult.dismissed > 0) {
162509
163051
  log(`[dreamer] user-memories: ${umOutput}`);
@@ -162514,17 +163056,23 @@ async function runDream(args) {
162514
163056
  result: umOutput
162515
163057
  });
162516
163058
  } catch (error51) {
162517
- log(`[dreamer] user-memory review failed: ${getErrorMessage(error51)}`);
163059
+ const errorDescription = describeError(error51);
163060
+ logWithStackHead(`[dreamer] user-memory review failed: ${errorDescription.brief}`, errorDescription.stackHead);
162518
163061
  result.tasks.push({
162519
163062
  name: "user memories",
162520
163063
  durationMs: Date.now() - umStart,
162521
163064
  result: "",
162522
- error: getErrorMessage(error51)
163065
+ error: errorDescription.brief
162523
163066
  });
162524
163067
  }
163068
+ if (lostLease)
163069
+ recordLeaseLostTask("user-memory review");
162525
163070
  }
162526
- if (Date.now() <= deadline) {
163071
+ if (!circuitBreakerTripped && !lostLease && Date.now() <= deadline) {
162527
163072
  try {
163073
+ if (!verifyLeaseStillHeld("before smart-note evaluation")) {
163074
+ throw new Error(lostLeaseReason ?? "Dream lease lost before smart-note evaluation");
163075
+ }
162528
163076
  await evaluateSmartNotes({
162529
163077
  db: args.db,
162530
163078
  client: args.client,
@@ -162533,15 +163081,27 @@ async function runDream(args) {
162533
163081
  sessionDirectory: args.sessionDirectory,
162534
163082
  holderId,
162535
163083
  deadline,
162536
- result
163084
+ result,
163085
+ fallbackModels: args.fallbackModels,
163086
+ onLeaseLost: markLeaseLost,
163087
+ isLeaseLost: () => lostLease
162537
163088
  });
163089
+ if (!verifyLeaseStillHeld("after smart-note evaluation")) {
163090
+ throw new Error(lostLeaseReason ?? "Dream lease lost after smart-note evaluation");
163091
+ }
162538
163092
  } catch (error51) {
162539
- log(`[dreamer] smart note evaluation failed: ${getErrorMessage(error51)}`);
163093
+ const errorDescription = describeError(error51);
163094
+ logWithStackHead(`[dreamer] smart note evaluation failed: ${errorDescription.brief}`, errorDescription.stackHead);
162540
163095
  }
163096
+ if (lostLease)
163097
+ recordLeaseLostTask("smart-note evaluation");
162541
163098
  }
162542
- if (args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
163099
+ if (!circuitBreakerTripped && !lostLease && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
162543
163100
  const kfStart = Date.now();
162544
163101
  try {
163102
+ if (!verifyLeaseStillHeld("before key-file identification")) {
163103
+ throw new Error(lostLeaseReason ?? "Dream lease lost before key-file identification");
163104
+ }
162545
163105
  await identifyKeyFiles({
162546
163106
  db: args.db,
162547
163107
  client: args.client,
@@ -162550,22 +163110,31 @@ async function runDream(args) {
162550
163110
  sessionDirectory: args.sessionDirectory ?? args.projectIdentity,
162551
163111
  holderId,
162552
163112
  deadline,
162553
- config: args.experimentalPinKeyFiles
163113
+ config: args.experimentalPinKeyFiles,
163114
+ fallbackModels: args.fallbackModels,
163115
+ onLeaseLost: markLeaseLost,
163116
+ isLeaseLost: () => lostLease
162554
163117
  });
163118
+ if (!verifyLeaseStillHeld("after key-file identification")) {
163119
+ throw new Error(lostLeaseReason ?? "Dream lease lost after key-file identification");
163120
+ }
162555
163121
  result.tasks.push({
162556
163122
  name: "key files",
162557
163123
  durationMs: Date.now() - kfStart,
162558
163124
  result: "completed"
162559
163125
  });
162560
163126
  } catch (error51) {
162561
- log(`[key-files] identification phase failed: ${getErrorMessage(error51)}`);
163127
+ const errorDescription = describeError(error51);
163128
+ logWithStackHead(`[key-files] identification phase failed: ${errorDescription.brief}`, errorDescription.stackHead);
162562
163129
  result.tasks.push({
162563
163130
  name: "key files",
162564
163131
  durationMs: Date.now() - kfStart,
162565
163132
  result: "",
162566
- error: getErrorMessage(error51)
163133
+ error: errorDescription.brief
162567
163134
  });
162568
163135
  }
163136
+ if (lostLease)
163137
+ recordLeaseLostTask("key-file identification");
162569
163138
  }
162570
163139
  } finally {
162571
163140
  releaseLease(args.db, holderId);
@@ -162598,9 +163167,15 @@ async function runDream(args) {
162598
163167
  smartNotesPending: result.smartNotesPending,
162599
163168
  memoryChanges: persistedMemoryChanges
162600
163169
  });
162601
- const POST_TASK_NAMES = new Set(["smart-notes", "user memories", "key files"]);
163170
+ const POST_TASK_NAMES = new Set([
163171
+ "smart-notes",
163172
+ "user memories",
163173
+ "key files",
163174
+ "post-task-phases",
163175
+ "circuit-breaker"
163176
+ ]);
162602
163177
  const hasSuccessfulTask = result.tasks.some((t) => !t.error && !POST_TASK_NAMES.has(t.name));
162603
- if (hasSuccessfulTask) {
163178
+ if (hasSuccessfulTask && !lostLease) {
162604
163179
  setDreamState(args.db, `last_dream_at:${args.projectIdentity}`, String(result.finishedAt));
162605
163180
  setDreamState(args.db, "last_dream_at", String(result.finishedAt));
162606
163181
  }
@@ -162649,9 +163224,11 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
162649
163224
  try {
162650
163225
  if (!renewLease(args.db, args.holderId)) {
162651
163226
  log("[dreamer] smart notes: lease renewal failed — aborting");
163227
+ args.onLeaseLost?.("smart notes");
162652
163228
  abortController.abort();
162653
163229
  }
162654
- } catch {
163230
+ } catch (error51) {
163231
+ args.onLeaseLost?.("smart notes", error51);
162655
163232
  abortController.abort();
162656
163233
  }
162657
163234
  }, 60000);
@@ -162677,7 +163254,12 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
162677
163254
  system: DREAMER_SYSTEM_PROMPT,
162678
163255
  parts: [{ type: "text", text: evaluationPrompt, synthetic: true }]
162679
163256
  }
162680
- }, { timeoutMs: Math.min(remainingMs, 300000), signal: abortController.signal });
163257
+ }, {
163258
+ timeoutMs: Math.min(remainingMs, 300000),
163259
+ signal: abortController.signal,
163260
+ fallbackModels: args.fallbackModels,
163261
+ callContext: "dreamer:smart-notes"
163262
+ });
162681
163263
  const messagesResponse = await args.client.session.messages({
162682
163264
  path: { id: agentSessionId },
162683
163265
  query: { directory: args.sessionDirectory ?? args.projectIdentity }
@@ -162736,15 +163318,15 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
162736
163318
  });
162737
163319
  } catch (error51) {
162738
163320
  const durationMs = Date.now() - taskStartedAt;
162739
- const errorMsg = getErrorMessage(error51);
163321
+ const errorDescription = describeError(error51);
162740
163322
  args.result.smartNotesSurfaced = 0;
162741
163323
  args.result.smartNotesPending = pendingNotes.length;
162742
- log(`[dreamer] smart notes: failed after ${(durationMs / 1000).toFixed(1)}s — ${errorMsg}`);
163324
+ logWithStackHead(`[dreamer] smart notes: failed after ${(durationMs / 1000).toFixed(1)}s — ${errorDescription.brief}`, errorDescription.stackHead);
162743
163325
  args.result.tasks.push({
162744
163326
  name: "smart-notes",
162745
163327
  durationMs,
162746
163328
  result: null,
162747
- error: errorMsg
163329
+ error: errorDescription.brief
162748
163330
  });
162749
163331
  } finally {
162750
163332
  clearInterval(leaseInterval);
@@ -162777,7 +163359,8 @@ async function processDreamQueue(args) {
162777
163359
  maxRuntimeMinutes: args.maxRuntimeMinutes,
162778
163360
  sessionDirectory: projectDirectory,
162779
163361
  experimentalUserMemories: args.experimentalUserMemories,
162780
- experimentalPinKeyFiles: args.experimentalPinKeyFiles
163362
+ experimentalPinKeyFiles: args.experimentalPinKeyFiles,
163363
+ fallbackModels: args.fallbackModels
162781
163364
  });
162782
163365
  } catch (error51) {
162783
163366
  log(`[dreamer] runDream threw for ${entry.projectIdentity}: ${getErrorMessage(error51)}`);
@@ -163501,7 +164084,8 @@ async function sweepProject(reg, origin) {
163501
164084
  maxRuntimeMinutes: reg.dreamerConfig.max_runtime_minutes,
163502
164085
  experimentalUserMemories: reg.experimentalUserMemories,
163503
164086
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
163504
- projectIdentity: registrationIdentity
164087
+ projectIdentity: registrationIdentity,
164088
+ fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
163505
164089
  });
163506
164090
  } catch (error51) {
163507
164091
  log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
@@ -163923,7 +164507,6 @@ function createScheduler(config2) {
163923
164507
  }
163924
164508
  };
163925
164509
  }
163926
-
163927
164510
  // ../plugin/src/features/magic-context/tagger.ts
163928
164511
  var TOOL_COMPOSITE_KEY_SEP = "\x00";
163929
164512
  function makeToolCompositeKey(ownerMsgId, callId) {
@@ -164962,16 +165545,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
164962
165545
  function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
164963
165546
  const cached2 = injectionCache.get(sessionId);
164964
165547
  if (!isCacheBusting && cached2) {
164965
- if (cached2.compartmentEndMessageId.length > 0) {
164966
- const cutoffIndex2 = messages.findIndex((message) => message.info.id === cached2.compartmentEndMessageId);
164967
- if (cutoffIndex2 >= 0) {
164968
- const remaining = messages.slice(cutoffIndex2 + 1);
164969
- messages.splice(0, messages.length, ...remaining);
164970
- } else {
164971
- 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
+ }
164972
165563
  }
165564
+ return { ...prepared, rebuiltFromDb: false };
164973
165565
  }
164974
- return cached2;
164975
165566
  }
164976
165567
  const compartments = getCompartments(db, sessionId);
164977
165568
  const facts = getSessionFacts(db, sessionId);
@@ -165003,7 +165594,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165003
165594
  }
165004
165595
  }
165005
165596
  if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
165006
- injectionCache.delete(sessionId);
165597
+ injectionCache.set(sessionId, {
165598
+ kind: "empty",
165599
+ compartmentEndMessageId: "",
165600
+ renderedBytes: 0
165601
+ });
165007
165602
  return null;
165008
165603
  }
165009
165604
  let dateRanges;
@@ -165036,9 +165631,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165036
165631
  compartmentCount: 0,
165037
165632
  skippedVisibleMessages: 0,
165038
165633
  factCount: facts.length,
165039
- memoryCount
165634
+ memoryCount,
165635
+ rebuiltFromDb: true
165040
165636
  };
165041
- injectionCache.set(sessionId, result2);
165637
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
165042
165638
  return result2;
165043
165639
  }
165044
165640
  const lastCompartment = compartments[compartments.length - 1];
@@ -165056,9 +165652,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165056
165652
  compartmentCount: compartments.length,
165057
165653
  skippedVisibleMessages: 0,
165058
165654
  factCount: facts.length,
165059
- memoryCount
165655
+ memoryCount,
165656
+ rebuiltFromDb: true
165060
165657
  };
165061
- injectionCache.set(sessionId, result2);
165658
+ injectionCache.set(sessionId, { kind: "populated", injection: result2 });
165062
165659
  return result2;
165063
165660
  }
165064
165661
  let skippedVisibleMessages = 0;
@@ -165067,17 +165664,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
165067
165664
  skippedVisibleMessages = cutoffIndex + 1;
165068
165665
  const remaining = messages.slice(cutoffIndex + 1);
165069
165666
  messages.splice(0, messages.length, ...remaining);
165667
+ } else {
165668
+ sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
165070
165669
  }
165071
165670
  const result = {
165072
165671
  block,
165073
165672
  compartmentEndMessage: lastEnd,
165074
- compartmentEndMessageId: lastEndMessageId,
165673
+ compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
165075
165674
  compartmentCount: compartments.length,
165076
165675
  skippedVisibleMessages,
165077
165676
  factCount: facts.length,
165078
- memoryCount
165677
+ memoryCount,
165678
+ rebuiltFromDb: true
165079
165679
  };
165080
- injectionCache.set(sessionId, result);
165680
+ injectionCache.set(sessionId, { kind: "populated", injection: result });
165081
165681
  return result;
165082
165682
  }
165083
165683
 
@@ -165299,8 +165899,9 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
165299
165899
  continue;
165300
165900
  const messageId = message.info.id;
165301
165901
  let textOrdinal = 0;
165302
- for (let partIndex = 0;partIndex < message.parts.length; partIndex += 1) {
165303
- const part = message.parts[partIndex];
165902
+ const parts = message.parts;
165903
+ for (let partIndex = 0;partIndex < parts.length; partIndex += 1) {
165904
+ const part = parts[partIndex];
165304
165905
  if (part === undefined)
165305
165906
  continue;
165306
165907
  if (part.kind === "text") {
@@ -166619,7 +167220,7 @@ function injectSessionHistoryIntoPi(db, sessionId, piMessages, isCacheBusting, p
166619
167220
  };
166620
167221
  }
166621
167222
  const boundaryId = prepared.compartmentEndMessageId;
166622
- 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;
166623
167224
  const historyBlock = `<session-history>
166624
167225
  ${prepared.block}
166625
167226
  </session-history>`;
@@ -167669,17 +168270,17 @@ function getReducedRecompTokenBudget(currentBudget) {
167669
168270
  }
167670
168271
 
167671
168272
  // ../plugin/src/hooks/magic-context/historian-state-file.ts
167672
- import { mkdirSync as mkdirSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
167673
- import { tmpdir as tmpdir2 } from "node:os";
167674
- 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";
167675
168276
  var HISTORIAN_STATE_INLINE_THRESHOLD = 30000;
167676
- var HISTORIAN_STATE_DIR = join10(tmpdir2(), "magic-context-historian");
167677
- function maybeWriteHistorianStateFile(sessionId, existingState) {
168277
+ function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
167678
168278
  if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
167679
168279
  return;
167680
168280
  try {
167681
- mkdirSync4(HISTORIAN_STATE_DIR, { recursive: true });
167682
- 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`);
167683
168284
  writeFileSync2(path5, existingState, "utf8");
167684
168285
  return path5;
167685
168286
  } catch {
@@ -167779,7 +168380,7 @@ async function runPiHistorian(deps) {
167779
168380
  const existingState = priorCompartments.length > 0 || priorFacts.length > 0 ? buildExistingStateXml(priorCompartments, priorFacts, memoryBlock) : memoryBlock ? `${memoryBlock}
167780
168381
 
167781
168382
  This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
167782
- stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
168383
+ stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, directory);
167783
168384
  if (stateFilePath) {
167784
168385
  sessionLog(sessionId, `pi-historian: existing state offloaded to file (${existingState.length} chars) → ${stateFilePath}`);
167785
168386
  }
@@ -169887,7 +170488,7 @@ init_logger();
169887
170488
  init_data_path();
169888
170489
  init_logger();
169889
170490
  await init_sqlite();
169890
- import { join as join11 } from "node:path";
170491
+ import { join as join10 } from "node:path";
169891
170492
  var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
169892
170493
  function randomBase62(length) {
169893
170494
  const chars = [];
@@ -169908,7 +170509,7 @@ function generatePartId(timestampMs, counter = 0n) {
169908
170509
  return generateId("prt", timestampMs, counter);
169909
170510
  }
169910
170511
  function getOpenCodeDbPath3() {
169911
- return join11(getDataDir(), "opencode", "opencode.db");
170512
+ return join10(getDataDir(), "opencode", "opencode.db");
169912
170513
  }
169913
170514
  var cachedWriteDb = null;
169914
170515
  var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
@@ -170399,7 +171000,8 @@ async function runCompressorPass2(args) {
170399
171000
  targetTokens,
170400
171001
  outputCount,
170401
171002
  outputDepth,
170402
- historianTimeoutMs
171003
+ historianTimeoutMs,
171004
+ fallbackModels
170403
171005
  } = args;
170404
171006
  const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount);
170405
171007
  let agentSessionId = null;
@@ -170421,7 +171023,11 @@ async function runCompressorPass2(args) {
170421
171023
  agent: HISTORIAN_AGENT2,
170422
171024
  parts: [{ type: "text", text: prompt, synthetic: true }]
170423
171025
  }
170424
- }, { timeoutMs: historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS });
171026
+ }, {
171027
+ timeoutMs: historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
171028
+ fallbackModels,
171029
+ callContext: "compressor"
171030
+ });
170425
171031
  const messagesResponse = await client.session.messages({
170426
171032
  path: { id: agentSessionId },
170427
171033
  query: { directory }
@@ -170461,11 +171067,13 @@ async function runCompressorPass2(args) {
170461
171067
  }
170462
171068
 
170463
171069
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
170464
- import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
170465
- import { tmpdir as tmpdir3 } from "node:os";
170466
- 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";
170467
171072
  init_magic_context();
170468
- var HISTORIAN_RESPONSE_DUMP_DIR = join12(tmpdir3(), "magic-context-historian");
171073
+ init_data_path();
171074
+ function historianResponseDumpDir(directory) {
171075
+ return getProjectMagicContextHistorianDir(directory);
171076
+ }
170469
171077
  var MAX_HISTORIAN_RETRIES = 2;
170470
171078
  async function runValidatedHistorianPass(args) {
170471
171079
  const firstRun = await runHistorianPrompt({
@@ -170559,7 +171167,8 @@ async function runHistorianPrompt(args) {
170559
171167
  timeoutMs,
170560
171168
  dumpLabel,
170561
171169
  modelOverride,
170562
- agentId = HISTORIAN_AGENT
171170
+ agentId = HISTORIAN_AGENT,
171171
+ fallbackModels
170563
171172
  } = args;
170564
171173
  let agentSessionId = null;
170565
171174
  try {
@@ -170586,7 +171195,11 @@ async function runHistorianPrompt(args) {
170586
171195
  ...modelOverride ? { model: modelOverride } : {},
170587
171196
  parts: [{ type: "text", text: prompt, synthetic: true }]
170588
171197
  }
170589
- }, { timeoutMs: timeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS });
171198
+ }, {
171199
+ timeoutMs: timeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
171200
+ fallbackModels: modelOverride ? undefined : fallbackModels,
171201
+ callContext: agentId === HISTORIAN_EDITOR_AGENT ? "historian:editor" : "historian"
171202
+ });
170590
171203
  sessionLog(parentSessionId, `historian: prompt completed (attempt ${retryIndex + 1}/${MAX_HISTORIAN_RETRIES + 1})`);
170591
171204
  break;
170592
171205
  } catch (error51) {
@@ -170612,7 +171225,7 @@ async function runHistorianPrompt(args) {
170612
171225
  if (!result) {
170613
171226
  return { ok: false, error: "Historian returned no assistant output." };
170614
171227
  }
170615
- const dumpPath = dumpHistorianResponse(parentSessionId, dumpLabel ?? "historian-response", result);
171228
+ const dumpPath = dumpHistorianResponse(parentSessionId, sessionDirectory, dumpLabel ?? "historian-response", result);
170616
171229
  return { ok: true, result, dumpPath };
170617
171230
  } catch (modelError) {
170618
171231
  const desc = describeError(modelError);
@@ -170704,12 +171317,13 @@ function cleanupHistorianDump(sessionId, dumpPath) {
170704
171317
  });
170705
171318
  }
170706
171319
  }
170707
- function dumpHistorianResponse(sessionId, label, text) {
171320
+ function dumpHistorianResponse(sessionId, directory, label, text) {
170708
171321
  try {
170709
- mkdirSync5(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
171322
+ const dumpDir = historianResponseDumpDir(directory);
171323
+ mkdirSync6(dumpDir, { recursive: true });
170710
171324
  const safeSessionId = sanitizeDumpName(sessionId);
170711
171325
  const safeLabel = sanitizeDumpName(label);
170712
- const dumpPath = join12(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
171326
+ const dumpPath = join11(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
170713
171327
  writeFileSync3(dumpPath, text, "utf8");
170714
171328
  sessionLog(sessionId, "compartment agent: historian response dumped", {
170715
171329
  label,
@@ -170934,9 +171548,18 @@ async function executePartialRecompInternal(deps, range) {
170934
171548
  }
170935
171549
  setRecompPartialRange(db, sessionId, null);
170936
171550
  clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
170937
- clearInjectionCache(sessionId);
170938
- deps.onInjectionCacheCleared?.(sessionId);
171551
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
171552
+ clearInjectionCache(sessionId);
171553
+ }
171554
+ deps.onCompartmentStatePublished?.(sessionId);
170939
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
+ }
170940
171563
  return { compartmentCount: merged.length, lastEndMessage: lastEnd };
170941
171564
  };
170942
171565
  const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
@@ -171042,6 +171665,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
171042
171665
  dumpLabelBase: `partial-recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
171043
171666
  timeoutMs: historianTimeoutMs,
171044
171667
  fallbackModelId: deps.fallbackModelId,
171668
+ fallbackModels: deps.fallbackModels,
171045
171669
  twoPass: deps.historianTwoPass,
171046
171670
  callbacks: {
171047
171671
  onRepairRetry: async (error51) => {
@@ -171150,8 +171774,10 @@ async function executeContextRecompInternal(deps) {
171150
171774
  if (!promoted2)
171151
171775
  return null;
171152
171776
  clearCompressionDepth(db, sessionId);
171153
- clearInjectionCache(sessionId);
171154
- deps.onInjectionCacheCleared?.(sessionId);
171777
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
171778
+ clearInjectionCache(sessionId);
171779
+ }
171780
+ deps.onCompartmentStatePublished?.(sessionId);
171155
171781
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
171156
171782
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
171157
171783
  }
@@ -171161,6 +171787,10 @@ async function executeContextRecompInternal(deps) {
171161
171787
  }
171162
171788
  if (deps.experimentalCompactionMarkers && lastCompartmentEnd2 > 0) {
171163
171789
  updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
171790
+ const stalePending = getPendingCompactionMarkerState(db, sessionId);
171791
+ if (stalePending) {
171792
+ clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
171793
+ }
171164
171794
  }
171165
171795
  return [
171166
171796
  `Persisted ${promoted2.compartments.length} compartment${promoted2.compartments.length === 1 ? "" : "s"} from ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
@@ -171226,7 +171856,7 @@ Nothing was written.`;
171226
171856
 
171227
171857
  This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
171228
171858
  cleanupHistorianStateFile(currentStateFilePath);
171229
- currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
171859
+ currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, sessionDirectory);
171230
171860
  const prompt = buildCompartmentAgentPrompt(existingState, `Messages ${chunk.startIndex}-${chunk.endIndex}:
171231
171861
 
171232
171862
  ${chunk.text}`, { stateFilePath: currentStateFilePath });
@@ -171244,6 +171874,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
171244
171874
  dumpLabelBase: `recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
171245
171875
  timeoutMs: historianTimeoutMs,
171246
171876
  fallbackModelId: deps.fallbackModelId,
171877
+ fallbackModels: deps.fallbackModels,
171247
171878
  twoPass: deps.historianTwoPass,
171248
171879
  callbacks: {
171249
171880
  onRepairRetry: async (error51) => {
@@ -171321,8 +171952,10 @@ Nothing was written.`;
171321
171952
  clearRecompStaging(db, sessionId);
171322
171953
  }
171323
171954
  clearCompressionDepth(db, sessionId);
171324
- clearInjectionCache(sessionId);
171325
- deps.onInjectionCacheCleared?.(sessionId);
171955
+ if (deps.preserveInjectionCacheUntilConsumed !== true) {
171956
+ clearInjectionCache(sessionId);
171957
+ }
171958
+ deps.onCompartmentStatePublished?.(sessionId);
171326
171959
  const finalCompartments = promoted?.compartments ?? candidateCompartments;
171327
171960
  const finalFacts = promoted?.facts ?? candidateFacts;
171328
171961
  if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
@@ -171341,6 +171974,7 @@ Nothing was written.`;
171341
171974
  directory: sessionDirectory,
171342
171975
  historyBudgetTokens: deps.historyBudgetTokens,
171343
171976
  historianTimeoutMs,
171977
+ fallbackModels: deps.fallbackModels,
171344
171978
  minCompartmentRatio: deps.compressorMinCompartmentRatio,
171345
171979
  maxMergeDepth: deps.compressorMaxMergeDepth
171346
171980
  });
@@ -171370,21 +172004,39 @@ Staging data preserved for resume on next attempt.`;
171370
172004
 
171371
172005
  // ../plugin/src/hooks/magic-context/compartment-runner.ts
171372
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
+ }
171373
172021
  async function executeContextRecomp(deps, options = {}) {
171374
172022
  const { sessionId } = deps;
171375
172023
  if (activeRuns.has(sessionId)) {
171376
172024
  return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
171377
172025
  }
171378
- const promise2 = options.range ? executePartialRecompInternal(deps, options.range) : executeContextRecompInternal(deps);
171379
- 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(() => {
171380
172029
  return;
171381
172030
  }).catch((err) => {
171382
172031
  sessionLog(sessionId, "compartment agent: recomp unhandled rejection:", err);
171383
- }));
172032
+ });
172033
+ activeRuns.set(sessionId, { promise: wrappedPromise, published: false });
171384
172034
  try {
171385
172035
  return await promise2;
171386
172036
  } finally {
171387
- activeRuns.delete(sessionId);
172037
+ if (activeRuns.get(sessionId)?.promise === wrappedPromise) {
172038
+ activeRuns.delete(sessionId);
172039
+ }
171388
172040
  }
171389
172041
  }
171390
172042
 
@@ -171877,7 +172529,7 @@ import {
171877
172529
  // package.json
171878
172530
  var package_default = {
171879
172531
  name: "@cortexkit/pi-magic-context",
171880
- version: "0.17.2",
172532
+ version: "0.19.0",
171881
172533
  type: "module",
171882
172534
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
171883
172535
  main: "dist/index.js",
@@ -172416,12 +173068,12 @@ function readHistorianState(db, sessionId, meta3) {
172416
173068
  init_magic_context();
172417
173069
  import { existsSync as existsSync8, readFileSync as readFileSync3 } from "node:fs";
172418
173070
  import { homedir as homedir4 } from "node:os";
172419
- import { join as join13 } from "node:path";
173071
+ import { join as join12 } from "node:path";
172420
173072
 
172421
173073
  // ../plugin/src/config/variable.ts
172422
173074
  import { existsSync as existsSync7, readFileSync as readFileSync2 } from "node:fs";
172423
173075
  import { homedir as homedir3 } from "node:os";
172424
- import { dirname as dirname3, isAbsolute, resolve } from "node:path";
173076
+ import { dirname as dirname4, isAbsolute, resolve } from "node:path";
172425
173077
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
172426
173078
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
172427
173079
  function substituteConfigVariables(input) {
@@ -172440,7 +173092,7 @@ function substituteConfigVariables(input) {
172440
173092
  if (fileMatches.length === 0) {
172441
173093
  return { text, warnings };
172442
173094
  }
172443
- const configDir = input.configPath ? dirname3(input.configPath) : process.cwd();
173095
+ const configDir = input.configPath ? dirname4(input.configPath) : process.cwd();
172444
173096
  let output = "";
172445
173097
  let cursor = 0;
172446
173098
  for (const match of fileMatches) {
@@ -172484,12 +173136,12 @@ function substituteConfigVariables(input) {
172484
173136
  var import_comment_json = __toESM(require_src2(), 1);
172485
173137
  var CONFIG_FILE_NAME = "magic-context";
172486
173138
  function getProjectConfigPaths(cwd) {
172487
- const basePath = join13(cwd, ".pi", CONFIG_FILE_NAME);
173139
+ const basePath = join12(cwd, ".pi", CONFIG_FILE_NAME);
172488
173140
  return [`${basePath}.jsonc`, `${basePath}.json`];
172489
173141
  }
172490
173142
  function getUserConfigPaths() {
172491
173143
  const home = process.env.HOME ?? homedir4();
172492
- const basePath = join13(home, ".pi", "agent", CONFIG_FILE_NAME);
173144
+ const basePath = join12(home, ".pi", "agent", CONFIG_FILE_NAME);
172493
173145
  return [`${basePath}.jsonc`, `${basePath}.json`];
172494
173146
  }
172495
173147
  function resolveFirstExisting(paths) {
@@ -172646,7 +173298,7 @@ function stripTagPrefixFromAssistantMessage(message) {
172646
173298
  // src/system-prompt.ts
172647
173299
  import { createHash as createHash4 } from "node:crypto";
172648
173300
  import { existsSync as existsSync9, readFileSync as readFileSync4, realpathSync } from "node:fs";
172649
- import { join as join14, resolve as resolve2, sep } from "node:path";
173301
+ import { join as join13, resolve as resolve2, sep } from "node:path";
172650
173302
 
172651
173303
  // ../plugin/src/agents/magic-context-prompt.ts
172652
173304
  function getToolHistoryGuidance(dropToolStructure) {
@@ -172748,7 +173400,7 @@ var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
172748
173400
  function readProjectDocs(directory) {
172749
173401
  const sections = [];
172750
173402
  for (const filename of DOC_FILES) {
172751
- const filePath = join14(directory, filename);
173403
+ const filePath = join13(directory, filename);
172752
173404
  try {
172753
173405
  if (existsSync9(filePath)) {
172754
173406
  const content = readFileSync4(filePath, "utf-8").trim();
@@ -177979,7 +178631,7 @@ function resolveDreamerFromConfig(config2) {
177979
178631
  }
177980
178632
  async function src_default2(pi) {
177981
178633
  const storageDir = getMagicContextStorageDir();
177982
- const dbPath = join15(storageDir, "context.db");
178634
+ const dbPath = join14(storageDir, "context.db");
177983
178635
  let db;
177984
178636
  try {
177985
178637
  db = openDatabase();