@wolfx/pi-magic-context 0.24.1 → 0.26.0-patch.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +16239 -15569
  2. package/dist/subagent-entry.js +828 -603
  3. package/package.json +4 -6
@@ -1,4 +1,3 @@
1
- import { createRequire } from "node:module";
2
1
  var __create = Object.create;
3
2
  var __getProtoOf = Object.getPrototypeOf;
4
3
  var __defProp = Object.defineProperty;
@@ -44,7 +43,6 @@ var __export = (target, all) => {
44
43
  });
45
44
  };
46
45
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
48
46
 
49
47
  // ../plugin/src/shared/harness.ts
50
48
  function setHarness(value) {
@@ -8010,6 +8008,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
8010
8008
  }
8011
8009
  super(typeof filename === "string" ? filename : ":memory:", translated);
8012
8010
  }
8011
+ prepare(sql) {
8012
+ const stmt = super.prepare(sql);
8013
+ for (const method of ["run", "get", "all"]) {
8014
+ const original = stmt[method].bind(stmt);
8015
+ stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
8016
+ }
8017
+ return stmt;
8018
+ }
8013
8019
  transaction(fn) {
8014
8020
  const self = this;
8015
8021
  const wrapped = function(...args) {
@@ -9574,6 +9580,41 @@ var MIGRATIONS = [
9574
9580
  `);
9575
9581
  }
9576
9582
  }
9583
+ },
9584
+ {
9585
+ version: 37,
9586
+ description: "emergency drain catch-up latch + historian drain failure backoff",
9587
+ up: (db) => {
9588
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
9589
+ if (!hasSessionMeta)
9590
+ return;
9591
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
9592
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
9593
+ }
9594
+ },
9595
+ {
9596
+ version: 38,
9597
+ description: "durable transform decisions for cache-event cause attribution",
9598
+ up: (db) => {
9599
+ db.exec(`
9600
+ CREATE TABLE IF NOT EXISTS transform_decisions (
9601
+ session_id TEXT NOT NULL,
9602
+ harness TEXT NOT NULL DEFAULT 'opencode',
9603
+ message_id TEXT NOT NULL,
9604
+ ts_ms INTEGER NOT NULL,
9605
+ decision TEXT NOT NULL,
9606
+ materialized INTEGER NOT NULL DEFAULT 0,
9607
+ materialize_reason TEXT,
9608
+ emergency INTEGER NOT NULL DEFAULT 0,
9609
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
9610
+ dropped_count INTEGER NOT NULL DEFAULT 0,
9611
+ input_tokens INTEGER NOT NULL DEFAULT 0,
9612
+ PRIMARY KEY (session_id, harness, message_id)
9613
+ );
9614
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
9615
+ ON transform_decisions(session_id, harness);
9616
+ `);
9617
+ }
9577
9618
  }
9578
9619
  ];
9579
9620
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -142550,8 +142591,9 @@ function backfillToolOwnersInChunks(db, result) {
142550
142591
  var databases = new Map;
142551
142592
  var persistenceByDatabase = new WeakMap;
142552
142593
  var persistenceErrorByDatabase = new WeakMap;
142594
+ var pathByDatabase = new WeakMap;
142553
142595
  var lastSchemaFenceRejection = null;
142554
- var LATEST_SUPPORTED_VERSION = 36;
142596
+ var LATEST_SUPPORTED_VERSION = 38;
142555
142597
  function resolveDatabasePath(dbPathOverride) {
142556
142598
  if (dbPathOverride) {
142557
142599
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -142564,6 +142606,9 @@ function resolveDatabasePath(dbPathOverride) {
142564
142606
  const dbDir = getMagicContextStorageDir();
142565
142607
  return { dbDir, dbPath: join4(dbDir, "context.db") };
142566
142608
  }
142609
+ function getDatabasePath(db) {
142610
+ return pathByDatabase.get(db) ?? null;
142611
+ }
142567
142612
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
142568
142613
  if (existsSync3(targetDbPath))
142569
142614
  return;
@@ -143078,6 +143123,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143078
143123
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
143079
143124
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
143080
143125
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
143126
+ emergency_drain_active INTEGER NOT NULL DEFAULT 0,
143127
+ historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
143081
143128
  cached_m0_materialized_at INTEGER,
143082
143129
  cached_m0_session_facts_version INTEGER,
143083
143130
  cached_m0_upgrade_state TEXT,
@@ -143141,6 +143188,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143141
143188
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
143142
143189
  ON historian_runs(status, created_at DESC);
143143
143190
 
143191
+ CREATE TABLE IF NOT EXISTS transform_decisions (
143192
+ session_id TEXT NOT NULL,
143193
+ harness TEXT NOT NULL DEFAULT 'opencode',
143194
+ message_id TEXT NOT NULL,
143195
+ ts_ms INTEGER NOT NULL,
143196
+ decision TEXT NOT NULL,
143197
+ materialized INTEGER NOT NULL DEFAULT 0,
143198
+ materialize_reason TEXT,
143199
+ emergency INTEGER NOT NULL DEFAULT 0,
143200
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
143201
+ dropped_count INTEGER NOT NULL DEFAULT 0,
143202
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143203
+ PRIMARY KEY (session_id, harness, message_id)
143204
+ );
143205
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
143206
+ ON transform_decisions(session_id, harness);
143207
+
143144
143208
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
143145
143209
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
143146
143210
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -143218,6 +143282,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143218
143282
  ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
143219
143283
  ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
143220
143284
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
143285
+ ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
143221
143286
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
143222
143287
  ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
143223
143288
  ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
@@ -143291,6 +143356,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143291
143356
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
143292
143357
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
143293
143358
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
143359
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
143360
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
143294
143361
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
143295
143362
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
143296
143363
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -143369,6 +143436,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143369
143436
  failed_at INTEGER NOT NULL,
143370
143437
  UNIQUE(table_name, row_id)
143371
143438
  );
143439
+ CREATE TABLE IF NOT EXISTS transform_decisions (
143440
+ session_id TEXT NOT NULL,
143441
+ harness TEXT NOT NULL DEFAULT 'opencode',
143442
+ message_id TEXT NOT NULL,
143443
+ ts_ms INTEGER NOT NULL,
143444
+ decision TEXT NOT NULL,
143445
+ materialized INTEGER NOT NULL DEFAULT 0,
143446
+ materialize_reason TEXT,
143447
+ emergency INTEGER NOT NULL DEFAULT 0,
143448
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
143449
+ dropped_count INTEGER NOT NULL DEFAULT 0,
143450
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143451
+ PRIMARY KEY (session_id, harness, message_id)
143452
+ );
143453
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
143454
+ ON transform_decisions(session_id, harness);
143372
143455
  `);
143373
143456
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143374
143457
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -143455,7 +143538,9 @@ function healNullIntegerColumns(db) {
143455
143538
  ["protected_tail_drain_tokens", 0],
143456
143539
  ["recovery_no_eligible_head_count", 0],
143457
143540
  ["force_emergency_bypass_window_start", 0],
143458
- ["force_emergency_bypass_used", 0]
143541
+ ["force_emergency_bypass_used", 0],
143542
+ ["emergency_drain_active", 0],
143543
+ ["historian_drain_failure_at", 0]
143459
143544
  ];
143460
143545
  for (const [column, fallback] of columns) {
143461
143546
  try {
@@ -143531,6 +143616,7 @@ function openDatabase(dbPathOrOptions) {
143531
143616
  setDatabase(db);
143532
143617
  loadToolDefinitionMeasurements(db);
143533
143618
  databases.set(dbPath, db);
143619
+ pathByDatabase.set(db, dbPath);
143534
143620
  persistenceByDatabase.set(db, true);
143535
143621
  persistenceErrorByDatabase.delete(db);
143536
143622
  return db;
@@ -158092,7 +158178,6 @@ var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
158092
158178
  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.";
158093
158179
  var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
158094
158180
  var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
158095
- var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
158096
158181
  var DREAMER_TASKS = [
158097
158182
  "consolidate",
158098
158183
  "verify",
@@ -158132,14 +158217,16 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
158132
158217
  }).optional();
158133
158218
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
158134
158219
  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)"),
158135
- 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")
158220
+ 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"),
158221
+ disallowed_tools: exports_external.array(exports_external.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"])).default([]).describe(`OpenCode only. Tools to REMOVE from the historian's default allow-list [read, aft_outline, aft_zoom, aft_search]. Applies to both historian and historian-editor agents. Use ["*"] to strip all tool definitions from the model request — this prevents weak instruction-following models (e.g. mistral-small-latest) from entering tool-calling loops. Individual tool names remove just that tool. Note: a user-supplied historian.permission override can re-allow a tool that disallowed_tools removed — disallowed_tools sets the baseline, permission overrides take precedence. (default: [])`)
158136
158222
  }).optional();
158137
158223
  var BaseEmbeddingConfigSchema = exports_external.object({
158138
- 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."),
158139
- model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
158224
+ provider: exports_external.enum(["openai-compatible", "off"]).default("off").describe("Embedding provider. 'openai-compatible' requires endpoint and model, 'off' disables embeddings (default)."),
158225
+ model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible."),
158140
158226
  endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
158141
158227
  api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
158142
- 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."),
158228
+ input_type: exports_external.string().optional().describe("Default input_type for stored/indexed (passage) embeddings in the request body. Required by some openai-compatible providers (e.g. NVIDIA NIM). Omitted from the request when unset."),
158229
+ query_input_type: exports_external.string().optional().describe("Optional input_type for query (search) embeddings on asymmetric models (e.g. NVIDIA NIM 'query'). When unset, query embeddings use embedding.input_type. Passage/stored content always uses embedding.input_type."),
158143
158230
  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."),
158144
158231
  max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
158145
158232
  }).superRefine((data, ctx) => {
@@ -158159,16 +158246,10 @@ var BaseEmbeddingConfigSchema = exports_external.object({
158159
158246
  }
158160
158247
  });
158161
158248
  var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
158162
- if (data.provider === "local") {
158163
- return {
158164
- provider: "local",
158165
- model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
158166
- ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
158167
- };
158168
- }
158169
158249
  if (data.provider === "openai-compatible") {
158170
158250
  const apiKey = data.api_key?.trim();
158171
158251
  const inputType = data.input_type?.trim();
158252
+ const queryInputType = data.query_input_type?.trim();
158172
158253
  const truncate = data.truncate?.trim();
158173
158254
  return {
158174
158255
  provider: "openai-compatible",
@@ -158176,6 +158257,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
158176
158257
  endpoint: data.endpoint?.trim() ?? "",
158177
158258
  ...apiKey ? { api_key: apiKey } : {},
158178
158259
  ...inputType ? { input_type: inputType } : {},
158260
+ ...queryInputType ? { query_input_type: queryInputType } : {},
158179
158261
  ...truncate ? { truncate } : {},
158180
158262
  ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
158181
158263
  };
@@ -158215,8 +158297,7 @@ var MagicContextConfigSchema = exports_external.object({
158215
158297
  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)")
158216
158298
  }).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."),
158217
158299
  embedding: EmbeddingConfigSchema.default({
158218
- provider: "local",
158219
- model: DEFAULT_LOCAL_EMBEDDING_MODEL
158300
+ provider: "off"
158220
158301
  }).describe("Embedding provider configuration"),
158221
158302
  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).'),
158222
158303
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
@@ -158899,6 +158980,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
158899
158980
  return lines.join(`
158900
158981
  `);
158901
158982
  }
158983
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
158984
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
158985
+ if (!row)
158986
+ return "";
158987
+ const title = typeof row.title === "string" ? row.title.trim() : "";
158988
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
158989
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
158990
+ return [title, body].filter((s) => s.length > 0).join(`
158991
+ `);
158992
+ }
158902
158993
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
158903
158994
  const lines = [];
158904
158995
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -159142,6 +159233,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
159142
159233
  )`).get(projectPath, sessionId, projectPath, modelId);
159143
159234
  return typeof row?.n === "number" ? row.n : 0;
159144
159235
  }
159236
+ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
159237
+ const row = db.prepare(`SELECT
159238
+ COUNT(*) AS total,
159239
+ SUM(CASE WHEN EXISTS (
159240
+ SELECT 1 FROM compartment_chunk_embeddings e
159241
+ WHERE e.compartment_id = c.id
159242
+ AND e.project_path = ?
159243
+ AND e.model_id = ?
159244
+ ) THEN 1 ELSE 0 END) AS embedded
159245
+ FROM compartments c
159246
+ JOIN session_projects sp
159247
+ ON sp.session_id = c.session_id
159248
+ AND sp.harness = c.harness
159249
+ AND sp.project_path = ?
159250
+ WHERE c.session_id = ?
159251
+ AND c.start_message IS NOT NULL
159252
+ AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
159253
+ return {
159254
+ total: typeof row?.total === "number" ? row.total : 0,
159255
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
159256
+ };
159257
+ }
159145
159258
 
159146
159259
  // ../plugin/src/features/magic-context/memory/cosine-similarity.ts
159147
159260
  function cosineSimilarity(a, b) {
@@ -159184,356 +159297,11 @@ function getEmbeddingProviderIdentity(config2) {
159184
159297
  endpoint: normalizeEndpoint2(config2.endpoint),
159185
159298
  apiKeyPresent: Boolean(config2.api_key?.trim()),
159186
159299
  inputType: config2.input_type?.trim() || ""
159187
- } : {
159188
- provider: "local",
159189
- model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
159190
- endpoint: "",
159191
- apiKeyPresent: false
159192
- };
159193
- return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
159194
- }
159195
-
159196
- // ../plugin/src/features/magic-context/memory/embedding-local.ts
159197
- import { mkdirSync as mkdirSync3 } from "node:fs";
159198
- import { open, stat, unlink, writeFile } from "node:fs/promises";
159199
- import { dirname as dirname4, join as join6 } from "node:path";
159200
- import { pathToFileURL } from "node:url";
159201
- init_data_path();
159202
- init_logger();
159203
- var LOCK_POLL_MS = 150;
159204
- var STALE_LOCK_MS = 3 * 60000;
159205
- var MAX_LOCK_WAIT_MS = 5 * 60000;
159206
- async function acquireModelLoadLock(lockPath) {
159207
- const waitStart = Date.now();
159208
- while (true) {
159209
- try {
159210
- const handle = await open(lockPath, "wx");
159211
- try {
159212
- await handle.writeFile(`pid=${process.pid} started=${Date.now()}
159213
- `);
159214
- } catch {}
159215
- await handle.close();
159216
- return async () => {
159217
- try {
159218
- await unlink(lockPath);
159219
- } catch {}
159220
- };
159221
- } catch (error51) {
159222
- const code = error51.code;
159223
- if (code !== "EEXIST" && code !== "EPERM") {
159224
- throw error51;
159225
- }
159226
- try {
159227
- const info = await stat(lockPath);
159228
- if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
159229
- log(`[magic-context] embedding-load lock stale (>${STALE_LOCK_MS}ms), taking over`);
159230
- try {
159231
- await unlink(lockPath);
159232
- } catch {}
159233
- continue;
159234
- }
159235
- } catch {
159236
- continue;
159237
- }
159238
- if (Date.now() - waitStart > MAX_LOCK_WAIT_MS) {
159239
- throw new Error(`[magic-context] embedding-load lock wait exceeded ${MAX_LOCK_WAIT_MS}ms; another process is still loading the model. Skipping this init attempt to avoid an unsynchronized native load.`);
159240
- }
159241
- await new Promise((resolve3) => setTimeout(resolve3, LOCK_POLL_MS));
159242
- }
159243
- }
159244
- }
159245
- function startLockHeartbeat(lockPath) {
159246
- const HEARTBEAT_MS = Math.floor(STALE_LOCK_MS / 3);
159247
- const timer = setInterval(() => {
159248
- writeFile(lockPath, `pid=${process.pid} alive=${Date.now()}
159249
- `).catch(() => {});
159250
- }, HEARTBEAT_MS);
159251
- timer.unref?.();
159252
- return () => clearInterval(timer);
159253
- }
159254
- async function injectWasmOrtForElectron() {
159255
- if (typeof process === "undefined" || !process.versions?.electron) {
159256
- return false;
159257
- }
159258
- try {
159259
- const ortWebSpec = `onnxruntime-${"web"}`;
159260
- const ortWeb = await import(ortWebSpec);
159261
- try {
159262
- const { createRequire: createRequireFn } = await import("node:module");
159263
- const requireFn = createRequireFn(import.meta.url);
159264
- const pkgPath = requireFn.resolve("onnxruntime-web/package.json");
159265
- const distDir = join6(dirname4(pkgPath), "dist");
159266
- const wasmPathsPrefix = `${pathToFileURL(distDir).href}/`;
159267
- if (ortWeb.env?.wasm) {
159268
- ortWeb.env.wasm.wasmPaths = wasmPathsPrefix;
159269
- }
159270
- } catch (pathError) {
159271
- log("[magic-context] could not resolve local onnxruntime-web/dist, falling back to default WASM paths:", pathError instanceof Error ? pathError.message : String(pathError));
159272
- }
159273
- globalThis[Symbol.for("onnxruntime")] = ortWeb;
159274
- log("[magic-context] Electron detected — using onnxruntime-web (WASM) for embeddings (bypasses onnxruntime-node native load)");
159275
- return true;
159276
- } catch (error51) {
159277
- log("[magic-context] failed to inject onnxruntime-web for Electron — letting transformers fall back to native:", error51 instanceof Error ? error51.message : String(error51));
159278
- return false;
159279
- }
159280
- }
159281
- async function withQuietConsole(fn) {
159282
- const origWarn = console.warn;
159283
- const origError = console.error;
159284
- const redirect = (...args) => {
159285
- const message = args.map((a) => typeof a === "string" ? a : String(a)).join(" ");
159286
- log(`[transformers] ${message}`);
159287
- };
159288
- console.warn = redirect;
159289
- console.error = redirect;
159290
- try {
159291
- return await fn();
159292
- } finally {
159293
- console.warn = origWarn;
159294
- console.error = origError;
159295
- }
159296
- }
159297
- function isTransientLoadError(error51) {
159298
- const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
159299
- if (!message)
159300
- return false;
159301
- const lower = message.toLowerCase();
159302
- return lower.includes("protobuf parsing failed") || lower.includes("unable to get model file path or buffer") || lower.includes("ebusy") || lower.includes("resource busy") || lower.includes("resource temporarily unavailable");
159303
- }
159304
- function isArrayLikeNumber(value) {
159305
- if (typeof value !== "object" || value === null || !("length" in value)) {
159306
- return false;
159307
- }
159308
- const arr = value;
159309
- if (typeof arr.length !== "number") {
159310
- return false;
159311
- }
159312
- return arr.length === 0 || typeof arr[0] === "number";
159313
- }
159314
- function toFloat32Array2(values) {
159315
- return values instanceof Float32Array ? new Float32Array(values) : Float32Array.from(Array.from(values));
159316
- }
159317
- function extractBatchEmbeddings(result, expectedCount) {
159318
- const { data } = result;
159319
- if (Array.isArray(data) && data.length === expectedCount && data.every((entry) => typeof entry !== "number" && isArrayLikeNumber(entry))) {
159320
- return data.map((entry) => toFloat32Array2(entry));
159321
- }
159322
- if (!isArrayLikeNumber(data)) {
159323
- log("[magic-context] embedding batch returned unexpected data shape");
159324
- return Array.from({ length: expectedCount }, () => null);
159325
- }
159326
- const flatData = toFloat32Array2(data);
159327
- const dimension = result.dims?.at(-1) ?? flatData.length / expectedCount;
159328
- if (!Number.isInteger(dimension) || dimension <= 0 || flatData.length !== expectedCount * dimension) {
159329
- log("[magic-context] embedding batch returned invalid dimensions");
159330
- return Array.from({ length: expectedCount }, () => null);
159331
- }
159332
- const embeddings = [];
159333
- for (let index = 0;index < expectedCount; index++) {
159334
- embeddings.push(flatData.slice(index * dimension, (index + 1) * dimension));
159335
- }
159336
- return embeddings;
159337
- }
159338
-
159339
- class LocalEmbeddingProvider {
159340
- modelId;
159341
- maxInputTokens;
159342
- model;
159343
- pipeline = null;
159344
- initPromise = null;
159345
- inFlight = 0;
159346
- disposing = false;
159347
- disposePromise = null;
159348
- inFlightWaiters = [];
159349
- constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
159350
- this.model = model;
159351
- this.maxInputTokens = maxInputTokens;
159352
- this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
159353
- }
159354
- async initialize() {
159355
- if (this.disposing) {
159356
- return false;
159357
- }
159358
- if (this.pipeline) {
159359
- return true;
159360
- }
159361
- if (this.initPromise) {
159362
- await this.initPromise;
159363
- return this.pipeline !== null;
159364
- }
159365
- this.initPromise = (async () => {
159366
- try {
159367
- if (this.disposing) {
159368
- return;
159369
- }
159370
- await injectWasmOrtForElectron();
159371
- const transformersSpec = "@huggingface/transformers";
159372
- const transformersModule = await import(transformersSpec);
159373
- const env = transformersModule.env;
159374
- const LogLevel = transformersModule.LogLevel;
159375
- if (LogLevel && "ERROR" in LogLevel) {
159376
- env.logLevel = LogLevel.ERROR;
159377
- }
159378
- const modelCacheDir = join6(getMagicContextStorageDir(), "models");
159379
- try {
159380
- mkdirSync3(modelCacheDir, { recursive: true });
159381
- env.cacheDir = modelCacheDir;
159382
- } catch {
159383
- log("[magic-context] could not create model cache dir, using library default");
159384
- }
159385
- const createPipeline = transformersModule.pipeline;
159386
- const lockPath = join6(modelCacheDir, ".load.lock");
159387
- const releaseLock = await acquireModelLoadLock(lockPath);
159388
- const stopHeartbeat = startLockHeartbeat(lockPath);
159389
- try {
159390
- const MAX_ATTEMPTS = 3;
159391
- let lastError;
159392
- for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
159393
- try {
159394
- const pipeline = await withQuietConsole(() => createPipeline("feature-extraction", this.model, {
159395
- dtype: "fp32"
159396
- }));
159397
- if (this.disposing) {
159398
- await pipeline.dispose?.();
159399
- this.pipeline = null;
159400
- } else {
159401
- this.pipeline = pipeline;
159402
- }
159403
- lastError = undefined;
159404
- break;
159405
- } catch (error51) {
159406
- lastError = error51;
159407
- if (!isTransientLoadError(error51) || attempt === MAX_ATTEMPTS) {
159408
- break;
159409
- }
159410
- const delayMs = 300 * attempt + Math.floor(Math.random() * 200);
159411
- log(`[magic-context] embedding model load attempt ${attempt}/${MAX_ATTEMPTS} failed transiently, retrying in ${delayMs}ms`);
159412
- await new Promise((resolve3) => setTimeout(resolve3, delayMs));
159413
- }
159414
- }
159415
- if (this.pipeline) {
159416
- log(`[magic-context] embedding model loaded: ${this.model}`);
159417
- } else if (this.disposing) {
159418
- return;
159419
- } else {
159420
- throw lastError ?? new Error("unknown embedding load failure");
159421
- }
159422
- } finally {
159423
- stopHeartbeat();
159424
- await releaseLock();
159425
- }
159426
- } catch (error51) {
159427
- log("[magic-context] embedding model failed to load:", error51);
159428
- this.pipeline = null;
159429
- } finally {
159430
- this.initPromise = null;
159431
- }
159432
- })();
159433
- await this.initPromise;
159434
- return this.pipeline !== null;
159435
- }
159436
- waitForInFlightToDrain() {
159437
- if (this.inFlight === 0) {
159438
- return Promise.resolve();
159439
- }
159440
- return new Promise((resolve3) => {
159441
- this.inFlightWaiters.push(resolve3);
159442
- });
159443
- }
159444
- finishInFlight() {
159445
- this.inFlight = Math.max(0, this.inFlight - 1);
159446
- if (this.inFlight !== 0)
159447
- return;
159448
- const waiters = this.inFlightWaiters.splice(0);
159449
- for (const waiter of waiters) {
159450
- waiter();
159451
- }
159452
- }
159453
- async embed(text, signal) {
159454
- if (signal?.aborted)
159455
- return null;
159456
- if (this.disposing)
159457
- return null;
159458
- this.inFlight += 1;
159459
- try {
159460
- if (!await this.initialize()) {
159461
- return null;
159462
- }
159463
- const pipeline = this.pipeline;
159464
- if (!pipeline) {
159465
- return null;
159466
- }
159467
- const result = await withQuietConsole(() => pipeline(text, {
159468
- pooling: "mean",
159469
- normalize: true
159470
- }));
159471
- return extractBatchEmbeddings(result, 1)[0] ?? null;
159472
- } catch (error51) {
159473
- log("[magic-context] embedding failed:", error51);
159474
- return null;
159475
- } finally {
159476
- this.finishInFlight();
159477
- }
159478
- }
159479
- async embedBatch(texts, signal) {
159480
- if (texts.length === 0) {
159481
- return [];
159482
- }
159483
- if (signal?.aborted) {
159484
- return Array.from({ length: texts.length }, () => null);
159485
- }
159486
- if (this.disposing) {
159487
- return Array.from({ length: texts.length }, () => null);
159488
- }
159489
- this.inFlight += 1;
159490
- try {
159491
- if (!await this.initialize()) {
159492
- return Array.from({ length: texts.length }, () => null);
159493
- }
159494
- const pipeline = this.pipeline;
159495
- if (!pipeline) {
159496
- return Array.from({ length: texts.length }, () => null);
159497
- }
159498
- const result = await withQuietConsole(() => pipeline(texts, {
159499
- pooling: "mean",
159500
- normalize: true
159501
- }));
159502
- return extractBatchEmbeddings(result, texts.length);
159503
- } catch (error51) {
159504
- log("[magic-context] embedding batch failed:", error51);
159505
- return Array.from({ length: texts.length }, () => null);
159506
- } finally {
159507
- this.finishInFlight();
159508
- }
159509
- }
159510
- async dispose() {
159511
- if (this.disposePromise) {
159512
- return this.disposePromise;
159513
- }
159514
- this.disposing = true;
159515
- this.disposePromise = (async () => {
159516
- if (this.initPromise) {
159517
- await this.initPromise;
159518
- }
159519
- await this.waitForInFlightToDrain();
159520
- const pipelineToDispose = this.pipeline;
159521
- this.pipeline = null;
159522
- this.initPromise = null;
159523
- if (!pipelineToDispose) {
159524
- return;
159525
- }
159526
- try {
159527
- await pipelineToDispose.dispose?.();
159528
- } catch (error51) {
159529
- log("[magic-context] embedding model dispose failed:", error51);
159530
- }
159531
- })();
159532
- return this.disposePromise;
159533
- }
159534
- isLoaded() {
159535
- return this.pipeline !== null;
159300
+ } : null;
159301
+ if (!identityInput) {
159302
+ return "embedding-provider:off";
159536
159303
  }
159304
+ return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
159537
159305
  }
159538
159306
 
159539
159307
  // ../plugin/src/features/magic-context/memory/embedding-openai.ts
@@ -159615,6 +159383,7 @@ class OpenAICompatibleEmbeddingProvider {
159615
159383
  model;
159616
159384
  apiKey;
159617
159385
  inputType;
159386
+ queryInputType;
159618
159387
  truncate;
159619
159388
  initialized = false;
159620
159389
  failureTimes = [];
@@ -159627,6 +159396,7 @@ class OpenAICompatibleEmbeddingProvider {
159627
159396
  this.model = options.model?.trim() ?? "";
159628
159397
  this.apiKey = options.apiKey?.trim() ?? "";
159629
159398
  this.inputType = options.inputType?.trim() ?? "";
159399
+ this.queryInputType = options.queryInputType?.trim() ?? "";
159630
159400
  this.truncate = options.truncate?.trim() ?? "";
159631
159401
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
159632
159402
  this.modelId = getEmbeddingProviderIdentity({
@@ -159654,11 +159424,17 @@ class OpenAICompatibleEmbeddingProvider {
159654
159424
  this.initialized = true;
159655
159425
  return true;
159656
159426
  }
159657
- async embed(text, signal) {
159658
- const [embedding] = await this.embedBatch([text], signal);
159427
+ resolveInputTypeForPurpose(purpose = "passage") {
159428
+ if (purpose === "query") {
159429
+ return this.queryInputType || this.inputType;
159430
+ }
159431
+ return this.inputType;
159432
+ }
159433
+ async embed(text, signal, purpose) {
159434
+ const [embedding] = await this.embedBatch([text], signal, purpose);
159659
159435
  return embedding ?? null;
159660
159436
  }
159661
- async embedBatch(texts, signal) {
159437
+ async embedBatch(texts, signal, purpose) {
159662
159438
  if (texts.length === 0) {
159663
159439
  return [];
159664
159440
  }
@@ -159684,6 +159460,7 @@ class OpenAICompatibleEmbeddingProvider {
159684
159460
  if (signal) {
159685
159461
  signal.addEventListener("abort", onOuterAbort, { once: true });
159686
159462
  }
159463
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
159687
159464
  const response = await fetch(`${this.endpoint}/embeddings`, {
159688
159465
  method: "POST",
159689
159466
  headers: {
@@ -159693,7 +159470,7 @@ class OpenAICompatibleEmbeddingProvider {
159693
159470
  body: JSON.stringify({
159694
159471
  model: this.model,
159695
159472
  input: texts,
159696
- ...this.inputType ? { input_type: this.inputType } : {},
159473
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
159697
159474
  ...this.truncate ? { truncate: this.truncate } : {}
159698
159475
  }),
159699
159476
  redirect: "error",
@@ -159849,7 +159626,7 @@ function isEmbeddingRow(row) {
159849
159626
  const candidate = row;
159850
159627
  return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding) && (candidate.modelId === null || typeof candidate.modelId === "string");
159851
159628
  }
159852
- function toFloat32Array3(blob) {
159629
+ function toFloat32Array2(blob) {
159853
159630
  if (blob instanceof Uint8Array) {
159854
159631
  const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
159855
159632
  return new Float32Array(buffer2);
@@ -159897,7 +159674,7 @@ function loadAllEmbeddings(db, projectPath) {
159897
159674
  const embeddings = new Map;
159898
159675
  for (const row of rows) {
159899
159676
  embeddings.set(row.memoryId, {
159900
- embedding: toFloat32Array3(row.embedding),
159677
+ embedding: toFloat32Array2(row.embedding),
159901
159678
  modelId: row.modelId
159902
159679
  });
159903
159680
  }
@@ -159910,10 +159687,24 @@ function getDistinctStoredModelIds(db, projectPath) {
159910
159687
  const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
159911
159688
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
159912
159689
  }
159690
+ function getMemoryEmbedCoverage(db, projectPath, modelId) {
159691
+ const row = db.prepare(`SELECT
159692
+ COUNT(*) AS total,
159693
+ SUM(CASE WHEN EXISTS (
159694
+ SELECT 1 FROM memory_embeddings e
159695
+ WHERE e.memory_id = m.id AND e.model_id = ?
159696
+ ) THEN 1 ELSE 0 END) AS embedded
159697
+ FROM memories m
159698
+ WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
159699
+ return {
159700
+ total: typeof row?.total === "number" ? row.total : 0,
159701
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
159702
+ };
159703
+ }
159913
159704
 
159914
159705
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
159915
- import { createHash as createHash6, randomUUID } from "node:crypto";
159916
159706
  init_logger();
159707
+ import { createHash as createHash6, randomUUID } from "node:crypto";
159917
159708
 
159918
159709
  // ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
159919
159710
  var saveStatements = new WeakMap;
@@ -160016,6 +159807,118 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
160016
159807
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
160017
159808
  }
160018
159809
 
159810
+ // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
159811
+ init_logger();
159812
+ var insertStatements = new WeakMap;
159813
+ var existingShasStatements = new WeakMap;
159814
+ var projectCountStatements = new WeakMap;
159815
+ var evictStatements = new WeakMap;
159816
+ var evictOverflowStatements = new WeakMap;
159817
+ var latestCommitTimeStatements = new WeakMap;
159818
+ function getInsertStatement(db) {
159819
+ let stmt = insertStatements.get(db);
159820
+ if (!stmt) {
159821
+ stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
159822
+ VALUES (?, ?, ?, ?, ?, ?, ?)
159823
+ ON CONFLICT(sha) DO UPDATE SET
159824
+ project_path = excluded.project_path,
159825
+ short_sha = excluded.short_sha,
159826
+ message = excluded.message,
159827
+ author = excluded.author,
159828
+ committed_at = excluded.committed_at,
159829
+ indexed_at = excluded.indexed_at
159830
+ WHERE git_commits.message != excluded.message`);
159831
+ insertStatements.set(db, stmt);
159832
+ }
159833
+ return stmt;
159834
+ }
159835
+ function getExistingShasStatement(db) {
159836
+ let stmt = existingShasStatements.get(db);
159837
+ if (!stmt) {
159838
+ stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
159839
+ existingShasStatements.set(db, stmt);
159840
+ }
159841
+ return stmt;
159842
+ }
159843
+ function getProjectCountStatement(db) {
159844
+ let stmt = projectCountStatements.get(db);
159845
+ if (!stmt) {
159846
+ stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
159847
+ projectCountStatements.set(db, stmt);
159848
+ }
159849
+ return stmt;
159850
+ }
159851
+ function getLatestCommitTimeStatement(db) {
159852
+ let stmt = latestCommitTimeStatements.get(db);
159853
+ if (!stmt) {
159854
+ stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
159855
+ latestCommitTimeStatements.set(db, stmt);
159856
+ }
159857
+ return stmt;
159858
+ }
159859
+ function getEvictOverflowStatement(db) {
159860
+ let stmt = evictOverflowStatements.get(db);
159861
+ if (!stmt) {
159862
+ stmt = db.prepare(`DELETE FROM git_commits
159863
+ WHERE rowid IN (
159864
+ SELECT rowid FROM git_commits
159865
+ WHERE project_path = ?
159866
+ ORDER BY committed_at DESC, sha DESC
159867
+ LIMIT -1 OFFSET ?
159868
+ )`);
159869
+ evictOverflowStatements.set(db, stmt);
159870
+ }
159871
+ return stmt;
159872
+ }
159873
+ function upsertCommits(db, projectPath, commits) {
159874
+ if (commits.length === 0)
159875
+ return { inserted: 0, updated: 0 };
159876
+ const existing = new Set;
159877
+ for (const row of getExistingShasStatement(db).all(projectPath)) {
159878
+ existing.add(row.sha);
159879
+ }
159880
+ let inserted = 0;
159881
+ let updated = 0;
159882
+ const now = Date.now();
159883
+ const insertStmt = getInsertStatement(db);
159884
+ db.transaction(() => {
159885
+ for (const commit of commits) {
159886
+ const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
159887
+ if (result.changes > 0) {
159888
+ if (existing.has(commit.sha)) {
159889
+ updated++;
159890
+ } else {
159891
+ inserted++;
159892
+ existing.add(commit.sha);
159893
+ }
159894
+ }
159895
+ }
159896
+ })();
159897
+ return { inserted, updated };
159898
+ }
159899
+ function getCommitCount(db, projectPath) {
159900
+ const row = getProjectCountStatement(db).get(projectPath);
159901
+ return row?.count ?? 0;
159902
+ }
159903
+ function getLatestIndexedCommitTimeMs(db, projectPath) {
159904
+ const row = getLatestCommitTimeStatement(db).get(projectPath);
159905
+ return row?.latest ?? null;
159906
+ }
159907
+ function enforceProjectCap(db, projectPath, maxCommits) {
159908
+ if (maxCommits <= 0)
159909
+ return 0;
159910
+ const count = getCommitCount(db, projectPath);
159911
+ if (count <= maxCommits)
159912
+ return 0;
159913
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
159914
+ const after = getCommitCount(db, projectPath);
159915
+ const evicted = Math.max(0, count - after);
159916
+ if (evicted > 0) {
159917
+ log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
159918
+ }
159919
+ return evicted;
159920
+ }
159921
+
160019
159922
  // ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
160020
159923
  var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
160021
159924
  var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
@@ -160248,25 +160151,24 @@ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
160248
160151
  var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
160249
160152
  var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
160250
160153
  var CHUNK_DRAIN_BATCH_SIZE = 8;
160251
- var MAX_WINDOWS_PER_EMBED_CALL = 16;
160154
+ var MAX_WINDOWS_PER_EMBED_CALL = 2;
160252
160155
  var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
160156
+ var EMBED_SLICE_RETRY_ATTEMPTS = 3;
160157
+ var EMBED_SLICE_RETRY_BASE_MS = 250;
160158
+ var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
160159
+ var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
160253
160160
  var projectRegistrations = new Map;
160254
160161
  var loadUnembeddedMemoriesStatements = new WeakMap;
160255
160162
  var globalRegistrationGeneration = 0;
160256
160163
  var testProviderFactory = null;
160257
160164
  function resolveEmbeddingConfig(config2) {
160258
- if (!config2 || config2.provider === "local") {
160259
- return {
160260
- provider: "local",
160261
- model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
160262
- ...config2?.max_input_tokens ? {
160263
- max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
160264
- } : {}
160265
- };
160165
+ if (!config2 || config2.provider === "off") {
160166
+ return { provider: "off" };
160266
160167
  }
160267
160168
  if (config2.provider === "openai-compatible") {
160268
160169
  const apiKey = config2.api_key?.trim();
160269
160170
  const inputType = config2.input_type?.trim();
160171
+ const queryInputType = config2.query_input_type?.trim();
160270
160172
  const truncate = config2.truncate?.trim();
160271
160173
  return {
160272
160174
  provider: "openai-compatible",
@@ -160274,6 +160176,7 @@ function resolveEmbeddingConfig(config2) {
160274
160176
  endpoint: config2.endpoint.trim(),
160275
160177
  ...apiKey ? { api_key: apiKey } : {},
160276
160178
  ...inputType ? { input_type: inputType } : {},
160179
+ ...queryInputType ? { query_input_type: queryInputType } : {},
160277
160180
  ...truncate ? { truncate } : {},
160278
160181
  ...config2.max_input_tokens ? {
160279
160182
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -160295,11 +160198,12 @@ function createProvider(config2) {
160295
160198
  model: config2.model,
160296
160199
  apiKey: config2.api_key,
160297
160200
  inputType: config2.input_type,
160201
+ queryInputType: config2.query_input_type,
160298
160202
  truncate: config2.truncate,
160299
160203
  maxInputTokens: config2.max_input_tokens
160300
160204
  });
160301
160205
  }
160302
- return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
160206
+ return null;
160303
160207
  }
160304
160208
  function stableStringify2(value) {
160305
160209
  if (Array.isArray(value)) {
@@ -160349,7 +160253,9 @@ function snapshotFor(registration) {
160349
160253
  enabled,
160350
160254
  gitCommitEnabled,
160351
160255
  modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
160352
- chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
160256
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
160257
+ model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
160258
+ provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "off"
160353
160259
  };
160354
160260
  }
160355
160261
  function disposeProvider(provider) {
@@ -160475,7 +160381,7 @@ function getOrCreateProjectProvider(registration) {
160475
160381
  registration.provider = provider;
160476
160382
  return provider;
160477
160383
  }
160478
- async function embedTextForProject(projectIdentity, text, signal) {
160384
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
160479
160385
  const registration = projectRegistrations.get(projectIdentity);
160480
160386
  if (!registration)
160481
160387
  return null;
@@ -160484,7 +160390,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
160484
160390
  const provider = getOrCreateProjectProvider(registration);
160485
160391
  if (!provider)
160486
160392
  return null;
160487
- const vector = await provider.embed(text, signal);
160393
+ const vector = await provider.embed(text, signal, purpose);
160488
160394
  if (!vector)
160489
160395
  return null;
160490
160396
  const current = projectRegistrations.get(projectIdentity);
@@ -160493,7 +160399,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
160493
160399
  }
160494
160400
  return { vector, modelId, generation };
160495
160401
  }
160496
- async function embedBatchForProject(projectIdentity, texts, signal) {
160402
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
160497
160403
  if (texts.length === 0) {
160498
160404
  const registration2 = projectRegistrations.get(projectIdentity);
160499
160405
  if (!registration2 || registration2.observationMode)
@@ -160509,7 +160415,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
160509
160415
  const provider = getOrCreateProjectProvider(registration);
160510
160416
  if (!provider)
160511
160417
  return null;
160512
- const vectors = await provider.embedBatch(texts, signal);
160418
+ const vectors = await provider.embedBatch(texts, signal, purpose);
160513
160419
  const current = projectRegistrations.get(projectIdentity);
160514
160420
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
160515
160421
  return null;
@@ -160560,12 +160466,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
160560
160466
  }
160561
160467
  async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
160562
160468
  const noWork = [];
160469
+ const failed = [];
160563
160470
  if (candidates.length === 0)
160564
- return { embedded: 0, noWork };
160471
+ return { embedded: 0, noWork, failed };
160565
160472
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
160566
160473
  const prepared = [];
160567
160474
  for (const candidate of candidates) {
160568
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
160475
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
160569
160476
  if (canonicalText.length === 0) {
160570
160477
  noWork.push(candidate.id);
160571
160478
  continue;
@@ -160578,7 +160485,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
160578
160485
  prepared.push({ candidate, windows });
160579
160486
  }
160580
160487
  if (prepared.length === 0)
160581
- return { embedded: 0, noWork };
160488
+ return { embedded: 0, noWork, failed };
160582
160489
  let embedded = 0;
160583
160490
  let i = 0;
160584
160491
  while (i < prepared.length) {
@@ -160595,35 +160502,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
160595
160502
  const texts = [];
160596
160503
  for (const item of slice)
160597
160504
  texts.push(...item.windows.map((w) => w.text));
160598
- try {
160599
- const result = await embedBatchForProject(projectIdentity, texts, signal);
160600
- if (!result)
160601
- continue;
160505
+ const persistedIds = new Set;
160506
+ for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
160602
160507
  if (signal?.aborted)
160603
160508
  break;
160604
- let offset = 0;
160605
- for (const item of slice) {
160606
- const vectors = result.vectors.slice(offset, offset + item.windows.length);
160607
- offset += item.windows.length;
160608
- if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
160609
- continue;
160509
+ let result = null;
160510
+ const attemptStart = Date.now();
160511
+ try {
160512
+ result = await embedBatchForProject(projectIdentity, texts, signal);
160513
+ } catch (error51) {
160514
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
160515
+ }
160516
+ if (signal?.aborted)
160517
+ break;
160518
+ if (result) {
160519
+ let offset = 0;
160520
+ for (const item of slice) {
160521
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
160522
+ offset += item.windows.length;
160523
+ if (persistedIds.has(item.candidate.id))
160524
+ continue;
160525
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
160526
+ continue;
160527
+ }
160528
+ const rows = item.windows.map((window, index) => ({
160529
+ compartmentId: item.candidate.id,
160530
+ sessionId: item.candidate.sessionId,
160531
+ projectPath: projectIdentity,
160532
+ window,
160533
+ modelId,
160534
+ vector: vectors[index]
160535
+ }));
160536
+ replaceCompartmentChunkEmbeddings(db, rows);
160537
+ persistedIds.add(item.candidate.id);
160610
160538
  }
160611
- const rows = item.windows.map((window, index) => ({
160612
- compartmentId: item.candidate.id,
160613
- sessionId: item.candidate.sessionId,
160614
- projectPath: projectIdentity,
160615
- window,
160616
- modelId,
160617
- vector: vectors[index]
160618
- }));
160619
- replaceCompartmentChunkEmbeddings(db, rows);
160620
- embedded += 1;
160621
160539
  }
160622
- } catch (error51) {
160623
- log("[magic-context] failed to proactively embed compartment chunks:", error51);
160540
+ if (persistedIds.size === slice.length)
160541
+ break;
160542
+ if (persistedIds.size > 0)
160543
+ break;
160544
+ if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
160545
+ break;
160546
+ if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
160547
+ await new Promise((resolve3) => setTimeout(resolve3, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
160548
+ }
160549
+ }
160550
+ embedded += persistedIds.size;
160551
+ if (!signal?.aborted) {
160552
+ for (const item of slice) {
160553
+ if (!persistedIds.has(item.candidate.id))
160554
+ failed.push(item.candidate.id);
160555
+ }
160624
160556
  }
160625
160557
  }
160626
- return { embedded, noWork };
160558
+ return { embedded, noWork, failed };
160627
160559
  }
160628
160560
  async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
160629
160561
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
@@ -160646,9 +160578,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
160646
160578
  renewal.unref?.();
160647
160579
  const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
160648
160580
  const skipIds = [];
160581
+ const failedIds = [];
160649
160582
  let embedded = 0;
160650
160583
  let aborted2 = false;
160651
- let providerStalled = false;
160584
+ let providerDown = false;
160585
+ let consecutiveFailedBatches = 0;
160652
160586
  try {
160653
160587
  options?.onProgress?.({ embedded, total });
160654
160588
  for (;; ) {
@@ -160656,15 +160590,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
160656
160590
  aborted2 = true;
160657
160591
  break;
160658
160592
  }
160659
- const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
160593
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
160660
160594
  if (candidates.length === 0)
160661
160595
  break;
160662
- const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
160596
+ const {
160597
+ embedded: n,
160598
+ noWork,
160599
+ failed
160600
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
160663
160601
  for (const id of noWork)
160664
160602
  skipIds.push(id);
160603
+ for (const id of failed)
160604
+ failedIds.push(id);
160665
160605
  if (n === 0 && noWork.length === 0) {
160666
- providerStalled = true;
160667
- break;
160606
+ consecutiveFailedBatches += 1;
160607
+ if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
160608
+ providerDown = true;
160609
+ break;
160610
+ }
160611
+ } else {
160612
+ consecutiveFailedBatches = 0;
160668
160613
  }
160669
160614
  embedded += n;
160670
160615
  options?.onProgress?.({ embedded: Math.min(embedded, total), total });
@@ -160672,22 +160617,55 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
160672
160617
  }
160673
160618
  } finally {
160674
160619
  clearInterval(renewal);
160675
- releaseGitSweepLease(db, projectIdentity, holderId);
160620
+ try {
160621
+ releaseGitSweepLease(db, projectIdentity, holderId);
160622
+ } catch (error51) {
160623
+ log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
160624
+ }
160676
160625
  }
160677
160626
  if (aborted2)
160678
- return { status: "aborted", embedded, total };
160679
- if (providerStalled) {
160627
+ return { status: "aborted", embedded, total, failed: failedIds.length };
160628
+ if (providerDown || failedIds.length > 0) {
160680
160629
  const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
160681
- if (remaining > 0)
160682
- return { status: "stalled", embedded, total, remaining };
160630
+ if (remaining > 0) {
160631
+ return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
160632
+ }
160633
+ }
160634
+ return { status: "done", embedded, total, failed: failedIds.length };
160635
+ }
160636
+ function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
160637
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
160638
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
160639
+ return {
160640
+ enabled: false,
160641
+ model: snapshot?.model ?? "off",
160642
+ provider: snapshot?.provider ?? "off",
160643
+ session: { embedded: 0, total: 0 },
160644
+ memories: { embedded: 0, total: 0 },
160645
+ commits: { embedded: 0, total: 0, gitEnabled: false }
160646
+ };
160683
160647
  }
160684
- return { status: "done", embedded, total };
160648
+ const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
160649
+ const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
160650
+ const gitEnabled = snapshot.gitCommitEnabled;
160651
+ const commits = gitEnabled ? {
160652
+ embedded: countEmbeddedCommits(db, projectIdentity),
160653
+ total: getCommitCount(db, projectIdentity),
160654
+ gitEnabled: true
160655
+ } : { embedded: 0, total: 0, gitEnabled: false };
160656
+ return {
160657
+ enabled: true,
160658
+ model: snapshot.model,
160659
+ provider: snapshot.provider,
160660
+ session,
160661
+ memories,
160662
+ commits
160663
+ };
160685
160664
  }
160686
160665
 
160687
160666
  // ../plugin/src/features/magic-context/memory/embedding.ts
160688
160667
  var DEFAULT_EMBEDDING_CONFIG = {
160689
- provider: "local",
160690
- model: DEFAULT_LOCAL_EMBEDDING_MODEL
160668
+ provider: "off"
160691
160669
  };
160692
160670
  var embeddingConfig = DEFAULT_EMBEDDING_CONFIG;
160693
160671
  var provider = null;
@@ -160702,11 +160680,12 @@ function createProvider2(config2) {
160702
160680
  model: config2.model,
160703
160681
  apiKey: config2.api_key,
160704
160682
  inputType: config2.input_type,
160683
+ queryInputType: config2.query_input_type,
160705
160684
  truncate: config2.truncate,
160706
160685
  maxInputTokens: config2.max_input_tokens
160707
160686
  });
160708
160687
  }
160709
- return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
160688
+ return null;
160710
160689
  }
160711
160690
  function getOrCreateProvider() {
160712
160691
  if (provider) {
@@ -160910,6 +160889,7 @@ var SESSION_META_SELECT_COLUMNS = [
160910
160889
  "conversation_tokens",
160911
160890
  "tool_call_tokens",
160912
160891
  "cleared_reasoning_through_tag",
160892
+ "tool_reclaim_watermark",
160913
160893
  "last_todo_state",
160914
160894
  "cached_m0_bytes",
160915
160895
  "cached_m1_bytes",
@@ -160936,6 +160916,8 @@ var SESSION_META_SELECT_COLUMNS = [
160936
160916
  "recovery_no_eligible_head_count",
160937
160917
  "force_emergency_bypass_window_start",
160938
160918
  "force_emergency_bypass_used",
160919
+ "emergency_drain_active",
160920
+ "historian_drain_failure_at",
160939
160921
  "upgrade_reminded_at",
160940
160922
  "pi_stable_id_scheme"
160941
160923
  ];
@@ -160958,6 +160940,7 @@ var META_COLUMNS = {
160958
160940
  conversationTokens: "conversation_tokens",
160959
160941
  toolCallTokens: "tool_call_tokens",
160960
160942
  clearedReasoningThroughTag: "cleared_reasoning_through_tag",
160943
+ toolReclaimWatermark: "tool_reclaim_watermark",
160961
160944
  lastTodoState: "last_todo_state",
160962
160945
  cachedM0Bytes: "cached_m0_bytes",
160963
160946
  cachedM1Bytes: "cached_m1_bytes",
@@ -160984,6 +160967,8 @@ var META_COLUMNS = {
160984
160967
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
160985
160968
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
160986
160969
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
160970
+ emergencyDrainActive: "emergency_drain_active",
160971
+ historianDrainFailureAt: "historian_drain_failure_at",
160987
160972
  upgradeRemindedAt: "upgrade_reminded_at",
160988
160973
  piStableIdScheme: "pi_stable_id_scheme"
160989
160974
  };
@@ -161026,7 +161011,7 @@ function isSessionMetaRow(row) {
161026
161011
  if (row === null || typeof row !== "object")
161027
161012
  return false;
161028
161013
  const r = row;
161029
- return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
161014
+ return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme) && isNumberOrNull(r.tool_reclaim_watermark);
161030
161015
  }
161031
161016
  function getDefaultSessionMeta(sessionId) {
161032
161017
  return {
@@ -161049,6 +161034,7 @@ function getDefaultSessionMeta(sessionId) {
161049
161034
  conversationTokens: 0,
161050
161035
  toolCallTokens: 0,
161051
161036
  clearedReasoningThroughTag: 0,
161037
+ toolReclaimWatermark: 0,
161052
161038
  lastTodoState: "",
161053
161039
  cachedM0Bytes: null,
161054
161040
  cachedM1Bytes: null,
@@ -161112,6 +161098,7 @@ function toSessionMeta(row) {
161112
161098
  conversationTokens: numOrZero(row.conversation_tokens),
161113
161099
  toolCallTokens: numOrZero(row.tool_call_tokens),
161114
161100
  clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
161101
+ toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
161115
161102
  lastTodoState: lastTodoStateRaw,
161116
161103
  cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
161117
161104
  cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
@@ -161369,9 +161356,9 @@ function escapeXmlContent(s) {
161369
161356
  init_data_path();
161370
161357
  init_logger();
161371
161358
  import { existsSync as existsSync6 } from "node:fs";
161372
- import { join as join7 } from "node:path";
161359
+ import { join as join6 } from "node:path";
161373
161360
  function getOpenCodeDbPath() {
161374
- return join7(getDataDir(), "opencode", "opencode.db");
161361
+ return join6(getDataDir(), "opencode", "opencode.db");
161375
161362
  }
161376
161363
  function openCodeDbExists() {
161377
161364
  return existsSync6(getOpenCodeDbPath());
@@ -161538,6 +161525,8 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
161538
161525
  var encoder = new TextEncoder;
161539
161526
  var TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
161540
161527
  var MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
161528
+ var DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
161529
+ var DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
161541
161530
  var COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
161542
161531
  var MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
161543
161532
  var STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
@@ -161550,6 +161539,9 @@ function stripCompleteTagPairsGlobally(value) {
161550
161539
  function stripMalformedTagNotationGlobally(value) {
161551
161540
  return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
161552
161541
  }
161542
+ function stripDanglingTagNotationGlobally(value) {
161543
+ return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
161544
+ }
161553
161545
  function stripTagSectionCharacters(value) {
161554
161546
  return value.replace(STRAY_SECTION_CHAR_REGEX, "");
161555
161547
  }
@@ -161557,6 +161549,7 @@ function stripPersistedAssistantText(value) {
161557
161549
  let text = stripWellFormedLeadingTagPrefix(value);
161558
161550
  text = stripCompleteTagPairsGlobally(text);
161559
161551
  text = stripMalformedTagNotationGlobally(text);
161552
+ text = stripDanglingTagNotationGlobally(text);
161560
161553
  text = stripTagSectionCharacters(text);
161561
161554
  return text.trim();
161562
161555
  }
@@ -161569,6 +161562,7 @@ function stripTagPrefix(value) {
161569
161562
  const prev = stripped;
161570
161563
  stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
161571
161564
  stripped = stripped.replace(TAG_PREFIX_REGEX, "");
161565
+ stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
161572
161566
  if (stripped === prev)
161573
161567
  break;
161574
161568
  }
@@ -161676,6 +161670,24 @@ class ToolMutationBatch {
161676
161670
  }
161677
161671
 
161678
161672
  // ../plugin/src/hooks/magic-context/read-session-chunk.ts
161673
+ var BLOCK_TOKEN_MEMO_MAX = 2048;
161674
+ var blockTokenMemo = new Map;
161675
+ function estimateBlockTokens(blockText) {
161676
+ const cached2 = blockTokenMemo.get(blockText);
161677
+ if (cached2 !== undefined) {
161678
+ blockTokenMemo.delete(blockText);
161679
+ blockTokenMemo.set(blockText, cached2);
161680
+ return cached2;
161681
+ }
161682
+ const count = estimateTokens(blockText);
161683
+ if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
161684
+ const oldest = blockTokenMemo.keys().next().value;
161685
+ if (oldest !== undefined)
161686
+ blockTokenMemo.delete(oldest);
161687
+ }
161688
+ blockTokenMemo.set(blockText, count);
161689
+ return count;
161690
+ }
161679
161691
  var activeRawMessageCache = null;
161680
161692
  var activeAbsoluteCountCache = null;
161681
161693
  var sessionProviders = new Map;
@@ -161858,7 +161870,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
161858
161870
  if (!currentBlock)
161859
161871
  return true;
161860
161872
  const blockText = formatBlock(currentBlock);
161861
- const blockTokens = estimateTokens(blockText);
161873
+ const blockTokens = estimateBlockTokens(blockText);
161862
161874
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
161863
161875
  return false;
161864
161876
  }
@@ -161984,9 +161996,184 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
161984
161996
 
161985
161997
  ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
161986
161998
 
161987
- Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.`;
161999
+ Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
162000
+
162001
+ Two recovery modes for finer detail:
162002
+ - ctx_expand(start=120, end=245, verbose=true) — lists each message SEPARATELY with its ordinal [N] and a per-part preview (each tool call shown with its output size). Use this to find the exact message or tool call you want, then recover it in full by ordinal.
162003
+ - ctx_expand(message=138) — returns the FULL untruncated content of the message at that ordinal: every text part, and every tool call's complete input + output, read from stored history. This is the cheap way to get back a tool output you dropped with ctx_reduce — the original is still in storage even though the wire shows [dropped §N§]. If the message was deleted from history (session prune/revert), it says so.`;
161988
162004
  var CTX_EXPAND_TOKEN_BUDGET = 15000;
161989
162005
 
162006
+ // ../plugin/src/tools/ctx-expand/render.ts
162007
+ function isRecord2(value) {
162008
+ return value !== null && typeof value === "object" && !Array.isArray(value);
162009
+ }
162010
+ function roleLabel(role) {
162011
+ if (role === "assistant")
162012
+ return "A (assistant)";
162013
+ if (role === "user")
162014
+ return "U (user)";
162015
+ return role;
162016
+ }
162017
+ function truncate(value, max) {
162018
+ const t = value.trim();
162019
+ return t.length <= max ? t : `${t.slice(0, max)}…`;
162020
+ }
162021
+ function keyArg(input) {
162022
+ if (!input)
162023
+ return "";
162024
+ for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
162025
+ const v = input[k];
162026
+ if (typeof v === "string" && v.length > 0)
162027
+ return truncate(v, 60);
162028
+ }
162029
+ if (typeof input.description === "string")
162030
+ return truncate(input.description, 60);
162031
+ return "";
162032
+ }
162033
+ function asToolPart(part) {
162034
+ const type = typeof part.type === "string" ? part.type : "";
162035
+ if (type === "tool") {
162036
+ const state = isRecord2(part.state) ? part.state : null;
162037
+ const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
162038
+ const metadata = state && isRecord2(state.metadata) ? state.metadata : null;
162039
+ const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
162040
+ return {
162041
+ name: typeof part.tool === "string" ? part.tool : "tool",
162042
+ callId: typeof part.callID === "string" ? part.callID : "",
162043
+ title,
162044
+ input: state && isRecord2(state.input) ? state.input : null,
162045
+ output
162046
+ };
162047
+ }
162048
+ if (type === "tool_use") {
162049
+ return {
162050
+ name: typeof part.name === "string" ? part.name : "tool",
162051
+ callId: typeof part.id === "string" ? part.id : "",
162052
+ title: null,
162053
+ input: isRecord2(part.input) ? part.input : null,
162054
+ output: null
162055
+ };
162056
+ }
162057
+ if (type === "tool_result") {
162058
+ const content = part.content;
162059
+ const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
162060
+ return {
162061
+ name: "tool_result",
162062
+ callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
162063
+ title: null,
162064
+ input: null,
162065
+ output
162066
+ };
162067
+ }
162068
+ return null;
162069
+ }
162070
+ function textOf(part) {
162071
+ if (part.type === "text" && typeof part.text === "string")
162072
+ return part.text;
162073
+ return null;
162074
+ }
162075
+ function reasoningOf(part) {
162076
+ if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
162077
+ return part.text;
162078
+ }
162079
+ return null;
162080
+ }
162081
+ function renderPartPreview(part) {
162082
+ if (!isRecord2(part))
162083
+ return null;
162084
+ const text = textOf(part);
162085
+ if (text !== null) {
162086
+ const t = truncate(text, 200);
162087
+ return t.length > 0 ? ` • ${t}` : null;
162088
+ }
162089
+ const tool = asToolPart(part);
162090
+ if (tool) {
162091
+ const arg = keyArg(tool.input);
162092
+ const head = arg ? `${tool.name}(${arg})` : tool.name;
162093
+ return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
162094
+ }
162095
+ const reasoning = reasoningOf(part);
162096
+ if (reasoning !== null)
162097
+ return ` • [reasoning] ${truncate(reasoning, 120)}`;
162098
+ const type = typeof part.type === "string" ? part.type : "part";
162099
+ if (type === "file")
162100
+ return " • [file]";
162101
+ if (type === "step-start" || type === "step-finish")
162102
+ return null;
162103
+ return ` • [${type}]`;
162104
+ }
162105
+ function renderPartFull(part) {
162106
+ if (!isRecord2(part))
162107
+ return null;
162108
+ const text = textOf(part);
162109
+ if (text !== null) {
162110
+ return text.trim().length > 0 ? ` [text]
162111
+ ${text}` : null;
162112
+ }
162113
+ const tool = asToolPart(part);
162114
+ if (tool) {
162115
+ const lines = [];
162116
+ const idSuffix = tool.callId ? ` #${tool.callId}` : "";
162117
+ lines.push(` [tool: ${tool.name}${idSuffix}]`);
162118
+ if (tool.title && tool.title.trim().length > 0) {
162119
+ lines.push(` description: ${tool.title.trim()}`);
162120
+ }
162121
+ if (tool.input)
162122
+ lines.push(` input: ${JSON.stringify(tool.input)}`);
162123
+ if (tool.output !== null)
162124
+ lines.push(` output:
162125
+ ${tool.output}`);
162126
+ return lines.join(`
162127
+ `);
162128
+ }
162129
+ const type = typeof part.type === "string" ? part.type : "part";
162130
+ if (type === "file") {
162131
+ const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
162132
+ return ` [file]${name2 ? ` ${name2}` : ""}`;
162133
+ }
162134
+ return null;
162135
+ }
162136
+ function renderMessageByOrdinal(sessionId, ordinal) {
162137
+ const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
162138
+ if (!msg) {
162139
+ return `No message at ordinal ${ordinal} in this session's stored history — it was deleted ` + `(session prune/revert) or the ordinal is wrong, so it can't be recovered. ` + `Re-run the tool if you still need the data.`;
162140
+ }
162141
+ const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
162142
+ const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
162143
+ if (rendered.length === 0) {
162144
+ lines.push(" (no recoverable content — message had only structural/reasoning parts)");
162145
+ } else {
162146
+ lines.push(...rendered);
162147
+ }
162148
+ return lines.join(`
162149
+ `);
162150
+ }
162151
+ function renderVerboseRange(sessionId, start, end, tokenBudget) {
162152
+ const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
162153
+ const out = [];
162154
+ let usedTokens = 0;
162155
+ let lastOrdinal = start - 1;
162156
+ let truncated = false;
162157
+ for (const msg of messages) {
162158
+ const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
162159
+ const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
162160
+ const block = partLines.length > 0 ? `${header}
162161
+ ${partLines.join(`
162162
+ `)}` : header;
162163
+ const blockTokens = estimateTokens(block);
162164
+ if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
162165
+ truncated = true;
162166
+ break;
162167
+ }
162168
+ out.push(block);
162169
+ usedTokens += blockTokens;
162170
+ lastOrdinal = msg.ordinal;
162171
+ }
162172
+ return { text: out.join(`
162173
+
162174
+ `), lastOrdinal, truncated };
162175
+ }
162176
+
161990
162177
  // ../../node_modules/.bun/typebox@1.1.38/node_modules/typebox/build/system/memory/memory.mjs
161991
162178
  var exports_memory = {};
161992
162179
  __export(exports_memory, {
@@ -166343,12 +166530,18 @@ function synthesizeToolResultParts(msg) {
166343
166530
 
166344
166531
  // src/tools/ctx-expand.ts
166345
166532
  var ParamsSchema = exports_typebox.Object({
166346
- start: exports_typebox.Number({
166533
+ start: exports_typebox.Optional(exports_typebox.Number({
166347
166534
  description: "Start message ordinal (from compartment start attribute)"
166348
- }),
166349
- end: exports_typebox.Number({
166535
+ })),
166536
+ end: exports_typebox.Optional(exports_typebox.Number({
166350
166537
  description: "End message ordinal (from compartment end attribute)"
166351
- })
166538
+ })),
166539
+ verbose: exports_typebox.Optional(exports_typebox.Boolean({
166540
+ description: "With start/end: list each message separately with its ordinal [N] and per-part preview, so you can recover one in full by ordinal."
166541
+ })),
166542
+ message: exports_typebox.Optional(exports_typebox.Number({
166543
+ description: "Full untruncated recovery of ONE message by its ordinal (text + every tool call's full input/output). Recovers a tool output you dropped with ctx_reduce."
166544
+ }))
166352
166545
  });
166353
166546
  function ok(text) {
166354
166547
  return { content: [{ type: "text", text }], details: undefined };
@@ -166367,22 +166560,41 @@ function createCtxExpandTool(deps) {
166367
166560
  description: CTX_EXPAND_DESCRIPTION,
166368
166561
  parameters: ParamsSchema,
166369
166562
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
166370
- if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
166371
- return err("Error: start and end must be positive integers with start <= end.");
166372
- }
166373
166563
  const sessionId = ctx.sessionManager.getSessionId();
166374
166564
  if (!sessionId) {
166375
166565
  return err("Error: no active Pi session.");
166376
166566
  }
166377
- const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
166378
- if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
166379
- return ok(`Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`);
166380
- }
166381
- const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
166382
166567
  const unregister = setRawMessageProvider(sessionId, {
166383
166568
  readMessages: () => readPiSessionMessages(ctx)
166384
166569
  });
166385
166570
  try {
166571
+ if (typeof params.message === "number" && params.message >= 1) {
166572
+ return ok(renderMessageByOrdinal(sessionId, params.message));
166573
+ }
166574
+ if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
166575
+ return err("Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).");
166576
+ }
166577
+ const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
166578
+ if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
166579
+ return ok(`Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`);
166580
+ }
166581
+ const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
166582
+ if (params.verbose === true) {
166583
+ const v = renderVerboseRange(sessionId, params.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
166584
+ if (!v.text) {
166585
+ return ok(`No messages found in range ${params.start}-${effectiveEnd}. The range may be outside this session's history.`);
166586
+ }
166587
+ const out = [
166588
+ `Messages ${params.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
166589
+ "",
166590
+ v.text
166591
+ ];
166592
+ if (v.truncated) {
166593
+ out.push("", `Truncated at message ${v.lastOrdinal} (budget: ~${CTX_EXPAND_TOKEN_BUDGET} tokens). Call again with start=${v.lastOrdinal + 1} end=${effectiveEnd} verbose=true for more.`);
166594
+ }
166595
+ return ok(out.join(`
166596
+ `));
166597
+ }
166386
166598
  const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, params.start, effectiveEnd + 1);
166387
166599
  if (!chunk.text || chunk.messageCount === 0) {
166388
166600
  return ok(`No messages found in range ${params.start}-${params.end}. The range may be outside this session's history.`);
@@ -167265,12 +167477,12 @@ function canonicalizeDocContent(raw) {
167265
167477
  }
167266
167478
  function fingerprintFile(filePath) {
167267
167479
  try {
167268
- const stat2 = lstatSync(filePath);
167269
- const isReadableDoc = stat2.isFile() && stat2.size <= MAX_PROJECT_DOC_BYTES;
167480
+ const stat = lstatSync(filePath);
167481
+ const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
167270
167482
  return {
167271
167483
  exists: isReadableDoc,
167272
- mtimeMs: stat2.mtimeMs,
167273
- size: stat2.size
167484
+ mtimeMs: stat.mtimeMs,
167485
+ size: stat.size
167274
167486
  };
167275
167487
  } catch {
167276
167488
  return { exists: false, mtimeMs: 0, size: 0 };
@@ -167608,7 +167820,9 @@ var DEFAULT_PROTECTED_TAIL_META = {
167608
167820
  protectedTailDrainTokens: 0,
167609
167821
  recoveryNoEligibleHeadCount: 0,
167610
167822
  forceEmergencyBypassWindowStart: 0,
167611
- forceEmergencyBypassUsed: 0
167823
+ forceEmergencyBypassUsed: 0,
167824
+ emergencyDrainActive: 0,
167825
+ historianDrainFailureAt: 0
167612
167826
  };
167613
167827
  function toProtectedTailMeta(row) {
167614
167828
  if (row === null || typeof row !== "object")
@@ -167622,7 +167836,9 @@ function toProtectedTailMeta(row) {
167622
167836
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
167623
167837
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
167624
167838
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
167625
- forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
167839
+ forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
167840
+ emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
167841
+ historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
167626
167842
  };
167627
167843
  }
167628
167844
  function loadProtectedTailMeta(db, sessionId) {
@@ -167630,7 +167846,7 @@ function loadProtectedTailMeta(db, sessionId) {
167630
167846
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
167631
167847
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
167632
167848
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
167633
- force_emergency_bypass_used
167849
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
167634
167850
  FROM session_meta WHERE session_id = ?`).get(sessionId);
167635
167851
  return toProtectedTailMeta(row);
167636
167852
  }
@@ -167674,6 +167890,17 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
167674
167890
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
167675
167891
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
167676
167892
  }
167893
+ var EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95;
167894
+ var EMERGENCY_DRAIN_EXIT_MARGIN = 10;
167895
+ var EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55;
167896
+ var EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000;
167897
+ var EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
167898
+ function emergencyDrainExitThreshold(executeThresholdPercentage) {
167899
+ if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
167900
+ return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
167901
+ }
167902
+ return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
167903
+ }
167677
167904
  function reserveProtectedTailDrainTokens(args) {
167678
167905
  const now = args.now ?? Date.now();
167679
167906
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -167692,18 +167919,30 @@ function reserveProtectedTailDrainTokens(args) {
167692
167919
  let meta3 = loadProtectedTailMeta(args.db, args.sessionId);
167693
167920
  if (now - meta3.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
167694
167921
  args.db.prepare(`UPDATE session_meta
167695
- SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0,
167696
- force_emergency_bypass_window_start = ?, force_emergency_bypass_used = 0
167697
- WHERE session_id = ?`).run(now, now, args.sessionId);
167922
+ SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
167923
+ WHERE session_id = ?`).run(now, args.sessionId);
167698
167924
  meta3 = loadProtectedTailMeta(args.db, args.sessionId);
167699
167925
  }
167926
+ const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
167927
+ let latchActiveSince = meta3.emergencyDrainActive;
167928
+ if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
167929
+ if (latchActiveSince <= 0)
167930
+ latchActiveSince = now;
167931
+ } else if (latchActiveSince > 0) {
167932
+ const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
167933
+ if (args.usagePercentage < exitThreshold || expired)
167934
+ latchActiveSince = 0;
167935
+ }
167936
+ if (latchActiveSince !== meta3.emergencyDrainActive) {
167937
+ args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
167938
+ }
167939
+ const latchActive = latchActiveSince > 0;
167700
167940
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
167701
167941
  const remaining = Math.max(0, budget - meta3.protectedTailDrainTokens);
167702
167942
  let reserved = Math.min(requested, args.perRunCap, remaining);
167703
167943
  let bypass = false;
167704
- const bypassWindowExpired = now - meta3.forceEmergencyBypassWindowStart > DRAIN_WINDOW_MS;
167705
- const bypassUsed = bypassWindowExpired ? 0 : meta3.forceEmergencyBypassUsed;
167706
- if (reserved <= 0 && args.usagePercentage >= 95 && bypassUsed === 0) {
167944
+ const inFailureBackoff = meta3.historianDrainFailureAt > 0 && now - meta3.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
167945
+ if (reserved <= 0 && latchActive && !inFailureBackoff) {
167707
167946
  reserved = Math.min(requested, args.perRunCap);
167708
167947
  bypass = true;
167709
167948
  }
@@ -167711,10 +167950,8 @@ function reserveProtectedTailDrainTokens(args) {
167711
167950
  return;
167712
167951
  args.db.prepare(`UPDATE session_meta
167713
167952
  SET protected_tail_drain_window_started_at = CASE WHEN protected_tail_drain_window_started_at = 0 THEN ? ELSE protected_tail_drain_window_started_at END,
167714
- protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?,
167715
- force_emergency_bypass_window_start = CASE WHEN ? THEN ? ELSE force_emergency_bypass_window_start END,
167716
- force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
167717
- WHERE session_id = ?`).run(now, reserved, bypass ? 1 : 0, now, bypass ? 1 : 0, args.sessionId);
167953
+ protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
167954
+ WHERE session_id = ?`).run(now, reserved, args.sessionId);
167718
167955
  result = {
167719
167956
  ok: true,
167720
167957
  reservedTokens: reserved,
@@ -167724,6 +167961,25 @@ function reserveProtectedTailDrainTokens(args) {
167724
167961
  })();
167725
167962
  return result;
167726
167963
  }
167964
+ function clearEmergencyDrainLatch(db, sessionId) {
167965
+ db.transaction(() => {
167966
+ ensureSessionMetaRow(db, sessionId);
167967
+ db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
167968
+ })();
167969
+ }
167970
+ function recordHistorianDrainFailure(db, sessionId, now) {
167971
+ const ts = now ?? Date.now();
167972
+ db.transaction(() => {
167973
+ ensureSessionMetaRow(db, sessionId);
167974
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
167975
+ })();
167976
+ }
167977
+ function clearHistorianDrainFailure(db, sessionId) {
167978
+ db.transaction(() => {
167979
+ ensureSessionMetaRow(db, sessionId);
167980
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
167981
+ })();
167982
+ }
167727
167983
  function rollbackProtectedTailDrainReservation(db, reservation) {
167728
167984
  if (!reservation || reservation.tokens <= 0)
167729
167985
  return;
@@ -168242,6 +168498,7 @@ var SESSION_META_FALLBACK_SELECTS = {
168242
168498
  last_transform_error: "'' AS last_transform_error",
168243
168499
  system_prompt_hash: "'' AS system_prompt_hash",
168244
168500
  last_todo_state: "'' AS last_todo_state",
168501
+ tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
168245
168502
  cached_m0_bytes: "NULL AS cached_m0_bytes",
168246
168503
  cached_m1_bytes: "NULL AS cached_m1_bytes",
168247
168504
  cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
@@ -168319,6 +168576,14 @@ function updateSessionMeta(db, sessionId, updates) {
168319
168576
  db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
168320
168577
  })();
168321
168578
  }
168579
+ function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
168580
+ if (maxTagNumber <= 0)
168581
+ return;
168582
+ db.transaction(() => {
168583
+ ensureSessionMetaRow(db, sessionId);
168584
+ db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
168585
+ })();
168586
+ }
168322
168587
  // ../plugin/src/features/magic-context/storage-notes.ts
168323
168588
  var NOTE_TYPES = new Set(["session", "smart"]);
168324
168589
  var NOTE_STATUSES = new Set(["active", "pending", "ready", "dismissed"]);
@@ -168716,12 +168981,37 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
168716
168981
  nullCount: row?.null_count ?? 0
168717
168982
  };
168718
168983
  }
168719
- function getTriggerTagTokenUpperBound(db, sessionId) {
168720
- const row = db.prepare(`SELECT
168984
+ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
168985
+ if (limit <= 0)
168986
+ return [];
168987
+ const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
168988
+ const whereProtected = protectedTags > 0 ? `AND tag_number < (
168989
+ SELECT tag_number FROM tags
168990
+ WHERE session_id = ? AND status = 'active'
168991
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
168992
+ )` : "";
168993
+ const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
168994
+ const rows = db.prepare(`SELECT tag_number, tool_name
168995
+ FROM tags
168996
+ WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
168997
+ ORDER BY tag_number ASC, id ASC
168998
+ LIMIT ?`).all(...params);
168999
+ return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
169000
+ tagNumber: row.tag_number,
169001
+ toolName: typeof row.tool_name === "string" ? row.tool_name : null
169002
+ }));
169003
+ }
169004
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
169005
+ const sql = floor > 0 ? `SELECT
168721
169006
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
168722
169007
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
168723
169008
  FROM tags
168724
- WHERE session_id = ? AND status IN ('active', 'dropped')`).get(sessionId);
169009
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
169010
+ COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
169011
+ COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
169012
+ FROM tags
169013
+ WHERE session_id = ? AND status IN ('active', 'dropped')`;
169014
+ const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
168725
169015
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
168726
169016
  }
168727
169017
  function updateTagInputByteSize(db, sessionId, tagNumber, newInputByteSize) {
@@ -168748,10 +169038,12 @@ function getUpdateTagInputTokenCountStatement(db) {
168748
169038
  function updateTagTokenCount(db, sessionId, tagNumber, newTokenCount) {
168749
169039
  getUpdateTagTokenCountStatement(db).run(newTokenCount, sessionId, tagNumber);
168750
169040
  }
168751
- function getAllStatusTagTokenTotalsFlat(db, sessionId) {
168752
- const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
168753
- FROM tags
168754
- WHERE session_id = ?`).all(sessionId);
169041
+ function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
169042
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
169043
+ FROM tags
169044
+ WHERE session_id = ? AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
169045
+ FROM tags
169046
+ WHERE session_id = ?`).all(sessionId);
168755
169047
  const totals = new Map;
168756
169048
  const nullMessageIds = new Set;
168757
169049
  for (const row of rows) {
@@ -168870,6 +169162,52 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
168870
169162
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
168871
169163
  return isTagNumberRow(row) ? row.tag_number : null;
168872
169164
  }
169165
+ var getMinMessageTagNumberForRawIdStatements = new WeakMap;
169166
+ function isMinTagNumberRow(row) {
169167
+ return row !== null && typeof row === "object" && "m" in row;
169168
+ }
169169
+ function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
169170
+ if (rawId.includes(":"))
169171
+ return null;
169172
+ let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
169173
+ if (!stmt) {
169174
+ stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
169175
+ getMinMessageTagNumberForRawIdStatements.set(db, stmt);
169176
+ }
169177
+ const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
169178
+ return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
169179
+ }
169180
+ var TAGGER_FLOOR_SCAN_MESSAGES = 8;
169181
+ var TAGGER_FLOOR_MAX_PROBES = 64;
169182
+ var TAGGER_FLOOR_SAFETY_MARGIN = 256;
169183
+ var TAGGER_FLOOR_PER_SKIP_MARGIN = 64;
169184
+ function deriveTagLoadFloor(db, sessionId, rawIds) {
169185
+ let min = Number.POSITIVE_INFINITY;
169186
+ let probes = 0;
169187
+ let hits = 0;
169188
+ let skippedBeforeFirstHit = 0;
169189
+ for (const rawId of rawIds) {
169190
+ if (typeof rawId !== "string" || rawId.length === 0)
169191
+ continue;
169192
+ if (probes >= TAGGER_FLOOR_MAX_PROBES)
169193
+ break;
169194
+ probes++;
169195
+ const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
169196
+ if (m === null) {
169197
+ if (hits === 0)
169198
+ skippedBeforeFirstHit++;
169199
+ continue;
169200
+ }
169201
+ if (m < min)
169202
+ min = m;
169203
+ if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
169204
+ break;
169205
+ }
169206
+ if (!Number.isFinite(min))
169207
+ return 0;
169208
+ const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
169209
+ return Math.max(0, min - margin);
169210
+ }
168873
169211
  var TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id";
168874
169212
  function getTagsBySession(db, sessionId) {
168875
169213
  const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
@@ -169665,15 +170003,16 @@ function parseInteger(str) {
169665
170003
  }
169666
170004
 
169667
170005
  // ../plugin/src/tools/ctx-reduce/constants.ts
169668
- var CTX_REDUCE_DESCRIPTION = `Reduce context by dropping tagged content you no longer need.
169669
- Use §N§ identifiers visible in conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
170006
+ var CTX_REDUCE_DESCRIPTION = `Mark spent tagged content as discardable to reclaim context space. This is NOT an immediate delete. Use §N§ identifiers visible in the conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
170007
+
170008
+ How it works:
170009
+ - Marking QUEUES content for release. It stays fully visible to you until context space is actually needed — which may be as soon as the next turn if you are already under pressure, or many turns later if not. So mark spent outputs as soon as you finish with them; don't hoard the call for the end of the turn.
170010
+ - The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
170011
+ - When content is finally released it becomes a short placeholder, and re-running the tool is the only way to get it back. So mark only what you are genuinely DONE with — the test is "have I extracted what I need from this?", not "is it safe / do I have time before it drops?".
169670
170012
 
169671
- CRITICAL RULES:
169672
- - NEVER blanket-drop large ranges (e.g., "1-50"). Always review what each tag contains first.
169673
- - Only drop tool outputs you have already processed and no longer need.
169674
- - Protected tags are accepted but deferred until they leave the last protected range.
169675
- - Keep recent context — only reduce OLD content that is no longer relevant to current work.
169676
- - Dropped content is gone forever.`;
170013
+ Mark discardable once processed: large outputs you've summarized, repeated or redundant dumps, data written to disk, status/log output that only confirmed an expected state.
170014
+ Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
170015
+ Never blanket-mark large ranges (e.g. "1-50") review what each tag holds first.`;
169677
170016
 
169678
170017
  // src/tools/ctx-reduce.ts
169679
170018
  var ParamsSchema4 = exports_typebox.Object({
@@ -169858,120 +170197,6 @@ ${body}` : subject;
169858
170197
  }
169859
170198
  // ../plugin/src/features/magic-context/git-commits/indexer.ts
169860
170199
  init_logger();
169861
-
169862
- // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
169863
- init_logger();
169864
- var insertStatements = new WeakMap;
169865
- var existingShasStatements = new WeakMap;
169866
- var projectCountStatements = new WeakMap;
169867
- var evictStatements = new WeakMap;
169868
- var evictOverflowStatements = new WeakMap;
169869
- var latestCommitTimeStatements = new WeakMap;
169870
- function getInsertStatement(db) {
169871
- let stmt = insertStatements.get(db);
169872
- if (!stmt) {
169873
- stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
169874
- VALUES (?, ?, ?, ?, ?, ?, ?)
169875
- ON CONFLICT(sha) DO UPDATE SET
169876
- project_path = excluded.project_path,
169877
- short_sha = excluded.short_sha,
169878
- message = excluded.message,
169879
- author = excluded.author,
169880
- committed_at = excluded.committed_at,
169881
- indexed_at = excluded.indexed_at
169882
- WHERE git_commits.message != excluded.message`);
169883
- insertStatements.set(db, stmt);
169884
- }
169885
- return stmt;
169886
- }
169887
- function getExistingShasStatement(db) {
169888
- let stmt = existingShasStatements.get(db);
169889
- if (!stmt) {
169890
- stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
169891
- existingShasStatements.set(db, stmt);
169892
- }
169893
- return stmt;
169894
- }
169895
- function getProjectCountStatement(db) {
169896
- let stmt = projectCountStatements.get(db);
169897
- if (!stmt) {
169898
- stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
169899
- projectCountStatements.set(db, stmt);
169900
- }
169901
- return stmt;
169902
- }
169903
- function getLatestCommitTimeStatement(db) {
169904
- let stmt = latestCommitTimeStatements.get(db);
169905
- if (!stmt) {
169906
- stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
169907
- latestCommitTimeStatements.set(db, stmt);
169908
- }
169909
- return stmt;
169910
- }
169911
- function getEvictOverflowStatement(db) {
169912
- let stmt = evictOverflowStatements.get(db);
169913
- if (!stmt) {
169914
- stmt = db.prepare(`DELETE FROM git_commits
169915
- WHERE rowid IN (
169916
- SELECT rowid FROM git_commits
169917
- WHERE project_path = ?
169918
- ORDER BY committed_at DESC, sha DESC
169919
- LIMIT -1 OFFSET ?
169920
- )`);
169921
- evictOverflowStatements.set(db, stmt);
169922
- }
169923
- return stmt;
169924
- }
169925
- function upsertCommits(db, projectPath, commits) {
169926
- if (commits.length === 0)
169927
- return { inserted: 0, updated: 0 };
169928
- const existing = new Set;
169929
- for (const row of getExistingShasStatement(db).all(projectPath)) {
169930
- existing.add(row.sha);
169931
- }
169932
- let inserted = 0;
169933
- let updated = 0;
169934
- const now = Date.now();
169935
- const insertStmt = getInsertStatement(db);
169936
- db.transaction(() => {
169937
- for (const commit of commits) {
169938
- const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
169939
- if (result.changes > 0) {
169940
- if (existing.has(commit.sha)) {
169941
- updated++;
169942
- } else {
169943
- inserted++;
169944
- existing.add(commit.sha);
169945
- }
169946
- }
169947
- }
169948
- })();
169949
- return { inserted, updated };
169950
- }
169951
- function getCommitCount(db, projectPath) {
169952
- const row = getProjectCountStatement(db).get(projectPath);
169953
- return row?.count ?? 0;
169954
- }
169955
- function getLatestIndexedCommitTimeMs(db, projectPath) {
169956
- const row = getLatestCommitTimeStatement(db).get(projectPath);
169957
- return row?.latest ?? null;
169958
- }
169959
- function enforceProjectCap(db, projectPath, maxCommits) {
169960
- if (maxCommits <= 0)
169961
- return 0;
169962
- const count = getCommitCount(db, projectPath);
169963
- if (count <= maxCommits)
169964
- return 0;
169965
- getEvictOverflowStatement(db).run(projectPath, maxCommits);
169966
- const after = getCommitCount(db, projectPath);
169967
- const evicted = Math.max(0, count - after);
169968
- if (evicted > 0) {
169969
- log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
169970
- }
169971
- return evicted;
169972
- }
169973
-
169974
- // ../plugin/src/features/magic-context/git-commits/indexer.ts
169975
170200
  var MS_PER_DAY = 24 * 60 * 60 * 1000;
169976
170201
  var EMBED_BATCH_SIZE = 16;
169977
170202
  var EMBED_MAX_PER_SWEEP = 500;
@@ -171156,13 +171381,13 @@ function extractM0Block(m0Text, tag) {
171156
171381
 
171157
171382
  // ../plugin/src/hooks/magic-context/key-files-block.ts
171158
171383
  import { readFileSync as readFileSync6, realpathSync as realpathSync2 } from "node:fs";
171159
- import { join as join9, sep as sep2 } from "node:path";
171384
+ import { join as join8, sep as sep2 } from "node:path";
171160
171385
 
171161
171386
  // ../plugin/src/features/magic-context/key-files/aft-availability.ts
171162
171387
  var import_comment_json2 = __toESM(require_src2(), 1);
171163
171388
  import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
171164
171389
  import { homedir as homedir4 } from "node:os";
171165
- import { isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "node:path";
171390
+ import { isAbsolute as isAbsolute2, join as join7, resolve as resolve3 } from "node:path";
171166
171391
  import { fileURLToPath } from "node:url";
171167
171392
  var overrideAvailability = null;
171168
171393
  function parseConfig(path5) {
@@ -171183,14 +171408,14 @@ function resolveLocalEntryPackageName(value, configDir) {
171183
171408
  return null;
171184
171409
  }
171185
171410
  } else if (value.startsWith("~/")) {
171186
- dir = join8(homedir4(), value.slice(2));
171411
+ dir = join7(homedir4(), value.slice(2));
171187
171412
  } else if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) {
171188
171413
  dir = isAbsolute2(value) ? value : resolve3(configDir, value);
171189
171414
  } else {
171190
171415
  return null;
171191
171416
  }
171192
171417
  try {
171193
- const pkg = JSON.parse(readFileSync5(join8(dir, "package.json"), "utf-8"));
171418
+ const pkg = JSON.parse(readFileSync5(join7(dir, "package.json"), "utf-8"));
171194
171419
  return typeof pkg.name === "string" ? pkg.name : null;
171195
171420
  } catch {
171196
171421
  return null;
@@ -171221,16 +171446,16 @@ function hasAftAtKeys(value, keys2, configDir) {
171221
171446
  function getAftAvailability() {
171222
171447
  const home = process.env.HOME || homedir4();
171223
171448
  const opencodePaths = [
171224
- join8(home, ".config", "opencode", "opencode.jsonc"),
171225
- join8(home, ".config", "opencode", "opencode.json")
171449
+ join7(home, ".config", "opencode", "opencode.jsonc"),
171450
+ join7(home, ".config", "opencode", "opencode.json")
171226
171451
  ];
171227
- const piPaths = [join8(home, ".pi", "agent", "settings.json")];
171452
+ const piPaths = [join7(home, ".pi", "agent", "settings.json")];
171228
171453
  const checkedPaths = [...opencodePaths, ...piPaths];
171229
171454
  let opencode = false;
171230
171455
  for (const path5 of opencodePaths) {
171231
171456
  try {
171232
171457
  const config2 = parseConfig(path5);
171233
- const configDir = join8(path5, "..");
171458
+ const configDir = join7(path5, "..");
171234
171459
  if (hasAftAtKeys(config2, ["plugin", "plugins", "mcp", "mcp_servers"], configDir)) {
171235
171460
  opencode = true;
171236
171461
  break;
@@ -171241,7 +171466,7 @@ function getAftAvailability() {
171241
171466
  for (const path5 of piPaths) {
171242
171467
  try {
171243
171468
  const config2 = parseConfig(path5);
171244
- const configDir = join8(path5, "..");
171469
+ const configDir = join7(path5, "..");
171245
171470
  if (hasAftAtKeys(config2, ["packages", "extensions"], configDir)) {
171246
171471
  pi = true;
171247
171472
  break;
@@ -171320,7 +171545,7 @@ function buildKeyFilesBlock(db, projectPath, config2 = { enabled: true, tokenBud
171320
171545
  let nextStale = null;
171321
171546
  let observed = false;
171322
171547
  try {
171323
- const absPath = join9(projectPath, row.path);
171548
+ const absPath = join8(projectPath, row.path);
171324
171549
  const real = realpathSync2(absPath);
171325
171550
  if (!isUnderResolvedRoot(projectRoot, real)) {
171326
171551
  nextStale = "missing";
@@ -171721,7 +171946,7 @@ function createCtxSearchTool(deps) {
171721
171946
  memoryEnabled,
171722
171947
  embeddingEnabled,
171723
171948
  embedQuery: async (text, signal) => {
171724
- const result = await embedTextForProject(projectIdentity, text, signal);
171949
+ const result = await embedTextForProject(projectIdentity, text, signal, "query");
171725
171950
  return result?.vector ?? null;
171726
171951
  },
171727
171952
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,