@cortexkit/pi-magic-context 0.22.0 → 0.22.2

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
@@ -86,9 +86,6 @@ function getMagicContextStorageDir() {
86
86
  function getLegacyOpenCodeMagicContextStorageDir() {
87
87
  return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
88
88
  }
89
- function getCacheDir() {
90
- return process.env.XDG_CACHE_HOME ?? path2.join(os.homedir(), ".cache");
91
- }
92
89
  var init_data_path = () => {};
93
90
 
94
91
  // ../plugin/src/shared/logger.ts
@@ -142698,7 +142695,7 @@ var databases = new Map;
142698
142695
  var persistenceByDatabase = new WeakMap;
142699
142696
  var persistenceErrorByDatabase = new WeakMap;
142700
142697
  var lastSchemaFenceRejection = null;
142701
- var LATEST_SUPPORTED_VERSION = 27;
142698
+ var LATEST_SUPPORTED_VERSION = 29;
142702
142699
  function resolveDatabasePath(dbPathOverride) {
142703
142700
  if (dbPathOverride) {
142704
142701
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -143018,6 +143015,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143018
143015
  updated_at INTEGER NOT NULL DEFAULT 0
143019
143016
  );
143020
143017
 
143018
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
143019
+ project_path TEXT PRIMARY KEY,
143020
+ lease_holder TEXT,
143021
+ lease_expires_at INTEGER,
143022
+ last_swept_at INTEGER
143023
+ );
143024
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
143025
+ ON git_sweep_coordinator(lease_expires_at);
143026
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
143027
+ ON git_sweep_coordinator(last_swept_at);
143028
+
143021
143029
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
143022
143030
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143023
143031
  session_id TEXT NOT NULL,
@@ -144240,6 +144248,38 @@ var MIGRATIONS = [
144240
144248
  ON tags(session_id, entry_fingerprint)
144241
144249
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
144242
144250
  }
144251
+ },
144252
+ {
144253
+ version: 28,
144254
+ description: "Add git commit sweep coordinator lease/cooldown table",
144255
+ up: (db) => {
144256
+ db.exec(`
144257
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
144258
+ project_path TEXT PRIMARY KEY,
144259
+ lease_holder TEXT,
144260
+ lease_expires_at INTEGER,
144261
+ last_swept_at INTEGER
144262
+ );
144263
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
144264
+ ON git_sweep_coordinator(lease_expires_at);
144265
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
144266
+ ON git_sweep_coordinator(last_swept_at);
144267
+ `);
144268
+ }
144269
+ },
144270
+ {
144271
+ version: 29,
144272
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
144273
+ up: (db) => {
144274
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
144275
+ if (!notesExists) {
144276
+ return;
144277
+ }
144278
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
144279
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
144280
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
144281
+ }
144282
+ }
144243
144283
  }
144244
144284
  ];
144245
144285
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -145127,7 +145167,8 @@ function toNote(row) {
145127
145167
  updatedAt: row.updated_at,
145128
145168
  lastCheckedAt: toNullableNumber(row.last_checked_at),
145129
145169
  readyAt: toNullableNumber(row.ready_at),
145130
- readyReason: toNullableString(row.ready_reason)
145170
+ readyReason: toNullableString(row.ready_reason),
145171
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
145131
145172
  };
145132
145173
  }
145133
145174
  function getNoteById(db, noteId) {
@@ -145179,7 +145220,7 @@ function getNotes(db, options = {}) {
145179
145220
  }
145180
145221
  function addNote(db, type, options) {
145181
145222
  const now = Date.now();
145182
- const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness) VALUES ('session', 'active', ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness()) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness());
145223
+ const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness, anchor_ordinal) VALUES ('session', 'active', ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness(), options.anchorOrdinal ?? null) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness, anchor_ordinal) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness(), options.anchorOrdinal ?? null);
145183
145224
  if (!isNoteRow(result)) {
145184
145225
  throw new Error("[notes] failed to insert note");
145185
145226
  }
@@ -145949,219 +145990,56 @@ function getAgentFallbackModels(agent) {
145949
145990
 
145950
145991
  // ../plugin/src/shared/models-dev-cache.ts
145951
145992
  init_data_path();
145952
- import { createHash as createHash5 } from "node:crypto";
145953
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
145954
- import { homedir as homedir2, platform } from "node:os";
145993
+ init_logger();
145994
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, writeFileSync } from "node:fs";
145955
145995
  import { join as join6 } from "node:path";
145956
-
145957
- // ../plugin/src/shared/jsonc-parser.ts
145958
- function stripJsonComments(content) {
145959
- let result = "";
145960
- let inString = false;
145961
- let escaped = false;
145962
- let inLineComment = false;
145963
- let inBlockComment = false;
145964
- for (let index = 0;index < content.length; index += 1) {
145965
- const char = content[index];
145966
- const next = content[index + 1];
145967
- if (inLineComment) {
145968
- if (char === `
145969
- `) {
145970
- inLineComment = false;
145971
- result += char;
145972
- }
145973
- continue;
145974
- }
145975
- if (inBlockComment) {
145976
- if (char === "*" && next === "/") {
145977
- inBlockComment = false;
145978
- index += 1;
145979
- }
145980
- continue;
145981
- }
145982
- if (inString) {
145983
- result += char;
145984
- if (escaped) {
145985
- escaped = false;
145986
- } else if (char === "\\") {
145987
- escaped = true;
145988
- } else if (char === '"') {
145989
- inString = false;
145990
- }
145991
- continue;
145992
- }
145993
- if (char === '"') {
145994
- inString = true;
145995
- result += char;
145996
- continue;
145997
- }
145998
- if (char === "/" && next === "/") {
145999
- inLineComment = true;
146000
- index += 1;
146001
- continue;
146002
- }
146003
- if (char === "/" && next === "*") {
146004
- inBlockComment = true;
146005
- index += 1;
146006
- continue;
146007
- }
146008
- result += char;
146009
- }
146010
- return result;
145996
+ var MIN_SANE_LIMIT = 20000;
145997
+ var MAX_SANE_LIMIT = 3000000;
145998
+ function isSaneLimit(limit) {
145999
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
146011
146000
  }
146012
- function stripTrailingCommas(content) {
146013
- let result = "";
146014
- let inString = false;
146015
- let escaped = false;
146016
- for (let index = 0;index < content.length; index += 1) {
146017
- const char = content[index];
146018
- if (inString) {
146019
- result += char;
146020
- if (escaped) {
146021
- escaped = false;
146022
- } else if (char === "\\") {
146023
- escaped = true;
146024
- } else if (char === '"') {
146025
- inString = false;
146026
- }
146027
- continue;
146028
- }
146029
- if (char === '"') {
146030
- inString = true;
146031
- result += char;
146032
- continue;
146033
- }
146034
- if (char === ",") {
146035
- let lookahead = index + 1;
146036
- while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
146037
- lookahead += 1;
146038
- }
146039
- const next = content[lookahead];
146040
- if (next === "}" || next === "]") {
146041
- continue;
146042
- }
146043
- }
146044
- result += char;
146045
- }
146046
- return result;
146047
- }
146048
- function parseJsonc(content) {
146049
- const normalized = stripTrailingCommas(stripJsonComments(content));
146050
- return JSON.parse(normalized);
146051
- }
146052
-
146053
- // ../plugin/src/shared/models-dev-cache.ts
146054
- init_logger();
146055
- var RELOAD_INTERVAL_MS = 5 * 60 * 1000;
146056
146001
  var apiCache = null;
146057
- var apiLoadedAt = 0;
146058
- var fileCache = null;
146059
- var fileLastAttempt = 0;
146060
- function hashFast(input) {
146061
- return createHash5("sha1").update(input).digest("hex");
146062
- }
146063
- function getModelsJsonPath() {
146064
- const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
146065
- if (explicit)
146066
- return explicit;
146067
- const cacheBase = getCacheDir();
146068
- const source = process.env.OPENCODE_MODELS_URL?.trim();
146069
- const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
146070
- return join6(cacheBase, "opencode", filename);
146071
- }
146072
- function getOpencodeConfigPath() {
146073
- const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
146074
- const configDir = envDir ? envDir : platform() === "win32" ? join6(homedir2(), ".config", "opencode") : join6(process.env.XDG_CONFIG_HOME || join6(homedir2(), ".config"), "opencode");
146075
- const jsonc = join6(configDir, "opencode.jsonc");
146076
- if (existsSync5(jsonc))
146077
- return jsonc;
146078
- const json = join6(configDir, "opencode.json");
146079
- if (existsSync5(json))
146080
- return json;
146081
- return null;
146002
+ var persistSeedLoaded = false;
146003
+ function persistFilePath() {
146004
+ return join6(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
146082
146005
  }
146083
- function resolveLimit(limit) {
146084
- if (!limit)
146006
+ function loadPersistedApiCacheOnce() {
146007
+ if (persistSeedLoaded || apiCache !== null)
146085
146008
  return;
146086
- if (typeof limit.input === "number" && limit.input > 0)
146087
- return limit.input;
146088
- if (typeof limit.context === "number" && limit.context > 0)
146089
- return limit.context;
146090
- return;
146091
- }
146092
- function setCachedModelMetadata(cache, key, model) {
146093
- const limit = resolveLimit(model?.limit);
146094
- if (limit === undefined) {
146095
- return;
146096
- }
146097
- const value = { limit };
146098
- cache.set(key, value);
146099
- const modes = model?.experimental?.modes;
146100
- if (modes && typeof modes === "object") {
146101
- for (const mode of Object.keys(modes)) {
146102
- cache.set(`${key}-${mode}`, value);
146103
- }
146104
- }
146105
- }
146106
- function loadModelsDevMetadataFromFile() {
146107
- const metadata = new Map;
146108
- const modelsJsonPath = getModelsJsonPath();
146109
- let fileFound = false;
146009
+ persistSeedLoaded = true;
146110
146010
  try {
146111
- if (existsSync5(modelsJsonPath)) {
146112
- fileFound = true;
146113
- const raw = readFileSync3(modelsJsonPath, "utf-8");
146114
- const data = JSON.parse(raw);
146115
- for (const [providerId, provider] of Object.entries(data)) {
146116
- if (!provider?.models || typeof provider.models !== "object")
146117
- continue;
146118
- for (const [modelId, model] of Object.entries(provider.models)) {
146119
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
146120
- }
146121
- }
146011
+ const raw = readFileSync3(persistFilePath(), "utf-8");
146012
+ const obj = JSON.parse(raw);
146013
+ const map = new Map;
146014
+ for (const [key, limit] of Object.entries(obj)) {
146015
+ if (isSaneLimit(limit))
146016
+ map.set(key, { limit });
146122
146017
  }
146123
- } catch (error) {
146124
- sessionLog("global", `models-dev-cache: failed to read models.json at ${modelsJsonPath}:`, error instanceof Error ? error.message : String(error));
146125
- }
146126
- try {
146127
- const configPath = getOpencodeConfigPath();
146128
- if (configPath && existsSync5(configPath)) {
146129
- const config = parseJsonc(readFileSync3(configPath, "utf-8"));
146130
- if (config.provider && typeof config.provider === "object") {
146131
- for (const [providerId, provider] of Object.entries(config.provider)) {
146132
- if (!provider?.models || typeof provider.models !== "object")
146133
- continue;
146134
- for (const [modelId, model] of Object.entries(provider.models)) {
146135
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
146136
- }
146137
- }
146138
- }
146018
+ if (map.size > 0) {
146019
+ apiCache = map;
146020
+ sessionLog("global", `models-dev-cache: seeded ${map.size} entries from persisted cache (cold start)`);
146139
146021
  }
146140
- } catch (error) {
146141
- sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error instanceof Error ? error.message : String(error));
146142
- }
146143
- sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
146144
- return metadata;
146022
+ } catch {}
146145
146023
  }
146146
- function getModelsDevContextLimit(providerID, modelID) {
146147
- const key = `${providerID}/${modelID}`;
146148
- if (apiCache) {
146149
- const fromApi = apiCache.get(key)?.limit;
146150
- if (typeof fromApi === "number")
146151
- return fromApi;
146152
- }
146153
- const now = Date.now();
146154
- if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
146155
- fileLastAttempt = now;
146156
- fileCache = loadModelsDevMetadataFromFile();
146157
- }
146158
- return fileCache.get(key)?.limit;
146024
+ function getSdkContextLimit(providerID, modelID) {
146025
+ loadPersistedApiCacheOnce();
146026
+ const fromApi = lookupLimitWithTagFallback(apiCache, providerID, modelID);
146027
+ return isSaneLimit(fromApi) ? fromApi : undefined;
146159
146028
  }
146160
- function clearModelsDevCache() {
146161
- apiCache = null;
146162
- apiLoadedAt = 0;
146163
- fileCache = null;
146164
- fileLastAttempt = 0;
146029
+ function lookupLimitWithTagFallback(cache, providerID, modelID) {
146030
+ if (!cache)
146031
+ return;
146032
+ const exact = cache.get(`${providerID}/${modelID}`)?.limit;
146033
+ if (typeof exact === "number")
146034
+ return exact;
146035
+ const colonIdx = modelID.lastIndexOf(":");
146036
+ if (colonIdx > 0) {
146037
+ const baseModel = modelID.slice(0, colonIdx);
146038
+ const fallback = cache.get(`${providerID}/${baseModel}`)?.limit;
146039
+ if (typeof fallback === "number")
146040
+ return fallback;
146041
+ }
146042
+ return;
146165
146043
  }
146166
146044
 
146167
146045
  // ../plugin/src/hooks/magic-context/derive-budgets.ts
@@ -146193,7 +146071,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
146193
146071
  const [providerID, ...rest] = historianModelOverride.split("/");
146194
146072
  const modelID = rest.join("/");
146195
146073
  if (providerID && modelID) {
146196
- const limit = getModelsDevContextLimit(providerID, modelID);
146074
+ const limit = getSdkContextLimit(providerID, modelID);
146197
146075
  if (typeof limit === "number" && limit > 0)
146198
146076
  return limit;
146199
146077
  }
@@ -146212,7 +146090,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
146212
146090
  const modelID = rest.join("/");
146213
146091
  if (!providerID || !modelID)
146214
146092
  continue;
146215
- const limit = getModelsDevContextLimit(providerID, modelID);
146093
+ const limit = getSdkContextLimit(providerID, modelID);
146216
146094
  if (typeof limit !== "number" || limit <= 0)
146217
146095
  continue;
146218
146096
  if (minLimit === undefined || limit < minLimit)
@@ -146225,37 +146103,6 @@ function resolveHistorianContextLimit(historianModelOverride) {
146225
146103
  init_logger();
146226
146104
  var DEFAULT_CONTEXT_LIMIT = 128000;
146227
146105
  var MAX_EXECUTE_THRESHOLD = 80;
146228
- function resolveContextLimit(providerID, modelID, ctx) {
146229
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
146230
- const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
146231
- if (ctx?.db && ctx.sessionID) {
146232
- try {
146233
- const overflow = getOverflowState(ctx.db, ctx.sessionID);
146234
- if (overflow.detectedContextLimit > 0 && overflow.detectedContextLimit < baseline) {
146235
- return overflow.detectedContextLimit;
146236
- }
146237
- } catch {}
146238
- }
146239
- return baseline;
146240
- }
146241
- function resolveTrustedContextLimit(providerID, modelID, ctx) {
146242
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
146243
- let detected;
146244
- if (ctx?.db && ctx.sessionID) {
146245
- try {
146246
- const overflow = getOverflowState(ctx.db, ctx.sessionID);
146247
- if (overflow.detectedContextLimit > 0) {
146248
- detected = overflow.detectedContextLimit;
146249
- }
146250
- } catch {}
146251
- }
146252
- if (typeof fromModelsDev === "number" && fromModelsDev > 0) {
146253
- if (detected !== undefined && detected < fromModelsDev)
146254
- return detected;
146255
- return fromModelsDev;
146256
- }
146257
- return detected;
146258
- }
146259
146106
  function resolveCacheTtl(cacheTtl, modelKey) {
146260
146107
  if (typeof cacheTtl === "string") {
146261
146108
  return cacheTtl;
@@ -146345,8 +146192,21 @@ function resolveExecuteThresholdDetail(config, modelKey, fallback, options) {
146345
146192
  if (!Number.isFinite(resolved) || resolved < 0) {
146346
146193
  resolved = fallback;
146347
146194
  }
146195
+ const cappedPercentage = Math.min(resolved, MAX_EXECUTE_THRESHOLD);
146196
+ if (cappedPercentage < resolved) {
146197
+ const dedupeKey = `pct|${options?.sessionId ?? "__global__"}|${modelKey ?? "__default__"}|${resolved}`;
146198
+ if (!clampWarnSeen.has(dedupeKey)) {
146199
+ clampWarnSeen.add(dedupeKey);
146200
+ const msg = `execute_threshold clamped ${resolved}% → ${MAX_EXECUTE_THRESHOLD}% for ${modelKey ?? "default"} (capped for cache safety; a large step can overflow before compaction, and 80% stays below the 85%/95% emergency bands)`;
146201
+ if (options?.sessionId) {
146202
+ sessionLog(options.sessionId, `WARN: ${msg}`);
146203
+ } else {
146204
+ log(`[magic-context] WARN: ${msg}`);
146205
+ }
146206
+ }
146207
+ }
146348
146208
  return {
146349
- percentage: Math.min(resolved, MAX_EXECUTE_THRESHOLD),
146209
+ percentage: cappedPercentage,
146350
146210
  mode: "percentage",
146351
146211
  matchedKey
146352
146212
  };
@@ -146463,7 +146323,7 @@ function clearNoteNudgeTriggerOnly(db, sessionId) {
146463
146323
  }
146464
146324
 
146465
146325
  // ../plugin/src/hooks/magic-context/todo-view.ts
146466
- import { createHash as createHash6 } from "node:crypto";
146326
+ import { createHash as createHash5 } from "node:crypto";
146467
146327
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
146468
146328
  var TITLE_DONE_STATUSES = new Set(["completed"]);
146469
146329
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -146508,7 +146368,7 @@ function buildSyntheticTodoPart(stateJson) {
146508
146368
  };
146509
146369
  }
146510
146370
  function computeSyntheticCallId(stateJson) {
146511
- const hash = createHash6("sha256").update(stateJson).digest("hex").slice(0, 16);
146371
+ const hash = createHash5("sha256").update(stateJson).digest("hex").slice(0, 16);
146512
146372
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash}`;
146513
146373
  }
146514
146374
  function parseTodoState(stateJson) {
@@ -146680,7 +146540,12 @@ function markAnnouncementSeen(version) {
146680
146540
  function shouldShowAnnouncement() {
146681
146541
  if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0)
146682
146542
  return false;
146683
- return readLastAnnouncedVersion() !== ANNOUNCEMENT_VERSION;
146543
+ const lastVersion = readLastAnnouncedVersion();
146544
+ if (!lastVersion) {
146545
+ markAnnouncementSeen(ANNOUNCEMENT_VERSION);
146546
+ return false;
146547
+ }
146548
+ return lastVersion !== ANNOUNCEMENT_VERSION;
146684
146549
  }
146685
146550
 
146686
146551
  // src/index.ts
@@ -146757,7 +146622,7 @@ init_logger();
146757
146622
 
146758
146623
  // src/subagent-runner.ts
146759
146624
  import * as childProcess from "node:child_process";
146760
- import { existsSync as existsSync7 } from "node:fs";
146625
+ import { existsSync as existsSync6 } from "node:fs";
146761
146626
  import { createRequire as createRequire2 } from "node:module";
146762
146627
  import { dirname as dirname3, join as join8, resolve as resolvePath } from "node:path";
146763
146628
  import { createInterface } from "node:readline";
@@ -146861,7 +146726,7 @@ function resolveBundledPiCli() {
146861
146726
  const require_ = createRequire2(import.meta.url);
146862
146727
  const pkgJson = require_.resolve("@earendil-works/pi-coding-agent/package.json");
146863
146728
  const cliPath = join8(dirname3(pkgJson), "dist/cli.js");
146864
- if (existsSync7(cliPath))
146729
+ if (existsSync6(cliPath))
146865
146730
  return cliPath;
146866
146731
  return null;
146867
146732
  } catch {
@@ -146872,7 +146737,7 @@ function resolveSubagentEntryPath() {
146872
146737
  try {
146873
146738
  const here = dirname3(fileURLToPath(import.meta.url));
146874
146739
  const candidate = resolvePath(here, "subagent-entry.js");
146875
- if (existsSync7(candidate))
146740
+ if (existsSync6(candidate))
146876
146741
  return candidate;
146877
146742
  return;
146878
146743
  } catch {
@@ -147632,7 +147497,7 @@ function clearStaleEntries(db, maxAgeMs, projectIdentity) {
147632
147497
  // src/commands/ctx-dream.ts
147633
147498
  init_logger();
147634
147499
  // ../plugin/src/features/magic-context/dreamer/runner.ts
147635
- import { existsSync as existsSync9 } from "node:fs";
147500
+ import { existsSync as existsSync8 } from "node:fs";
147636
147501
  import { join as join11 } from "node:path";
147637
147502
 
147638
147503
  // ../plugin/src/shared/index.ts
@@ -147886,12 +147751,12 @@ init_logger();
147886
147751
 
147887
147752
  // ../plugin/src/features/magic-context/key-files/aft-availability.ts
147888
147753
  var import_comment_json = __toESM(require_src2(), 1);
147889
- import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
147890
- import { homedir as homedir3 } from "node:os";
147754
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
147755
+ import { homedir as homedir2 } from "node:os";
147891
147756
  import { join as join9 } from "node:path";
147892
147757
  var overrideAvailability = null;
147893
147758
  function parseConfig(path7) {
147894
- if (!existsSync8(path7))
147759
+ if (!existsSync7(path7))
147895
147760
  return null;
147896
147761
  return import_comment_json.parse(readFileSync5(path7, "utf-8"));
147897
147762
  }
@@ -147913,7 +147778,7 @@ function hasAftAtKeys(value, keys) {
147913
147778
  return false;
147914
147779
  }
147915
147780
  function getAftAvailability() {
147916
- const home = process.env.HOME || homedir3();
147781
+ const home = process.env.HOME || homedir2();
147917
147782
  const opencodePaths = [
147918
147783
  join9(home, ".config", "opencode", "opencode.jsonc"),
147919
147784
  join9(home, ".config", "opencode", "opencode.json")
@@ -148557,13 +148422,13 @@ function invalidateMemory(projectPath, memoryId) {
148557
148422
  }
148558
148423
 
148559
148424
  // ../plugin/src/features/magic-context/memory/normalize-hash.ts
148560
- import { createHash as createHash7 } from "node:crypto";
148425
+ import { createHash as createHash6 } from "node:crypto";
148561
148426
  function normalizeMemoryContent(content) {
148562
148427
  return content.toLowerCase().replace(/\s+/g, " ").trim();
148563
148428
  }
148564
148429
  function computeNormalizedHash(content) {
148565
148430
  const normalized = normalizeMemoryContent(content);
148566
- return createHash7("md5").update(normalized).digest("hex");
148431
+ return createHash6("md5").update(normalized).digest("hex");
148567
148432
  }
148568
148433
 
148569
148434
  // ../plugin/src/features/magic-context/memory/storage-memory.ts
@@ -149714,7 +149579,7 @@ function getOpenCodeDbPath2() {
149714
149579
  }
149715
149580
  function openOpenCodeDb() {
149716
149581
  const dbPath = getOpenCodeDbPath2();
149717
- if (!existsSync9(dbPath)) {
149582
+ if (!existsSync8(dbPath)) {
149718
149583
  log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
149719
149584
  return null;
149720
149585
  }
@@ -149864,8 +149729,8 @@ async function runDream(args) {
149864
149729
  try {
149865
149730
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
149866
149731
  const existingDocs = taskName === "maintain-docs" ? {
149867
- architecture: existsSync9(join11(docsDir, "ARCHITECTURE.md")),
149868
- structure: existsSync9(join11(docsDir, "STRUCTURE.md"))
149732
+ architecture: existsSync8(join11(docsDir, "ARCHITECTURE.md")),
149733
+ structure: existsSync8(join11(docsDir, "STRUCTURE.md"))
149869
149734
  } : undefined;
149870
149735
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
149871
149736
  id: um.id,
@@ -164841,25 +164706,26 @@ var PermissionSchema = exports_external.object({
164841
164706
  external_directory: PermissionValueSchema.optional()
164842
164707
  }).optional();
164843
164708
  var AgentOverrideConfigSchema = exports_external.object({
164844
- model: exports_external.string().optional(),
164845
- temperature: exports_external.number().min(0).max(2).optional(),
164846
- top_p: exports_external.number().min(0).max(1).optional(),
164847
- prompt: exports_external.string().optional(),
164848
- tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
164849
- disable: exports_external.boolean().optional(),
164850
- description: exports_external.string().optional(),
164851
- mode: exports_external.enum(["subagent", "primary", "all"]).optional(),
164852
- color: exports_external.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
164853
- maxSteps: exports_external.number().optional(),
164854
- permission: PermissionSchema,
164855
- maxTokens: exports_external.number().optional(),
164856
- variant: exports_external.string().optional(),
164857
- fallback_models: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional()
164709
+ model: exports_external.string().optional().describe("Primary model ID (e.g. 'claude-sonnet-4-6')"),
164710
+ temperature: exports_external.number().min(0).max(2).optional().describe("Sampling temperature (0-2)"),
164711
+ top_p: exports_external.number().min(0).max(1).optional().describe("Nucleus sampling top_p (0-1)"),
164712
+ prompt: exports_external.string().optional().describe("Additional system prompt text"),
164713
+ tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional().describe("Tool enable/disable overrides"),
164714
+ disable: exports_external.boolean().optional().describe("Disable this agent"),
164715
+ description: exports_external.string().optional().describe("Agent description"),
164716
+ mode: exports_external.enum(["subagent", "primary", "all"]).optional().describe("Agent mode (subagent, primary, or all)"),
164717
+ color: exports_external.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for the agent (e.g. '#a1b2c3')"),
164718
+ maxSteps: exports_external.number().optional().describe("Maximum tool-call steps per invocation"),
164719
+ permission: PermissionSchema.describe("Per-tool permission overrides"),
164720
+ maxTokens: exports_external.number().optional().describe("Maximum output tokens"),
164721
+ variant: exports_external.string().optional().describe("OpenCode reasoning variant (e.g. for extended thinking)"),
164722
+ fallback_models: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional().describe("Fallback model IDs if primary is unavailable")
164858
164723
  });
164859
164724
 
164860
164725
  // ../plugin/src/config/schema/magic-context.ts
164861
164726
  var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4;
164862
164727
  var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
164728
+ var EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cache safety: a single large agent step can overflow the context window before Magic Context can compact between turns, forcing OpenCode's native compaction (hard to recover from). 80% also leaves headroom below the 85%/95% emergency bands. Use a value between 20 and 80.";
164863
164729
  var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
164864
164730
  var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
164865
164731
  var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
@@ -164879,36 +164745,38 @@ var DEFAULT_DREAMER_TASKS = [
164879
164745
  ];
164880
164746
  var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional();
164881
164747
  var DreamerConfigSchema = AgentOverrideConfigSchema.merge(exports_external.object({
164882
- schedule: exports_external.string().default("02:00-06:00"),
164883
- max_runtime_minutes: exports_external.number().min(10).default(120),
164884
- tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS),
164885
- task_timeout_minutes: exports_external.number().min(5).default(20),
164886
- inject_docs: exports_external.boolean().default(true),
164748
+ schedule: exports_external.string().default("02:00-06:00").describe("Scheduled window for overnight dreaming (e.g. '02:00-06:00')"),
164749
+ max_runtime_minutes: exports_external.number().min(10).default(120).describe("Maximum runtime per dream session in minutes"),
164750
+ tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS).describe("Tasks to run during dreaming, in order"),
164751
+ task_timeout_minutes: exports_external.number().min(5).default(20).describe("Minutes allocated per task before moving to next"),
164752
+ inject_docs: exports_external.boolean().default(true).describe("Inject ARCHITECTURE.md and STRUCTURE.md into system prompt"),
164887
164753
  user_memories: exports_external.object({
164888
- enabled: exports_external.boolean().default(true),
164889
- promotion_threshold: exports_external.number().min(2).max(20).default(3)
164890
- }).default({ enabled: true, promotion_threshold: 3 }),
164754
+ enabled: exports_external.boolean().default(true).describe("Enable user memory extraction and promotion (default: true)"),
164755
+ promotion_threshold: exports_external.number().min(2).max(20).default(3).describe("Minimum candidate observations before dreamer considers promotion (default: 3)")
164756
+ }).default({ enabled: true, promotion_threshold: 3 }).describe("User memory pipeline: historian extracts behavior observations from each compartment run; dreamer reviews recurring patterns and promotes them to stable user memories injected into all sessions as <user-profile>. Requires dreamer to not be disabled for promotion to actually happen. Graduated from experimental in v0.14. Default: enabled."),
164891
164757
  pin_key_files: exports_external.object({
164892
- enabled: exports_external.boolean().default(false),
164893
- token_budget: exports_external.number().min(2000).max(30000).default(1e4),
164894
- min_reads: exports_external.number().min(2).max(20).default(4)
164895
- }).default({ enabled: false, token_budget: 1e4, min_reads: 4 }),
164896
- thinking_level: PiThinkingLevelSchema
164758
+ enabled: exports_external.boolean().default(false).describe("Enable key file pinning (default: false)"),
164759
+ token_budget: exports_external.number().min(2000).max(30000).default(1e4).describe("Total token budget for all pinned key files (min: 2000, max: 30000, default: 10000)"),
164760
+ min_reads: exports_external.number().min(2).max(20).default(4).describe("Minimum full-read count before a file is considered for pinning (min: 2, default: 4)")
164761
+ }).default({ enabled: false, token_budget: 1e4, min_reads: 4 }).describe("Pin frequently-read key files into the system prompt so the agent doesn't need to re-read them after context drops. Dreamer identifies key files per session based on read patterns. Requires dreamer to not be disabled for selection to happen. Graduated from experimental in v0.14. Default: disabled."),
164762
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level for dreamer subagent invocations. See historian.thinking_level.")
164897
164763
  }));
164898
164764
  var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
164899
- timeout_ms: exports_external.number().default(30000),
164900
- system_prompt: exports_external.string().optional(),
164901
- thinking_level: PiThinkingLevelSchema
164765
+ timeout_ms: exports_external.number().default(30000).describe("Timeout for sidekick calls in milliseconds"),
164766
+ system_prompt: exports_external.string().optional().describe("Custom system prompt for sidekick"),
164767
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level for sidekick subagent invocations. See historian.thinking_level.")
164902
164768
  }).optional();
164903
164769
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
164904
- two_pass: exports_external.boolean().default(false),
164905
- thinking_level: PiThinkingLevelSchema
164770
+ two_pass: exports_external.boolean().default(false).describe("Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)"),
164771
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh")
164906
164772
  }).optional();
164907
164773
  var BaseEmbeddingConfigSchema = exports_external.object({
164908
- provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local"),
164909
- model: exports_external.string().optional(),
164910
- endpoint: exports_external.string().optional(),
164911
- api_key: exports_external.string().optional()
164774
+ provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'off' disables embeddings."),
164775
+ model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
164776
+ endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
164777
+ api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
164778
+ input_type: exports_external.string().optional().describe("Optional input_type sent in the embedding request body. Required by some openai-compatible providers (e.g. NVIDIA NIM expects 'query' or 'passage'). Omitted from the request when unset."),
164779
+ truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset.")
164912
164780
  }).superRefine((data, ctx) => {
164913
164781
  if (data.provider === "openai-compatible" && !data.endpoint?.trim()) {
164914
164782
  ctx.addIssue({
@@ -164934,76 +164802,80 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
164934
164802
  }
164935
164803
  if (data.provider === "openai-compatible") {
164936
164804
  const apiKey = data.api_key?.trim();
164805
+ const inputType = data.input_type?.trim();
164806
+ const truncate = data.truncate?.trim();
164937
164807
  return {
164938
164808
  provider: "openai-compatible",
164939
164809
  model: data.model?.trim() ?? "",
164940
164810
  endpoint: data.endpoint?.trim() ?? "",
164941
- ...apiKey ? { api_key: apiKey } : {}
164811
+ ...apiKey ? { api_key: apiKey } : {},
164812
+ ...inputType ? { input_type: inputType } : {},
164813
+ ...truncate ? { truncate } : {}
164942
164814
  };
164943
164815
  }
164944
164816
  return { provider: "off" };
164945
164817
  });
164946
164818
  var MagicContextConfigSchema = exports_external.object({
164947
- enabled: exports_external.boolean().default(true),
164948
- auto_update: exports_external.boolean().optional(),
164949
- ctx_reduce_enabled: exports_external.boolean().default(true),
164950
- historian: HistorianConfigSchema,
164951
- dreamer: DreamerConfigSchema.optional(),
164952
- cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m"),
164953
- nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS),
164819
+ enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
164820
+ auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
164821
+ ctx_reduce_enabled: exports_external.boolean().default(true).describe("When false, ctx_reduce tool is hidden, all nudges disabled, and prompt guidance about ctx_reduce stripped. Heuristic cleanup, compartments, memory, and other features still work. (default: true)"),
164822
+ historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
164823
+ dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
164824
+ cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m") or per-model object ({ default: "5m", "model-id": "10m" })'),
164825
+ nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS).describe("Minimum token growth between low-priority rolling nudges (default: DEFAULT_NUDGE_INTERVAL_TOKENS)"),
164954
164826
  execute_threshold_percentage: exports_external.union([
164955
- exports_external.number().min(20).max(80),
164956
- exports_external.object({ default: exports_external.number().min(20).max(80) }).catchall(exports_external.number().min(20).max(80))
164957
- ]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE),
164827
+ exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE),
164828
+ exports_external.object({ default: exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE) }).catchall(exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE))
164829
+ ]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE).describe('Context percentage that forces queued operations to execute. Number or per-model object ({ default: 65, "provider/model": 45 }). Values above 80 are rejected because the runtime caps at 80% for cache safety (MAX_EXECUTE_THRESHOLD). Default: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE'),
164958
164830
  execute_threshold_tokens: exports_external.object({
164959
164831
  default: exports_external.number().min(5000).max(2000000).optional()
164960
- }).catchall(exports_external.number().min(5000).max(2000000)).optional(),
164961
- protected_tags: exports_external.number().min(1).max(100).optional(),
164962
- auto_drop_tool_age: exports_external.number().min(10).default(100),
164963
- drop_tool_structure: exports_external.boolean().default(true),
164964
- clear_reasoning_age: exports_external.number().min(10).default(50),
164965
- iteration_nudge_threshold: exports_external.number().min(5).default(15),
164966
- history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE),
164967
- historian_timeout_ms: exports_external.number().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS),
164832
+ }).catchall(exports_external.number().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above 80% × context_limit are clamped with a warning log. Min 5_000, max 2_000_000."),
164833
+ protected_tags: exports_external.number().min(1).max(100).optional().describe("Number of recent tags to protect from dropping (min: 1, max: 100, default: 20)"),
164834
+ auto_drop_tool_age: exports_external.number().min(10).default(100).describe("Auto-drop tool outputs older than N tags during queue execution (default: 100)"),
164835
+ drop_tool_structure: exports_external.boolean().default(true).describe("When true, dropped tool parts are fully removed instead of truncated in place (default: true)"),
164836
+ clear_reasoning_age: exports_external.number().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
164837
+ iteration_nudge_threshold: exports_external.number().min(5).default(15).describe("Number of consecutive assistant messages without user input to trigger iteration nudge (default: 15)"),
164838
+ history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE).describe("Fraction of usable context (context_limit × execute_threshold) reserved for the session history block (default: 0.15)"),
164839
+ historian_timeout_ms: exports_external.number().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS).describe("Timeout for each historian prompt call in milliseconds (default: 300000)"),
164968
164840
  commit_cluster_trigger: exports_external.object({
164969
- enabled: exports_external.boolean().default(true),
164970
- min_clusters: exports_external.number().min(1).default(3)
164971
- }).default({ enabled: true, min_clusters: 3 }),
164841
+ enabled: exports_external.boolean().default(true).describe("Enable commit-cluster based historian triggering (default: true)"),
164842
+ min_clusters: exports_external.number().min(1).default(3).describe("Minimum commit clusters required to trigger historian (min: 1, default: 3)")
164843
+ }).default({ enabled: true, min_clusters: 3 }).describe("Commit-cluster trigger: fire historian when enough commit clusters accumulate in the unsummarized tail"),
164972
164844
  system_prompt_injection: exports_external.object({
164973
- enabled: exports_external.boolean().default(true),
164974
- skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"])
164845
+ enabled: exports_external.boolean().default(true).describe("When false, NO injection happens for ANY agent — global escape hatch. (default: true)"),
164846
+ skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"]).describe(`Substring opt-out list. If the agent's system prompt contains any of these strings, skip ALL Magic Context injection for that call. Default "<!-- magic-context: skip -->" is meant to be added inside a user's custom agent prompt to opt that agent out.`)
164975
164847
  }).default({
164976
164848
  enabled: true,
164977
164849
  skip_signatures: ["<!-- magic-context: skip -->"]
164978
- }),
164850
+ }).describe("Controls whether and where Magic Context augments the system prompt. Lets users opt specific agents out of the Magic Context guidance and the surrounding project-docs / user-profile / key-files blocks. OpenCode's internal hidden agents — title, summary, and compaction — are always skipped automatically."),
164979
164851
  sqlite: exports_external.object({
164980
- cache_size_mb: exports_external.number().min(2).max(2048).default(64),
164981
- mmap_size_mb: exports_external.number().min(0).max(8192).default(0)
164982
- }).default({ cache_size_mb: 64, mmap_size_mb: 0 }),
164852
+ cache_size_mb: exports_external.number().min(2).max(2048).default(64).describe("Page-cache size in MiB per connection (PRAGMA cache_size). Larger keeps more hot pages resident, cutting re-reads on repeated full-table scans. (min 2, max 2048, default 64)"),
164853
+ mmap_size_mb: exports_external.number().min(0).max(8192).default(0).describe("Memory-mapped I/O size in MiB (PRAGMA mmap_size). 0 disables mmap (SQLite default). Raising it can cut read overhead on large DBs at the cost of address space. (min 0, max 8192, default 0)")
164854
+ }).default({ cache_size_mb: 64, mmap_size_mb: 0 }).describe("SQLite connection tuning for Magic Context's own context.db. These are per-connection PRAGMAs applied at open; they do not change the schema or what is stored."),
164983
164855
  embedding: EmbeddingConfigSchema.default({
164984
164856
  provider: "local",
164985
164857
  model: DEFAULT_LOCAL_EMBEDDING_MODEL
164986
- }),
164987
- temporal_awareness: exports_external.boolean().default(true),
164858
+ }).describe("Embedding provider configuration"),
164859
+ temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
164988
164860
  caveman_text_compression: exports_external.object({
164989
- enabled: exports_external.boolean().default(false),
164990
- min_chars: exports_external.number().min(100).max(1e4).default(500)
164991
- }).default({ enabled: false, min_chars: 500 }),
164861
+ enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Only active when ctx_reduce_enabled=false. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
164862
+ min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
164863
+ }).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Only active when ctx_reduce_enabled is false. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
164992
164864
  memory: exports_external.object({
164993
- enabled: exports_external.boolean().default(true),
164994
- injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000),
164995
- auto_promote: exports_external.boolean().default(true),
164996
- retrieval_count_promotion_threshold: exports_external.number().min(1).default(3),
164865
+ enabled: exports_external.boolean().default(true).describe("Enable cross-session memory (default: true)"),
164866
+ injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000).describe("Token budget for memory injection on session start (min: 500, max: 20000, default: 4000)"),
164867
+ auto_promote: exports_external.boolean().default(true).describe("Automatically promote eligible session facts into memory (default: true)"),
164868
+ retrieval_count_promotion_threshold: exports_external.number().min(1).default(3).describe("retrieval_count threshold for promoting memory to permanent status (min: 1, default: 3)"),
164997
164869
  auto_search: exports_external.object({
164998
- enabled: exports_external.boolean().default(true),
164999
- score_threshold: exports_external.number().min(0.3).max(0.95).default(0.6),
165000
- min_prompt_chars: exports_external.number().min(5).max(500).default(20)
165001
- }).default({ enabled: true, score_threshold: 0.6, min_prompt_chars: 20 }),
164870
+ enabled: exports_external.boolean().default(true).describe("Automatically append a compact <ctx-search-hint> to eligible user messages when relevant memories, conversation, or commits are found. Graduated from experimental.auto_search; on by default (set false to opt out). Independent of memory.enabled."),
164871
+ score_threshold: exports_external.number().min(0.3).max(0.95).default(0.6).describe("Top hit score must exceed this threshold for the hint to fire (min: 0.3, max: 0.95, default: 0.60)"),
164872
+ min_prompt_chars: exports_external.number().min(5).max(500).default(20).describe("Skip hint when user message is shorter than this (min: 5, max: 500, default: 20)")
164873
+ }).default({ enabled: true, score_threshold: 0.6, min_prompt_chars: 20 }).describe("Auto-search hint: transform-time ctx_search on each new user message; when the top hit clears the threshold, append a compact <ctx-search-hint> block of vague fragments to that user message. Does NOT inject full content. Graduated from experimental.auto_search; enabled by default (set enabled: false to opt out). Independent of memory.enabled."),
165002
164874
  git_commit_indexing: exports_external.object({
165003
- enabled: exports_external.boolean().default(false),
165004
- since_days: exports_external.number().min(7).max(3650).default(365),
165005
- max_commits: exports_external.number().min(100).max(20000).default(2000)
165006
- }).default({ enabled: false, since_days: 365, max_commits: 2000 })
164875
+ enabled: exports_external.boolean().default(false).describe("Index HEAD git commits for ctx_search (git_commit source). Graduated from experimental.git_commit_indexing; opt-in, default off. Independent of memory.enabled."),
164876
+ since_days: exports_external.number().min(7).max(3650).default(365).describe("Days of HEAD history to index (min: 7, max: 3650, default: 365)"),
164877
+ max_commits: exports_external.number().min(100).max(20000).default(2000).describe("Max commits kept per project; oldest evicted (min: 100, max: 20000, default: 2000)")
164878
+ }).default({ enabled: false, since_days: 365, max_commits: 2000 }).describe("Index git commit messages from HEAD into ctx_search. Commits become a 4th searchable source alongside memories and session history. Graduated from experimental.git_commit_indexing; opt-in, default off (per-project embedding cost). Independent of memory.enabled.")
165007
164879
  }).default({
165008
164880
  enabled: true,
165009
164881
  injection_budget_tokens: 4000,
@@ -165011,8 +164883,8 @@ var MagicContextConfigSchema = exports_external.object({
165011
164883
  retrieval_count_promotion_threshold: 3,
165012
164884
  auto_search: { enabled: true, score_threshold: 0.6, min_prompt_chars: 20 },
165013
164885
  git_commit_indexing: { enabled: false, since_days: 365, max_commits: 2000 }
165014
- }),
165015
- sidekick: SidekickConfigSchema
164886
+ }).describe("Cross-session memory configuration"),
164887
+ sidekick: SidekickConfigSchema.describe("Optional sidekick agent configuration for session-start memory retrieval")
165016
164888
  }).transform((data) => {
165017
164889
  return {
165018
164890
  ...data,
@@ -165052,7 +164924,8 @@ function getEmbeddingProviderIdentity(config2) {
165052
164924
  provider: "openai-compatible",
165053
164925
  model: config2.model.trim(),
165054
164926
  endpoint: normalizeEndpoint(config2.endpoint),
165055
- apiKeyPresent: Boolean(config2.api_key?.trim())
164927
+ apiKeyPresent: Boolean(config2.api_key?.trim()),
164928
+ inputType: config2.input_type?.trim() || ""
165056
164929
  } : {
165057
164930
  provider: "local",
165058
164931
  model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
@@ -165063,7 +164936,7 @@ function getEmbeddingProviderIdentity(config2) {
165063
164936
  }
165064
164937
 
165065
164938
  // ../plugin/src/features/magic-context/memory/embedding-local.ts
165066
- import { mkdirSync as mkdirSync4 } from "node:fs";
164939
+ import { mkdirSync as mkdirSync5 } from "node:fs";
165067
164940
  import { open, stat, unlink, writeFile } from "node:fs/promises";
165068
164941
  import { dirname as dirname4, join as join12 } from "node:path";
165069
164942
  import { pathToFileURL } from "node:url";
@@ -165244,7 +165117,7 @@ class LocalEmbeddingProvider {
165244
165117
  }
165245
165118
  const modelCacheDir = join12(getMagicContextStorageDir(), "models");
165246
165119
  try {
165247
- mkdirSync4(modelCacheDir, { recursive: true });
165120
+ mkdirSync5(modelCacheDir, { recursive: true });
165248
165121
  env.cacheDir = modelCacheDir;
165249
165122
  } catch {
165250
165123
  log("[magic-context] could not create model cache dir, using library default");
@@ -165418,6 +165291,8 @@ class OpenAICompatibleEmbeddingProvider {
165418
165291
  endpoint;
165419
165292
  model;
165420
165293
  apiKey;
165294
+ inputType;
165295
+ truncate;
165421
165296
  initialized = false;
165422
165297
  failureTimes = [];
165423
165298
  circuitOpenUntil = 0;
@@ -165427,6 +165302,8 @@ class OpenAICompatibleEmbeddingProvider {
165427
165302
  this.endpoint = normalizeEndpoint2(options.endpoint);
165428
165303
  this.model = options.model?.trim() ?? "";
165429
165304
  this.apiKey = options.apiKey?.trim() ?? "";
165305
+ this.inputType = options.inputType?.trim() ?? "";
165306
+ this.truncate = options.truncate?.trim() ?? "";
165430
165307
  this.modelId = getEmbeddingProviderIdentity({
165431
165308
  provider: "openai-compatible",
165432
165309
  endpoint: this.endpoint,
@@ -165483,7 +165360,9 @@ class OpenAICompatibleEmbeddingProvider {
165483
165360
  },
165484
165361
  body: JSON.stringify({
165485
165362
  model: this.model,
165486
- input: texts
165363
+ input: texts,
165364
+ ...this.inputType ? { input_type: this.inputType } : {},
165365
+ ...this.truncate ? { truncate: this.truncate } : {}
165487
165366
  }),
165488
165367
  signal: internalController.signal
165489
165368
  });
@@ -165613,7 +165492,7 @@ class OpenAICompatibleEmbeddingProvider {
165613
165492
  }
165614
165493
 
165615
165494
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
165616
- import { createHash as createHash8 } from "node:crypto";
165495
+ import { createHash as createHash7, randomUUID } from "node:crypto";
165617
165496
  init_logger();
165618
165497
 
165619
165498
  // ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
@@ -165717,6 +165596,125 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
165717
165596
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
165718
165597
  }
165719
165598
 
165599
+ // ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
165600
+ var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
165601
+ var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
165602
+ var GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
165603
+ function runImmediate2(db, body) {
165604
+ db.exec("BEGIN IMMEDIATE");
165605
+ let committed = false;
165606
+ try {
165607
+ const result = body();
165608
+ db.exec("COMMIT");
165609
+ committed = true;
165610
+ return result;
165611
+ } finally {
165612
+ if (!committed) {
165613
+ try {
165614
+ db.exec("ROLLBACK");
165615
+ } catch {}
165616
+ }
165617
+ }
165618
+ }
165619
+ function rowToState(row) {
165620
+ return {
165621
+ projectPath: row.project_path,
165622
+ leaseHolder: row.lease_holder,
165623
+ leaseExpiresAt: row.lease_expires_at,
165624
+ lastSweptAt: row.last_swept_at
165625
+ };
165626
+ }
165627
+ function getGitSweepCoordinatorState(db, projectPath) {
165628
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
165629
+ FROM git_sweep_coordinator
165630
+ WHERE project_path = ?`).get(projectPath);
165631
+ return row ? rowToState(row) : null;
165632
+ }
165633
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
165634
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
165635
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
165636
+ return runImmediate2(db, () => {
165637
+ const now = Date.now();
165638
+ const row = getGitSweepCoordinatorState(db, projectPath);
165639
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
165640
+ return {
165641
+ acquired: false,
165642
+ projectPath,
165643
+ reason: "lease_active",
165644
+ leaseHolder: row.leaseHolder,
165645
+ leaseExpiresAt: row.leaseExpiresAt,
165646
+ lastSweptAt: row.lastSweptAt,
165647
+ nextAllowedAt: null
165648
+ };
165649
+ }
165650
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
165651
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
165652
+ if (nextAllowedAt > now) {
165653
+ return {
165654
+ acquired: false,
165655
+ projectPath,
165656
+ reason: "cooldown_active",
165657
+ leaseHolder: row.leaseHolder,
165658
+ leaseExpiresAt: row.leaseExpiresAt,
165659
+ lastSweptAt: row.lastSweptAt,
165660
+ nextAllowedAt
165661
+ };
165662
+ }
165663
+ }
165664
+ const leaseExpiresAt = now + leaseTtlMs;
165665
+ db.prepare(`INSERT INTO git_sweep_coordinator (
165666
+ project_path,
165667
+ lease_holder,
165668
+ lease_expires_at,
165669
+ last_swept_at
165670
+ ) VALUES (?, ?, ?, NULL)
165671
+ ON CONFLICT(project_path) DO UPDATE SET
165672
+ lease_holder = excluded.lease_holder,
165673
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
165674
+ return {
165675
+ acquired: true,
165676
+ projectPath,
165677
+ holderId,
165678
+ acquiredAt: now,
165679
+ leaseExpiresAt
165680
+ };
165681
+ });
165682
+ }
165683
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
165684
+ return runImmediate2(db, () => {
165685
+ const now = Date.now();
165686
+ const leaseExpiresAt = now + leaseTtlMs;
165687
+ const result = db.prepare(`UPDATE git_sweep_coordinator
165688
+ SET lease_expires_at = ?
165689
+ WHERE project_path = ?
165690
+ AND lease_holder = ?
165691
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
165692
+ return result.changes === 1;
165693
+ });
165694
+ }
165695
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
165696
+ return runImmediate2(db, () => {
165697
+ const now = Date.now();
165698
+ const result = db.prepare(`UPDATE git_sweep_coordinator
165699
+ SET lease_holder = NULL,
165700
+ lease_expires_at = NULL,
165701
+ last_swept_at = ?
165702
+ WHERE project_path = ?
165703
+ AND lease_holder = ?
165704
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
165705
+ return result.changes === 1;
165706
+ });
165707
+ }
165708
+ function releaseGitSweepLease(db, projectPath, holderId) {
165709
+ runImmediate2(db, () => {
165710
+ db.prepare(`UPDATE git_sweep_coordinator
165711
+ SET lease_holder = NULL,
165712
+ lease_expires_at = NULL
165713
+ WHERE project_path = ?
165714
+ AND lease_holder = ?`).run(projectPath, holderId);
165715
+ });
165716
+ }
165717
+
165720
165718
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
165721
165719
  var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
165722
165720
  var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
@@ -165769,7 +165767,7 @@ function stableStringify2(value) {
165769
165767
  return JSON.stringify(value);
165770
165768
  }
165771
165769
  function sha256Prefix(value, length = 16) {
165772
- return createHash8("sha256").update(value).digest("hex").slice(0, length);
165770
+ return createHash7("sha256").update(value).digest("hex").slice(0, length);
165773
165771
  }
165774
165772
  function getRuntimeFingerprint(config2) {
165775
165773
  if (config2.provider === "off") {
@@ -165999,7 +165997,9 @@ function createProvider2(config2) {
165999
165997
  return new OpenAICompatibleEmbeddingProvider({
166000
165998
  endpoint: config2.endpoint,
166001
165999
  model: config2.model,
166002
- apiKey: config2.api_key
166000
+ apiKey: config2.api_key,
166001
+ inputType: config2.input_type,
166002
+ truncate: config2.truncate
166003
166003
  });
166004
166004
  }
166005
166005
  return new LocalEmbeddingProvider(config2.model);
@@ -166032,6 +166032,7 @@ var insertStatements = new WeakMap;
166032
166032
  var existingShasStatements = new WeakMap;
166033
166033
  var projectCountStatements = new WeakMap;
166034
166034
  var evictStatements = new WeakMap;
166035
+ var evictOverflowStatements = new WeakMap;
166035
166036
  var latestCommitTimeStatements = new WeakMap;
166036
166037
  function getInsertStatement(db) {
166037
166038
  let stmt = insertStatements.get(db);
@@ -166074,17 +166075,17 @@ function getLatestCommitTimeStatement(db) {
166074
166075
  }
166075
166076
  return stmt;
166076
166077
  }
166077
- function getEvictStatement(db) {
166078
- let stmt = evictStatements.get(db);
166078
+ function getEvictOverflowStatement(db) {
166079
+ let stmt = evictOverflowStatements.get(db);
166079
166080
  if (!stmt) {
166080
166081
  stmt = db.prepare(`DELETE FROM git_commits
166081
- WHERE sha IN (
166082
- SELECT sha FROM git_commits
166082
+ WHERE rowid IN (
166083
+ SELECT rowid FROM git_commits
166083
166084
  WHERE project_path = ?
166084
- ORDER BY committed_at ASC
166085
- LIMIT ?
166085
+ ORDER BY committed_at DESC, sha DESC
166086
+ LIMIT -1 OFFSET ?
166086
166087
  )`);
166087
- evictStatements.set(db, stmt);
166088
+ evictOverflowStatements.set(db, stmt);
166088
166089
  }
166089
166090
  return stmt;
166090
166091
  }
@@ -166122,22 +166123,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
166122
166123
  const row = getLatestCommitTimeStatement(db).get(projectPath);
166123
166124
  return row?.latest ?? null;
166124
166125
  }
166125
- function evictOldestCommits(db, projectPath, excess) {
166126
- if (excess <= 0)
166127
- return 0;
166128
- const before = getCommitCount(db, projectPath);
166129
- getEvictStatement(db).run(projectPath, excess);
166130
- const after = getCommitCount(db, projectPath);
166131
- return Math.max(0, before - after);
166132
- }
166133
166126
  function enforceProjectCap(db, projectPath, maxCommits) {
166134
166127
  if (maxCommits <= 0)
166135
166128
  return 0;
166136
166129
  const count = getCommitCount(db, projectPath);
166137
166130
  if (count <= maxCommits)
166138
166131
  return 0;
166139
- const excess = count - maxCommits;
166140
- const evicted = evictOldestCommits(db, projectPath, excess);
166132
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
166133
+ const after = getCommitCount(db, projectPath);
166134
+ const evicted = Math.max(0, count - after);
166141
166135
  if (evicted > 0) {
166142
166136
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
166143
166137
  }
@@ -166517,10 +166511,31 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
166517
166511
  log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
166518
166512
  }
166519
166513
  }
166514
+ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
166515
+ const timer = setInterval(() => {
166516
+ try {
166517
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
166518
+ log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
166519
+ }
166520
+ } catch (error51) {
166521
+ log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
166522
+ }
166523
+ }, GIT_SWEEP_LEASE_RENEWAL_MS);
166524
+ timer.unref?.();
166525
+ return () => clearInterval(timer);
166526
+ }
166520
166527
  async function sweepGitCommits(args) {
166521
166528
  const { directory, projectIdentity, db, gitCommitIndexing } = args;
166529
+ const holderId = crypto.randomUUID();
166530
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
166531
+ if (!lease2.acquired) {
166532
+ const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
166533
+ log(`[git-commits] sweep skipped for ${projectIdentity} (${directory}): ${reason}`);
166534
+ return;
166535
+ }
166522
166536
  const startedAt = Date.now();
166523
- log(`[git-commits] sweep starting for ${directory} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
166537
+ const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
166538
+ log(`[git-commits] sweep starting for ${directory} (project=${projectIdentity} sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
166524
166539
  try {
166525
166540
  const result = await indexCommitsForProject(db, projectIdentity, directory, {
166526
166541
  sinceDays: gitCommitIndexing.since_days,
@@ -166530,16 +166545,24 @@ async function sweepGitCommits(args) {
166530
166545
  if (result.embedded > 0) {
166531
166546
  drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
166532
166547
  }
166548
+ const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
166549
+ if (!cooldownMarked) {
166550
+ releaseGitSweepLease(db, projectIdentity, holderId);
166551
+ log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
166552
+ }
166533
166553
  const elapsedMs = Date.now() - startedAt;
166534
166554
  log(`[git-commits] sweep finished for ${projectIdentity} in ${elapsedMs}ms: scanned=${result.scanned} inserted=${result.inserted} updated=${result.updated} evicted=${result.evicted} embedded=${result.embedded} drained=${drainedEmbeddings}`);
166535
166555
  } catch (error51) {
166556
+ releaseGitSweepLease(db, projectIdentity, holderId);
166536
166557
  const elapsedMs = Date.now() - startedAt;
166537
166558
  log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
166559
+ } finally {
166560
+ stopRenewal();
166538
166561
  }
166539
166562
  }
166540
166563
 
166541
166564
  // ../plugin/src/plugin/embedding-bootstrap-helpers.ts
166542
- import { createHash as createHash9 } from "node:crypto";
166565
+ import { createHash as createHash8 } from "node:crypto";
166543
166566
  init_logger();
166544
166567
  var EMBEDDING_AFFECTING_KEYS = new Set([
166545
166568
  "embedding.api_key",
@@ -166551,7 +166574,7 @@ var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set(["embedding", "memory", "experi
166551
166574
  var EMBEDDING_WARNING_TERMS = ["api_key", "endpoint", "model", "provider", "embedding"];
166552
166575
  var loggedFailureSignatures = new Map;
166553
166576
  function sha256Prefix2(value, length = 16) {
166554
- return createHash9("sha256").update(value).digest("hex").slice(0, length);
166577
+ return createHash8("sha256").update(value).digest("hex").slice(0, length);
166555
166578
  }
166556
166579
  function warningLooksEmbeddingRelated(message) {
166557
166580
  const lower = message.toLowerCase();
@@ -166615,8 +166638,8 @@ function handleUntrustedLoad(db, projectIdentity, directory, detailed) {
166615
166638
  }
166616
166639
 
166617
166640
  // src/config/index.ts
166618
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
166619
- import { homedir as homedir5 } from "node:os";
166641
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
166642
+ import { homedir as homedir4 } from "node:os";
166620
166643
  import { join as join13 } from "node:path";
166621
166644
 
166622
166645
  // ../plugin/src/config/migrate-experimental.ts
@@ -166706,9 +166729,67 @@ function migrateLegacyExperimental(rawConfig, warnings) {
166706
166729
  }
166707
166730
 
166708
166731
  // ../plugin/src/config/variable.ts
166709
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
166710
- import { homedir as homedir4 } from "node:os";
166732
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
166733
+ import { homedir as homedir3 } from "node:os";
166711
166734
  import { dirname as dirname5, isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
166735
+
166736
+ // ../plugin/src/shared/jsonc-parser.ts
166737
+ function stripJsonComments(content) {
166738
+ let result = "";
166739
+ let inString = false;
166740
+ let escaped = false;
166741
+ let inLineComment = false;
166742
+ let inBlockComment = false;
166743
+ for (let index = 0;index < content.length; index += 1) {
166744
+ const char = content[index];
166745
+ const next = content[index + 1];
166746
+ if (inLineComment) {
166747
+ if (char === `
166748
+ `) {
166749
+ inLineComment = false;
166750
+ result += char;
166751
+ }
166752
+ continue;
166753
+ }
166754
+ if (inBlockComment) {
166755
+ if (char === "*" && next === "/") {
166756
+ inBlockComment = false;
166757
+ index += 1;
166758
+ }
166759
+ continue;
166760
+ }
166761
+ if (inString) {
166762
+ result += char;
166763
+ if (escaped) {
166764
+ escaped = false;
166765
+ } else if (char === "\\") {
166766
+ escaped = true;
166767
+ } else if (char === '"') {
166768
+ inString = false;
166769
+ }
166770
+ continue;
166771
+ }
166772
+ if (char === '"') {
166773
+ inString = true;
166774
+ result += char;
166775
+ continue;
166776
+ }
166777
+ if (char === "/" && next === "/") {
166778
+ inLineComment = true;
166779
+ index += 1;
166780
+ continue;
166781
+ }
166782
+ if (char === "/" && next === "*") {
166783
+ inBlockComment = true;
166784
+ index += 1;
166785
+ continue;
166786
+ }
166787
+ result += char;
166788
+ }
166789
+ return result;
166790
+ }
166791
+
166792
+ // ../plugin/src/config/variable.ts
166712
166793
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
166713
166794
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
166714
166795
  function substituteConfigVariables(input) {
@@ -166760,11 +166841,11 @@ function substituteConfigVariables(input) {
166760
166841
  }
166761
166842
  let filePath = rawPath.trim();
166762
166843
  if (filePath.startsWith("~/")) {
166763
- filePath = resolve3(homedir4(), filePath.slice(2));
166844
+ filePath = resolve3(homedir3(), filePath.slice(2));
166764
166845
  } else if (!isAbsolute2(filePath)) {
166765
166846
  filePath = resolve3(configDir, filePath);
166766
166847
  }
166767
- if (!existsSync10(filePath)) {
166848
+ if (!existsSync9(filePath)) {
166768
166849
  warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
166769
166850
  continue;
166770
166851
  }
@@ -166790,12 +166871,12 @@ function getProjectConfigPaths(cwd) {
166790
166871
  return [`${basePath}.jsonc`, `${basePath}.json`];
166791
166872
  }
166792
166873
  function getUserConfigPaths() {
166793
- const home = process.env.HOME ?? homedir5();
166874
+ const home = process.env.HOME ?? homedir4();
166794
166875
  const basePath = join13(home, ".pi", "agent", CONFIG_FILE_NAME);
166795
166876
  return [`${basePath}.jsonc`, `${basePath}.json`];
166796
166877
  }
166797
166878
  function resolveFirstExisting(paths) {
166798
- return paths.find((path7) => existsSync11(path7));
166879
+ return paths.find((path7) => existsSync10(path7));
166799
166880
  }
166800
166881
  function loadConfigFile(path7, scope) {
166801
166882
  try {
@@ -169399,6 +169480,62 @@ function buildToolTarget(part, message) {
169399
169480
 
169400
169481
  // ../plugin/src/features/magic-context/search.ts
169401
169482
  init_logger();
169483
+
169484
+ // ../plugin/src/features/magic-context/literal-probes.ts
169485
+ var MAX_PROBES = 5;
169486
+ var MIN_PROBE_LENGTH = 3;
169487
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
169488
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
169489
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
169490
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
169491
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
169492
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
169493
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
169494
+ function looksLikeSha(token) {
169495
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
169496
+ }
169497
+ function extractLiteralProbes(query) {
169498
+ const trimmed = query.trim();
169499
+ if (trimmed.length === 0)
169500
+ return [];
169501
+ const ordered = [];
169502
+ const seen = new Set;
169503
+ const add = (raw) => {
169504
+ if (!raw)
169505
+ return;
169506
+ const probe = raw.trim();
169507
+ if (probe.length < MIN_PROBE_LENGTH)
169508
+ return;
169509
+ const key = probe.toLowerCase();
169510
+ if (seen.has(key))
169511
+ return;
169512
+ seen.add(key);
169513
+ ordered.push(probe);
169514
+ };
169515
+ for (const m of trimmed.matchAll(QUOTED_RE))
169516
+ add(m[1]);
169517
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
169518
+ add(m[0]);
169519
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
169520
+ add(m[0]);
169521
+ for (const m of trimmed.matchAll(DOTTED_RE))
169522
+ add(m[0]);
169523
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
169524
+ add(m[0]);
169525
+ for (const m of trimmed.matchAll(CAMEL_RE))
169526
+ add(m[0]);
169527
+ for (const m of trimmed.matchAll(SHA_RE)) {
169528
+ if (looksLikeSha(m[0]))
169529
+ add(m[0]);
169530
+ }
169531
+ return ordered.slice(0, MAX_PROBES);
169532
+ }
169533
+ function containsProbeVerbatim(text, probes) {
169534
+ if (probes.length === 0)
169535
+ return false;
169536
+ const haystack = text.toLowerCase();
169537
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
169538
+ }
169402
169539
  // ../plugin/src/features/magic-context/memory/embedding-backfill.ts
169403
169540
  init_logger();
169404
169541
  async function ensureMemoryEmbeddings(args) {
@@ -169661,36 +169798,82 @@ function linearDecayScore(rank, total) {
169661
169798
  return 0;
169662
169799
  return Math.max(0, 1 - rank / total);
169663
169800
  }
169664
- function searchMessages(args) {
169665
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
169666
- if (sanitizedQuery.length === 0) {
169801
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
169802
+ if (ftsQuery.length === 0)
169667
169803
  return [];
169668
- }
169669
- const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
169670
- const rows = getMessageSearchStatement(args.db).all(args.sessionId, sanitizedQuery, fetchLimit).map((row) => row);
169671
- const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
169672
- const filtered = rows.map((row) => {
169804
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
169805
+ const result = [];
169806
+ for (const row of rows) {
169673
169807
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
169674
169808
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
169675
- return null;
169809
+ continue;
169676
169810
  }
169677
169811
  if (cutoff !== null && messageOrdinal > cutoff) {
169678
- return null;
169812
+ continue;
169679
169813
  }
169680
- return {
169814
+ result.push({
169681
169815
  messageOrdinal,
169682
169816
  messageId: row.messageId,
169683
169817
  role: row.role,
169684
169818
  content: row.content
169685
- };
169686
- }).filter((result) => result !== null).slice(0, args.limit);
169687
- return filtered.map((row, rank) => ({
169819
+ });
169820
+ }
169821
+ return result;
169822
+ }
169823
+ var RRF_K = 60;
169824
+ var VERBATIM_PROBE_BONUS = 0.5;
169825
+ function searchMessages(args) {
169826
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
169827
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
169828
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
169829
+ const probes = args.probes ?? [];
169830
+ if (probes.length === 0) {
169831
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
169832
+ return filtered.map((row, rank) => ({
169833
+ source: "message",
169834
+ content: previewText(row.content),
169835
+ score: linearDecayScore(rank, filtered.length),
169836
+ messageOrdinal: row.messageOrdinal,
169837
+ messageId: row.messageId,
169838
+ role: row.role
169839
+ }));
169840
+ }
169841
+ const queryLists = [];
169842
+ if (baseQuery.length > 0) {
169843
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
169844
+ }
169845
+ for (const probe of probes) {
169846
+ const probeQuery = sanitizeFtsQuery(probe);
169847
+ if (probeQuery.length === 0)
169848
+ continue;
169849
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
169850
+ }
169851
+ const fused = new Map;
169852
+ for (const list of queryLists) {
169853
+ list.forEach((row, rank) => {
169854
+ const rrf = 1 / (RRF_K + rank);
169855
+ const existing = fused.get(row.messageId);
169856
+ if (existing) {
169857
+ existing.score += rrf;
169858
+ } else {
169859
+ fused.set(row.messageId, { row, score: rrf });
169860
+ }
169861
+ });
169862
+ }
169863
+ for (const entry of fused.values()) {
169864
+ if (containsProbeVerbatim(entry.row.content, probes)) {
169865
+ entry.score += VERBATIM_PROBE_BONUS;
169866
+ }
169867
+ }
169868
+ const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
169869
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
169870
+ return ranked.map((entry) => ({
169688
169871
  source: "message",
169689
- content: previewText(row.content),
169690
- score: linearDecayScore(rank, filtered.length),
169691
- messageOrdinal: row.messageOrdinal,
169692
- messageId: row.messageId,
169693
- role: row.role
169872
+ content: previewText(entry.row.content),
169873
+ score: maxScore > 0 ? entry.score / maxScore : 0,
169874
+ messageOrdinal: entry.row.messageOrdinal,
169875
+ messageId: entry.row.messageId,
169876
+ role: entry.row.role
169694
169877
  }));
169695
169878
  }
169696
169879
  function getSourceBoost(result) {
@@ -169774,12 +169957,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
169774
169957
  return null;
169775
169958
  }) : Promise.resolve(null);
169776
169959
  await Promise.resolve();
169960
+ const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
169777
169961
  const messageResults = runMessages ? searchMessages({
169778
169962
  db,
169779
169963
  sessionId,
169780
169964
  query: trimmedQuery,
169781
169965
  limit: tierLimit,
169782
- maxOrdinal: options.maxMessageOrdinal
169966
+ maxOrdinal: options.maxMessageOrdinal,
169967
+ probes: messageProbes
169783
169968
  }) : [];
169784
169969
  const queryEmbedding = await queryEmbeddingPromise;
169785
169970
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -175642,7 +175827,7 @@ function stripPiDroppedPlaceholderMessages(args) {
175642
175827
  }
175643
175828
 
175644
175829
  // src/system-prompt.ts
175645
- import { createHash as createHash10 } from "node:crypto";
175830
+ import { createHash as createHash9 } from "node:crypto";
175646
175831
 
175647
175832
  // ../plugin/src/agents/magic-context-prompt.ts
175648
175833
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -175787,7 +175972,7 @@ function processSystemPromptForCache(args) {
175787
175972
  sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
175788
175973
  }
175789
175974
  }
175790
- const currentHash = createHash10("md5").update(frozenPrompt).digest("hex");
175975
+ const currentHash = createHash9("md5").update(frozenPrompt).digest("hex");
175791
175976
  const hashChanged = !isFirstHash && currentHash !== previousHash;
175792
175977
  if (hashChanged) {
175793
175978
  sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
@@ -176903,14 +177088,10 @@ function registerPiContextHandler(pi, options) {
176903
177088
  }
176904
177089
  const sessionMeta = sessionMetaForUsage;
176905
177090
  const modelKey = liveModelBySession.get(sessionId);
176906
- if (usageContextLimit === undefined && modelKey) {
176907
- const { providerID, modelID } = splitModelKeyForPi(modelKey);
176908
- const trusted = resolveTrustedContextLimit(providerID, modelID, {
176909
- db: options.db,
176910
- sessionID: sessionId
176911
- });
176912
- if (trusted !== undefined && trusted > 0) {
176913
- usageContextLimit = trusted;
177091
+ if (usageContextLimit === undefined) {
177092
+ const modelWindow = ctx.model?.contextWindow;
177093
+ if (isSaneLimit(modelWindow)) {
177094
+ usageContextLimit = modelWindow;
176914
177095
  }
176915
177096
  }
176916
177097
  let schedulerDecision;
@@ -177163,27 +177344,8 @@ async function awaitInFlightHistorians() {
177163
177344
  return;
177164
177345
  await Promise.allSettled(Array.from(inFlightHistorian.values()));
177165
177346
  }
177166
- function splitModelKeyForPi(modelKey) {
177167
- if (!modelKey)
177168
- return { providerID: undefined, modelID: undefined };
177169
- const slash = modelKey.indexOf("/");
177170
- if (slash <= 0 || slash === modelKey.length - 1) {
177171
- return { providerID: undefined, modelID: undefined };
177172
- }
177173
- return {
177174
- providerID: modelKey.slice(0, slash),
177175
- modelID: modelKey.slice(slash + 1)
177176
- };
177177
- }
177178
177347
  function resolvePiHistorianTriggerInputs(args) {
177179
- const { providerID, modelID } = splitModelKeyForPi(args.modelKey);
177180
- let contextLimit = resolveContextLimit(providerID, modelID, {
177181
- db: args.db,
177182
- sessionID: args.sessionId
177183
- });
177184
- if ((providerID === undefined || modelID === undefined) && typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0) {
177185
- contextLimit = args.usageContextLimit;
177186
- }
177348
+ const contextLimit = typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0 ? args.usageContextLimit : DEFAULT_CONTEXT_LIMIT;
177187
177349
  const executeThresholdPercentage = resolveExecuteThreshold(args.historian.executeThresholdPercentage ?? 65, args.modelKey, 65, {
177188
177350
  tokensConfig: args.historian.executeThresholdTokens,
177189
177351
  contextLimit,
@@ -178005,7 +178167,7 @@ ${result}`;
178005
178167
  init_logger();
178006
178168
 
178007
178169
  // ../plugin/src/hooks/magic-context/historian-state-file.ts
178008
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
178170
+ import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
178009
178171
  init_data_path();
178010
178172
  function cleanupHistorianStateFile(path7) {
178011
178173
  if (!path7)
@@ -178209,7 +178371,7 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
178209
178371
  }
178210
178372
 
178211
178373
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
178212
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
178374
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
178213
178375
  import { join as join16 } from "node:path";
178214
178376
  init_data_path();
178215
178377
  function historianResponseDumpDir(directory) {
@@ -178514,11 +178676,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
178514
178676
  function dumpHistorianResponse(sessionId, directory, label, text) {
178515
178677
  try {
178516
178678
  const dumpDir = historianResponseDumpDir(directory);
178517
- mkdirSync6(dumpDir, { recursive: true });
178679
+ mkdirSync7(dumpDir, { recursive: true });
178518
178680
  const safeSessionId = sanitizeDumpName(sessionId);
178519
178681
  const safeLabel = sanitizeDumpName(label);
178520
178682
  const dumpPath = join16(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
178521
- writeFileSync3(dumpPath, text, "utf8");
178683
+ writeFileSync4(dumpPath, text, "utf8");
178522
178684
  sessionLog(sessionId, "compartment agent: historian response dumped", {
178523
178685
  label,
178524
178686
  dumpPath
@@ -180430,7 +180592,7 @@ function formatThresholdPercent(value) {
180430
180592
  // package.json
180431
180593
  var package_default = {
180432
180594
  name: "@cortexkit/pi-magic-context",
180433
- version: "0.22.0",
180595
+ version: "0.22.2",
180434
180596
  type: "module",
180435
180597
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
180436
180598
  main: "dist/index.js",
@@ -185534,15 +185696,26 @@ function err3(text) {
185534
185696
  isError: true
185535
185697
  };
185536
185698
  }
185699
+ function captureAnchorOrdinal(db, sessionId) {
185700
+ try {
185701
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
185702
+ return ordinal > 0 ? ordinal : null;
185703
+ } catch {
185704
+ return null;
185705
+ }
185706
+ }
185707
+ function anchorSuffix(note) {
185708
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
185709
+ }
185537
185710
  function formatNoteLine(note) {
185538
185711
  if (note.type === "smart") {
185539
185712
  const conditionLine = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
185540
185713
  const statusSuffix2 = note.status === "active" ? "" : ` (${note.status})`;
185541
- return `- **#${note.id}**${statusSuffix2}: ${note.content}
185714
+ return `- **#${note.id}**${statusSuffix2}: ${note.content}${anchorSuffix(note)}
185542
185715
  *Condition*: ${conditionLine}`;
185543
185716
  }
185544
185717
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
185545
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
185718
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
185546
185719
  }
185547
185720
  var DISMISS_FOOTER = `
185548
185721
 
@@ -185564,6 +185737,7 @@ function createCtxNoteTool(deps) {
185564
185737
  const content = params.content?.trim();
185565
185738
  if (!content)
185566
185739
  return err3("Error: 'content' is required when action is 'write'.");
185740
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
185567
185741
  const surfaceCondition = params.surface_condition?.trim();
185568
185742
  if (surfaceCondition) {
185569
185743
  if (deps.dreamerEnabled !== true) {
@@ -185576,13 +185750,18 @@ function createCtxNoteTool(deps) {
185576
185750
  const note2 = addNote(deps.db, "smart", {
185577
185751
  content,
185578
185752
  projectPath: projectIdentity,
185579
- surfaceCondition
185753
+ surfaceCondition,
185754
+ anchorOrdinal
185580
185755
  });
185581
185756
  return ok3(`Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
185582
185757
  - Content: ${content}
185583
185758
  - Condition: ${surfaceCondition}`);
185584
185759
  }
185585
- const note = addNote(deps.db, "session", { sessionId, content });
185760
+ const note = addNote(deps.db, "session", {
185761
+ sessionId,
185762
+ content,
185763
+ anchorOrdinal
185764
+ });
185586
185765
  return ok3(`Saved session note #${note.id}.`);
185587
185766
  }
185588
185767
  if (action2 === "dismiss") {
@@ -185645,9 +185824,13 @@ function createCtxNoteTool(deps) {
185645
185824
 
185646
185825
  No notes for the current filter.`);
185647
185826
  }
185648
- return ok3(`${sections.join(`
185827
+ const body = sections.join(`
185828
+
185829
+ `);
185830
+ const anchorHint = body.includes("↳ @msg ") ? `
185649
185831
 
185650
- `)}${DISMISS_FOOTER}`);
185832
+ ↳ @msg N marks the conversation tail when a note was written. To see what led to it: ctx_expand(start=N-x, end=N) (pick x for how far back to look).` : "";
185833
+ return ok3(`${body}${anchorHint}${DISMISS_FOOTER}`);
185651
185834
  }
185652
185835
  };
185653
185836
  }
@@ -185992,7 +186175,8 @@ function createCtxSearchTool(deps) {
185992
186175
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
185993
186176
  gitCommitsEnabled,
185994
186177
  sources: params.sources,
185995
- visibleMemoryIds
186178
+ visibleMemoryIds,
186179
+ explicitSearch: true
185996
186180
  });
185997
186181
  return {
185998
186182
  content: [{ type: "text", text: formatSearchResults(query, results) }],
@@ -186140,8 +186324,7 @@ function getPiMessageModel(message) {
186140
186324
  };
186141
186325
  }
186142
186326
  function resolvePiPressureContextLimit(args) {
186143
- const cachedLimit = args.provider && args.model ? getModelsDevContextLimit(args.provider, args.model) : undefined;
186144
- let effectiveContextLimit = cachedLimit ?? args.piContextWindow;
186327
+ let effectiveContextLimit = isSaneLimit(args.piContextWindow) ? args.piContextWindow : 0;
186145
186328
  try {
186146
186329
  const overflowState = getOverflowState(args.db, args.sessionId);
186147
186330
  if (overflowState.detectedContextLimit > 0) {
@@ -186157,8 +186340,6 @@ async function persistPiPressureFromMessageEnd(args) {
186157
186340
  const effectiveContextLimit = resolvePiPressureContextLimit({
186158
186341
  db: args.db,
186159
186342
  sessionId: args.sessionId,
186160
- provider: provider2,
186161
- model,
186162
186343
  piContextWindow: args.piContextWindow
186163
186344
  });
186164
186345
  const usage = extractAssistantUsage(args.message);
@@ -186167,24 +186348,12 @@ async function persistPiPressureFromMessageEnd(args) {
186167
186348
  const messageHadOverflowError = typeof msg?.errorMessage === "string" && detectOverflow(msg.errorMessage).isOverflow;
186168
186349
  const updates = { lastResponseTime: Date.now() };
186169
186350
  if (pressure) {
186170
- let percentage = pressure.percentage;
186171
- let contextLimit = effectiveContextLimit;
186351
+ const percentage = pressure.percentage;
186352
+ const contextLimit = effectiveContextLimit;
186172
186353
  const meta3 = getOrCreateSessionMeta(args.db, args.sessionId);
186173
186354
  const observedSafeInputTokens = meta3.observedSafeInputTokens ?? 0;
186174
186355
  if (percentage > 100 && observedSafeInputTokens > 0 && pressure.inputTokens <= observedSafeInputTokens * 2) {
186175
- const oldLimit = contextLimit;
186176
- clearModelsDevCache();
186177
- contextLimit = resolvePiPressureContextLimit({
186178
- db: args.db,
186179
- sessionId: args.sessionId,
186180
- provider: provider2,
186181
- model,
186182
- piContextWindow: args.piContextWindow
186183
- });
186184
- if (contextLimit >= pressure.inputTokens) {
186185
- percentage = pressure.inputTokens / contextLimit * 100;
186186
- log(`${PREFIX} models-dev-cache: regression recovered for ${provider2}/${model} via Pi cache reload (was=${oldLimit}, now=${contextLimit})`);
186187
- } else if (!meta3.cacheAlertSent) {
186356
+ if (!meta3.cacheAlertSent) {
186188
186357
  updates.cacheAlertSent = true;
186189
186358
  const safeTokens = Math.max(observedSafeInputTokens, pressure.inputTokens);
186190
186359
  const modelLabel = provider2 && model ? `${provider2}/${model}` : "the active model";
@@ -186655,7 +186824,7 @@ ${block}` : event.systemPrompt;
186655
186824
  cacheTtlConfig: config2.cache_ttl
186656
186825
  });
186657
186826
  const piUsage = ctx.getContextUsage?.();
186658
- const piContextWindow = piUsage && typeof piUsage.contextWindow === "number" ? piUsage.contextWindow : 0;
186827
+ const piContextWindow = piUsage && typeof piUsage.contextWindow === "number" && piUsage.contextWindow > 0 ? piUsage.contextWindow : ctx.model?.contextWindow ?? 0;
186659
186828
  await persistPiPressureFromMessageEnd({
186660
186829
  db,
186661
186830
  sessionId,