@wolfx/pi-magic-context 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -141212,6 +141212,24 @@ class ToolMutationBatch {
141212
141212
  }
141213
141213
 
141214
141214
  // ../plugin/src/hooks/magic-context/read-session-chunk.ts
141215
+ var BLOCK_TOKEN_MEMO_MAX = 2048;
141216
+ var blockTokenMemo = new Map;
141217
+ function estimateBlockTokens(blockText) {
141218
+ const cached = blockTokenMemo.get(blockText);
141219
+ if (cached !== undefined) {
141220
+ blockTokenMemo.delete(blockText);
141221
+ blockTokenMemo.set(blockText, cached);
141222
+ return cached;
141223
+ }
141224
+ const count = estimateTokens(blockText);
141225
+ if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
141226
+ const oldest = blockTokenMemo.keys().next().value;
141227
+ if (oldest !== undefined)
141228
+ blockTokenMemo.delete(oldest);
141229
+ }
141230
+ blockTokenMemo.set(blockText, count);
141231
+ return count;
141232
+ }
141215
141233
  var activeRawMessageCache = null;
141216
141234
  var activeAbsoluteCountCache = null;
141217
141235
  var sessionProviders = new Map;
@@ -141394,7 +141412,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
141394
141412
  if (!currentBlock)
141395
141413
  return true;
141396
141414
  const blockText = formatBlock(currentBlock);
141397
- const blockTokens = estimateTokens(blockText);
141415
+ const blockTokens = estimateBlockTokens(blockText);
141398
141416
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
141399
141417
  return false;
141400
141418
  }
@@ -141999,6 +142017,8 @@ var SESSION_META_SELECT_COLUMNS = [
141999
142017
  "recovery_no_eligible_head_count",
142000
142018
  "force_emergency_bypass_window_start",
142001
142019
  "force_emergency_bypass_used",
142020
+ "emergency_drain_active",
142021
+ "historian_drain_failure_at",
142002
142022
  "upgrade_reminded_at",
142003
142023
  "pi_stable_id_scheme"
142004
142024
  ];
@@ -142048,6 +142068,8 @@ var META_COLUMNS = {
142048
142068
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
142049
142069
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
142050
142070
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
142071
+ emergencyDrainActive: "emergency_drain_active",
142072
+ historianDrainFailureAt: "historian_drain_failure_at",
142051
142073
  upgradeRemindedAt: "upgrade_reminded_at",
142052
142074
  piStableIdScheme: "pi_stable_id_scheme"
142053
142075
  };
@@ -142949,8 +142971,9 @@ function backfillToolOwnersInChunks(db, result) {
142949
142971
  var databases = new Map;
142950
142972
  var persistenceByDatabase = new WeakMap;
142951
142973
  var persistenceErrorByDatabase = new WeakMap;
142974
+ var pathByDatabase = new WeakMap;
142952
142975
  var lastSchemaFenceRejection = null;
142953
- var LATEST_SUPPORTED_VERSION = 36;
142976
+ var LATEST_SUPPORTED_VERSION = 38;
142954
142977
  function resolveDatabasePath(dbPathOverride) {
142955
142978
  if (dbPathOverride) {
142956
142979
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -142963,6 +142986,9 @@ function resolveDatabasePath(dbPathOverride) {
142963
142986
  const dbDir = getMagicContextStorageDir();
142964
142987
  return { dbDir, dbPath: join5(dbDir, "context.db") };
142965
142988
  }
142989
+ function getDatabasePath(db) {
142990
+ return pathByDatabase.get(db) ?? null;
142991
+ }
142966
142992
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
142967
142993
  if (existsSync4(targetDbPath))
142968
142994
  return;
@@ -143477,6 +143503,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143477
143503
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
143478
143504
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
143479
143505
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
143506
+ emergency_drain_active INTEGER NOT NULL DEFAULT 0,
143507
+ historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
143480
143508
  cached_m0_materialized_at INTEGER,
143481
143509
  cached_m0_session_facts_version INTEGER,
143482
143510
  cached_m0_upgrade_state TEXT,
@@ -143540,6 +143568,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143540
143568
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
143541
143569
  ON historian_runs(status, created_at DESC);
143542
143570
 
143571
+ CREATE TABLE IF NOT EXISTS transform_decisions (
143572
+ session_id TEXT NOT NULL,
143573
+ harness TEXT NOT NULL DEFAULT 'opencode',
143574
+ message_id TEXT NOT NULL,
143575
+ ts_ms INTEGER NOT NULL,
143576
+ decision TEXT NOT NULL,
143577
+ materialized INTEGER NOT NULL DEFAULT 0,
143578
+ materialize_reason TEXT,
143579
+ emergency INTEGER NOT NULL DEFAULT 0,
143580
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
143581
+ dropped_count INTEGER NOT NULL DEFAULT 0,
143582
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143583
+ PRIMARY KEY (session_id, harness, message_id)
143584
+ );
143585
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
143586
+ ON transform_decisions(session_id, harness);
143587
+
143543
143588
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
143544
143589
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
143545
143590
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -143691,6 +143736,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143691
143736
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
143692
143737
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
143693
143738
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
143739
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
143740
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
143694
143741
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
143695
143742
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
143696
143743
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -143769,6 +143816,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143769
143816
  failed_at INTEGER NOT NULL,
143770
143817
  UNIQUE(table_name, row_id)
143771
143818
  );
143819
+ CREATE TABLE IF NOT EXISTS transform_decisions (
143820
+ session_id TEXT NOT NULL,
143821
+ harness TEXT NOT NULL DEFAULT 'opencode',
143822
+ message_id TEXT NOT NULL,
143823
+ ts_ms INTEGER NOT NULL,
143824
+ decision TEXT NOT NULL,
143825
+ materialized INTEGER NOT NULL DEFAULT 0,
143826
+ materialize_reason TEXT,
143827
+ emergency INTEGER NOT NULL DEFAULT 0,
143828
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
143829
+ dropped_count INTEGER NOT NULL DEFAULT 0,
143830
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143831
+ PRIMARY KEY (session_id, harness, message_id)
143832
+ );
143833
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
143834
+ ON transform_decisions(session_id, harness);
143772
143835
  `);
143773
143836
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143774
143837
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -143855,7 +143918,9 @@ function healNullIntegerColumns(db) {
143855
143918
  ["protected_tail_drain_tokens", 0],
143856
143919
  ["recovery_no_eligible_head_count", 0],
143857
143920
  ["force_emergency_bypass_window_start", 0],
143858
- ["force_emergency_bypass_used", 0]
143921
+ ["force_emergency_bypass_used", 0],
143922
+ ["emergency_drain_active", 0],
143923
+ ["historian_drain_failure_at", 0]
143859
143924
  ];
143860
143925
  for (const [column, fallback] of columns) {
143861
143926
  try {
@@ -143931,6 +143996,7 @@ function openDatabase(dbPathOrOptions) {
143931
143996
  setDatabase(db);
143932
143997
  loadToolDefinitionMeasurements(db);
143933
143998
  databases.set(dbPath, db);
143999
+ pathByDatabase.set(db, dbPath);
143934
144000
  persistenceByDatabase.set(db, true);
143935
144001
  persistenceErrorByDatabase.delete(db);
143936
144002
  return db;
@@ -145172,6 +145238,41 @@ var MIGRATIONS = [
145172
145238
  `);
145173
145239
  }
145174
145240
  }
145241
+ },
145242
+ {
145243
+ version: 37,
145244
+ description: "emergency drain catch-up latch + historian drain failure backoff",
145245
+ up: (db) => {
145246
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
145247
+ if (!hasSessionMeta)
145248
+ return;
145249
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
145250
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
145251
+ }
145252
+ },
145253
+ {
145254
+ version: 38,
145255
+ description: "durable transform decisions for cache-event cause attribution",
145256
+ up: (db) => {
145257
+ db.exec(`
145258
+ CREATE TABLE IF NOT EXISTS transform_decisions (
145259
+ session_id TEXT NOT NULL,
145260
+ harness TEXT NOT NULL DEFAULT 'opencode',
145261
+ message_id TEXT NOT NULL,
145262
+ ts_ms INTEGER NOT NULL,
145263
+ decision TEXT NOT NULL,
145264
+ materialized INTEGER NOT NULL DEFAULT 0,
145265
+ materialize_reason TEXT,
145266
+ emergency INTEGER NOT NULL DEFAULT 0,
145267
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
145268
+ dropped_count INTEGER NOT NULL DEFAULT 0,
145269
+ input_tokens INTEGER NOT NULL DEFAULT 0,
145270
+ PRIMARY KEY (session_id, harness, message_id)
145271
+ );
145272
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
145273
+ ON transform_decisions(session_id, harness);
145274
+ `);
145275
+ }
145175
145276
  }
145176
145277
  ];
145177
145278
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -145603,7 +145704,9 @@ var DEFAULT_PROTECTED_TAIL_META = {
145603
145704
  protectedTailDrainTokens: 0,
145604
145705
  recoveryNoEligibleHeadCount: 0,
145605
145706
  forceEmergencyBypassWindowStart: 0,
145606
- forceEmergencyBypassUsed: 0
145707
+ forceEmergencyBypassUsed: 0,
145708
+ emergencyDrainActive: 0,
145709
+ historianDrainFailureAt: 0
145607
145710
  };
145608
145711
  function toProtectedTailMeta(row) {
145609
145712
  if (row === null || typeof row !== "object")
@@ -145617,7 +145720,9 @@ function toProtectedTailMeta(row) {
145617
145720
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
145618
145721
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
145619
145722
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
145620
- forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
145723
+ forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
145724
+ emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
145725
+ historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
145621
145726
  };
145622
145727
  }
145623
145728
  function loadProtectedTailMeta(db, sessionId) {
@@ -145625,7 +145730,7 @@ function loadProtectedTailMeta(db, sessionId) {
145625
145730
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
145626
145731
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
145627
145732
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
145628
- force_emergency_bypass_used
145733
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
145629
145734
  FROM session_meta WHERE session_id = ?`).get(sessionId);
145630
145735
  return toProtectedTailMeta(row);
145631
145736
  }
@@ -145669,6 +145774,17 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
145669
145774
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
145670
145775
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
145671
145776
  }
145777
+ var EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95;
145778
+ var EMERGENCY_DRAIN_EXIT_MARGIN = 10;
145779
+ var EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55;
145780
+ var EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000;
145781
+ var EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
145782
+ function emergencyDrainExitThreshold(executeThresholdPercentage) {
145783
+ if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
145784
+ return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
145785
+ }
145786
+ return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
145787
+ }
145672
145788
  function reserveProtectedTailDrainTokens(args) {
145673
145789
  const now = args.now ?? Date.now();
145674
145790
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -145687,18 +145803,30 @@ function reserveProtectedTailDrainTokens(args) {
145687
145803
  let meta = loadProtectedTailMeta(args.db, args.sessionId);
145688
145804
  if (now - meta.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
145689
145805
  args.db.prepare(`UPDATE session_meta
145690
- SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0,
145691
- force_emergency_bypass_window_start = ?, force_emergency_bypass_used = 0
145692
- WHERE session_id = ?`).run(now, now, args.sessionId);
145806
+ SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
145807
+ WHERE session_id = ?`).run(now, args.sessionId);
145693
145808
  meta = loadProtectedTailMeta(args.db, args.sessionId);
145694
145809
  }
145810
+ const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
145811
+ let latchActiveSince = meta.emergencyDrainActive;
145812
+ if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
145813
+ if (latchActiveSince <= 0)
145814
+ latchActiveSince = now;
145815
+ } else if (latchActiveSince > 0) {
145816
+ const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
145817
+ if (args.usagePercentage < exitThreshold || expired)
145818
+ latchActiveSince = 0;
145819
+ }
145820
+ if (latchActiveSince !== meta.emergencyDrainActive) {
145821
+ args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
145822
+ }
145823
+ const latchActive = latchActiveSince > 0;
145695
145824
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
145696
145825
  const remaining = Math.max(0, budget - meta.protectedTailDrainTokens);
145697
145826
  let reserved = Math.min(requested, args.perRunCap, remaining);
145698
145827
  let bypass = false;
145699
- const bypassWindowExpired = now - meta.forceEmergencyBypassWindowStart > DRAIN_WINDOW_MS;
145700
- const bypassUsed = bypassWindowExpired ? 0 : meta.forceEmergencyBypassUsed;
145701
- if (reserved <= 0 && args.usagePercentage >= 95 && bypassUsed === 0) {
145828
+ const inFailureBackoff = meta.historianDrainFailureAt > 0 && now - meta.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
145829
+ if (reserved <= 0 && latchActive && !inFailureBackoff) {
145702
145830
  reserved = Math.min(requested, args.perRunCap);
145703
145831
  bypass = true;
145704
145832
  }
@@ -145706,10 +145834,8 @@ function reserveProtectedTailDrainTokens(args) {
145706
145834
  return;
145707
145835
  args.db.prepare(`UPDATE session_meta
145708
145836
  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,
145709
- protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?,
145710
- force_emergency_bypass_window_start = CASE WHEN ? THEN ? ELSE force_emergency_bypass_window_start END,
145711
- force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
145712
- WHERE session_id = ?`).run(now, reserved, bypass ? 1 : 0, now, bypass ? 1 : 0, args.sessionId);
145837
+ protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
145838
+ WHERE session_id = ?`).run(now, reserved, args.sessionId);
145713
145839
  result = {
145714
145840
  ok: true,
145715
145841
  reservedTokens: reserved,
@@ -145719,6 +145845,25 @@ function reserveProtectedTailDrainTokens(args) {
145719
145845
  })();
145720
145846
  return result;
145721
145847
  }
145848
+ function clearEmergencyDrainLatch(db, sessionId) {
145849
+ db.transaction(() => {
145850
+ ensureSessionMetaRow(db, sessionId);
145851
+ db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
145852
+ })();
145853
+ }
145854
+ function recordHistorianDrainFailure(db, sessionId, now) {
145855
+ const ts = now ?? Date.now();
145856
+ db.transaction(() => {
145857
+ ensureSessionMetaRow(db, sessionId);
145858
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
145859
+ })();
145860
+ }
145861
+ function clearHistorianDrainFailure(db, sessionId) {
145862
+ db.transaction(() => {
145863
+ ensureSessionMetaRow(db, sessionId);
145864
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
145865
+ })();
145866
+ }
145722
145867
  function rollbackProtectedTailDrainReservation(db, reservation) {
145723
145868
  if (!reservation || reservation.tokens <= 0)
145724
145869
  return;
@@ -146740,12 +146885,17 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
146740
146885
  toolName: typeof row.tool_name === "string" ? row.tool_name : null
146741
146886
  }));
146742
146887
  }
146743
- function getTriggerTagTokenUpperBound(db, sessionId) {
146744
- const row = db.prepare(`SELECT
146888
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
146889
+ const sql = floor > 0 ? `SELECT
146745
146890
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
146746
146891
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
146747
146892
  FROM tags
146748
- WHERE session_id = ? AND status IN ('active', 'dropped')`).get(sessionId);
146893
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
146894
+ COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
146895
+ COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
146896
+ FROM tags
146897
+ WHERE session_id = ? AND status IN ('active', 'dropped')`;
146898
+ const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
146749
146899
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
146750
146900
  }
146751
146901
  function updateTagInputByteSize(db, sessionId, tagNumber, newInputByteSize) {
@@ -146772,10 +146922,12 @@ function getUpdateTagInputTokenCountStatement(db) {
146772
146922
  function updateTagTokenCount(db, sessionId, tagNumber, newTokenCount) {
146773
146923
  getUpdateTagTokenCountStatement(db).run(newTokenCount, sessionId, tagNumber);
146774
146924
  }
146775
- function getAllStatusTagTokenTotalsFlat(db, sessionId) {
146776
- const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
146777
- FROM tags
146778
- WHERE session_id = ?`).all(sessionId);
146925
+ function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
146926
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
146927
+ FROM tags
146928
+ 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
146929
+ FROM tags
146930
+ WHERE session_id = ?`).all(sessionId);
146779
146931
  const totals = new Map;
146780
146932
  const nullMessageIds = new Set;
146781
146933
  for (const row of rows) {
@@ -146894,6 +147046,52 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
146894
147046
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
146895
147047
  return isTagNumberRow(row) ? row.tag_number : null;
146896
147048
  }
147049
+ var getMinMessageTagNumberForRawIdStatements = new WeakMap;
147050
+ function isMinTagNumberRow(row) {
147051
+ return row !== null && typeof row === "object" && "m" in row;
147052
+ }
147053
+ function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
147054
+ if (rawId.includes(":"))
147055
+ return null;
147056
+ let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
147057
+ if (!stmt) {
147058
+ stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
147059
+ getMinMessageTagNumberForRawIdStatements.set(db, stmt);
147060
+ }
147061
+ const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
147062
+ return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
147063
+ }
147064
+ var TAGGER_FLOOR_SCAN_MESSAGES = 8;
147065
+ var TAGGER_FLOOR_MAX_PROBES = 64;
147066
+ var TAGGER_FLOOR_SAFETY_MARGIN = 256;
147067
+ var TAGGER_FLOOR_PER_SKIP_MARGIN = 64;
147068
+ function deriveTagLoadFloor(db, sessionId, rawIds) {
147069
+ let min = Number.POSITIVE_INFINITY;
147070
+ let probes = 0;
147071
+ let hits = 0;
147072
+ let skippedBeforeFirstHit = 0;
147073
+ for (const rawId of rawIds) {
147074
+ if (typeof rawId !== "string" || rawId.length === 0)
147075
+ continue;
147076
+ if (probes >= TAGGER_FLOOR_MAX_PROBES)
147077
+ break;
147078
+ probes++;
147079
+ const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
147080
+ if (m === null) {
147081
+ if (hits === 0)
147082
+ skippedBeforeFirstHit++;
147083
+ continue;
147084
+ }
147085
+ if (m < min)
147086
+ min = m;
147087
+ if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
147088
+ break;
147089
+ }
147090
+ if (!Number.isFinite(min))
147091
+ return 0;
147092
+ const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
147093
+ return Math.max(0, min - margin);
147094
+ }
146897
147095
  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";
146898
147096
  function getTagsBySession(db, sessionId) {
146899
147097
  const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
@@ -146997,6 +147195,11 @@ init_logger();
146997
147195
  import { createHash as createHash5 } from "node:crypto";
146998
147196
  import { realpathSync as realpathSync2 } from "node:fs";
146999
147197
  import path5 from "node:path";
147198
+
147199
+ // ../plugin/src/features/magic-context/memory/relocate-memory.ts
147200
+ var memoryCopyColumnsCache = new WeakMap;
147201
+
147202
+ // ../plugin/src/features/magic-context/v22-deferred-backfill.ts
147000
147203
  var BATCH_SIZE = 25;
147001
147204
  var YIELD_EVERY_N_ROWS = 5;
147002
147205
  var BACKFILL_META_KEY = "v22_legacy_memory_backfill";
@@ -147212,65 +147415,6 @@ async function runDeferredV22Backfill(db, options = {}) {
147212
147415
  };
147213
147416
  }
147214
147417
 
147215
- // ../plugin/src/agents/historian.ts
147216
- var HISTORIAN_AGENT = "historian";
147217
- var HISTORIAN_EDITOR_AGENT = "historian-editor";
147218
-
147219
- // ../plugin/src/agents/dreamer.ts
147220
- var DREAMER_AGENT = "dreamer";
147221
-
147222
- // ../plugin/src/agents/sidekick.ts
147223
- var SIDEKICK_AGENT = "sidekick";
147224
-
147225
- // ../plugin/src/shared/model-requirements.ts
147226
- var HISTORIAN_FALLBACK_CHAIN = [
147227
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
147228
- { providers: ["opencode-go"], model: "minimax-m2.7" },
147229
- {
147230
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
147231
- model: "glm-5"
147232
- },
147233
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4" },
147234
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" }
147235
- ];
147236
- var DREAMER_FALLBACK_CHAIN = [
147237
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
147238
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
147239
- {
147240
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
147241
- model: "glm-5"
147242
- },
147243
- { providers: ["opencode-go"], model: "minimax-m2.7" },
147244
- { providers: ["github-copilot", "openai", "opencode"], model: "gpt-5.4-mini" }
147245
- ];
147246
- var SIDEKICK_FALLBACK_CHAIN = [
147247
- { providers: ["cerebras"], model: "qwen-3-235b-a22b-instruct-2507" },
147248
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
147249
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4-mini" },
147250
- { providers: ["opencode"], model: "gpt-5-nano" }
147251
- ];
147252
- var AGENT_MODEL_REQUIREMENTS = {
147253
- [HISTORIAN_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
147254
- [HISTORIAN_EDITOR_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
147255
- [DREAMER_AGENT]: { fallbackChain: DREAMER_FALLBACK_CHAIN },
147256
- [SIDEKICK_AGENT]: { fallbackChain: SIDEKICK_FALLBACK_CHAIN }
147257
- };
147258
- function expandFallbackChain(chain) {
147259
- const models = [];
147260
- for (const entry of chain) {
147261
- for (const provider of entry.providers) {
147262
- models.push(`${provider}/${entry.model}`);
147263
- }
147264
- }
147265
- return models;
147266
- }
147267
- function getAgentFallbackModels(agent) {
147268
- const requirement = AGENT_MODEL_REQUIREMENTS[agent];
147269
- if (!requirement)
147270
- return;
147271
- return expandFallbackChain(requirement.fallbackChain);
147272
- }
147273
-
147274
147418
  // ../plugin/src/shared/models-dev-cache.ts
147275
147419
  init_data_path();
147276
147420
  init_logger();
@@ -147361,25 +147505,9 @@ function resolveHistorianContextLimit(historianModelOverride) {
147361
147505
  return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147362
147506
  }
147363
147507
  if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
147364
- console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
147508
+ console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using the default context limit for chunk-budget derivation.`);
147365
147509
  }
147366
- const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
147367
- if (!chain || chain.length === 0)
147368
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147369
- const expanded = expandFallbackChain(chain);
147370
- let minLimit;
147371
- for (const key of expanded) {
147372
- const [providerID, ...rest] = key.split("/");
147373
- const modelID = rest.join("/");
147374
- if (!providerID || !modelID)
147375
- continue;
147376
- const limit = getSdkContextLimit(providerID, modelID);
147377
- if (typeof limit !== "number" || limit <= 0)
147378
- continue;
147379
- if (minLimit === undefined || limit < minLimit)
147380
- minLimit = limit;
147381
- }
147382
- return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147510
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147383
147511
  }
147384
147512
 
147385
147513
  // ../plugin/src/hooks/magic-context/event-resolvers.ts
@@ -147792,13 +147920,13 @@ async function maybeSendUpgradeReminder(deps, sessionId) {
147792
147920
  init_data_path();
147793
147921
  import * as fs2 from "node:fs";
147794
147922
  import * as path6 from "node:path";
147795
- var ANNOUNCEMENT_VERSION = "0.25.0";
147923
+ var ANNOUNCEMENT_VERSION = "0.26.0";
147796
147924
  var ANNOUNCEMENT_FEATURES = [
147797
- "Old tool output is now reclaimed automatically: once a file read / search / command output has gone a full execute cycle unused, it's dropped on the next one — no need to call ctx_reduce for stale results.",
147798
- "Recover anything that was dropped: ctx_expand({ message: N }) returns a dropped message's full content (every tool call's input + output) from storage. ctx_expand({ start, end, verbose: true }) lists a range message-by-message to find it.",
147799
- "Searchable history made reliable: /ctx-embed shows embedding coverage and runs a resilient backfill (retries transient failures, no longer bails on the first hiccup); the active session now auto-embeds in the background. ctx_reduce guidance also reframed as deferred + recoverable so models trim spent output earlier.",
147800
- "Pi: fixed /ctx-dream (was failing with 'Unknown named parameter') and local-embedding load failures on Windows/Desktop (#151, #128).",
147801
- "Runaway background agents on weak/local models are now capped and force-stopped (#154, #152). Plus several prompt-cache busts removed."
147925
+ "Faster on large sessions: per-message transform overhead is at least 2x lower on typical passes and up to ~10x lower when history summarization fires (no more multi-second pause on big sessions).",
147926
+ "No more surprise models: the built-in fallback chain is gone. Hidden agents only use the model (and fallback_models) you configure — no confusing 'model not found' for providers you never set up. `doctor` now records every historian run so real failures are visible.",
147927
+ "Anthropic thinking-block fix: clearing old reasoning no longer risks a stale-signature rejection on Claude / Bedrock / proxied-Claude routes. Plus fewer prompt-cache busts.",
147928
+ "Community fixes: TUI crash on the upgrade progress panel (#168), historian.disallowed_tools for weak models that loop on tool calls (#166), and a Pi-only config key leak (#167).",
147929
+ "New: doctor migrate-session re-homes a session (and optionally its memories) to another project, with a dry-run preview."
147802
147930
  ];
147803
147931
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
147804
147932
  var STATE_FILENAME = "last_announced_version";
@@ -147857,15 +147985,9 @@ function shouldKeepSubagents() {
147857
147985
  init_logger();
147858
147986
 
147859
147987
  // ../plugin/src/shared/resolve-fallbacks.ts
147860
- function resolveFallbackChain(agentName, userFallbacks) {
147988
+ function resolveFallbackChain(userFallbacks) {
147861
147989
  const userList = normalizeUserFallbacks(userFallbacks);
147862
- if (userList.length > 0) {
147863
- return dedupe(userList.filter(isValidModelSpec));
147864
- }
147865
- const builtin = getAgentFallbackModels(agentName);
147866
- if (!builtin || builtin.length === 0)
147867
- return [];
147868
- return dedupe(builtin.filter(isValidModelSpec));
147990
+ return dedupe(userList.filter(isValidModelSpec));
147869
147991
  }
147870
147992
  function normalizeUserFallbacks(userFallbacks) {
147871
147993
  if (!userFallbacks)
@@ -148804,6 +148926,9 @@ init_logger();
148804
148926
  import { existsSync as existsSync8 } from "node:fs";
148805
148927
  import { join as join11 } from "node:path";
148806
148928
 
148929
+ // ../plugin/src/agents/dreamer.ts
148930
+ var DREAMER_AGENT = "dreamer";
148931
+
148807
148932
  // ../plugin/src/shared/index.ts
148808
148933
  init_logger();
148809
148934
 
@@ -150604,6 +150729,7 @@ ${modeIntro}
150604
150729
  4. **Write or update** using the Write tool. Always write to project root, NOT to .planning/.
150605
150730
 
150606
150731
  ### Rules
150732
+ - **NEVER touch protected regions**: any content between \`<!-- mc:protected START ... -->\` and \`<!-- mc:protected END -->\` is hand-authored and cache-critical. Reproduce it BYTE-FOR-BYTE in your rewrite — do not edit, reword, reorder, summarize, trim, or drop a single line of it, and keep the marker comments themselves. Only a human edits that region.
150607
150733
  - **Be prescriptive**: "Use X pattern" not "X pattern is used"
150608
150734
  - **Always include file paths** in backticks
150609
150735
  - **Write current state only**: no temporal language, no history
@@ -166242,14 +166368,16 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
166242
166368
  }).optional();
166243
166369
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
166244
166370
  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)"),
166245
- 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")
166371
+ 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"),
166372
+ 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: [])`)
166246
166373
  }).optional();
166247
166374
  var BaseEmbeddingConfigSchema = exports_external.object({
166248
166375
  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."),
166249
166376
  model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
166250
166377
  endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
166251
166378
  api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
166252
- 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."),
166379
+ 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."),
166380
+ 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."),
166253
166381
  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."),
166254
166382
  max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
166255
166383
  }).superRefine((data, ctx) => {
@@ -166279,6 +166407,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
166279
166407
  if (data.provider === "openai-compatible") {
166280
166408
  const apiKey = data.api_key?.trim();
166281
166409
  const inputType = data.input_type?.trim();
166410
+ const queryInputType = data.query_input_type?.trim();
166282
166411
  const truncate = data.truncate?.trim();
166283
166412
  return {
166284
166413
  provider: "openai-compatible",
@@ -166286,6 +166415,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
166286
166415
  endpoint: data.endpoint?.trim() ?? "",
166287
166416
  ...apiKey ? { api_key: apiKey } : {},
166288
166417
  ...inputType ? { input_type: inputType } : {},
166418
+ ...queryInputType ? { query_input_type: queryInputType } : {},
166289
166419
  ...truncate ? { truncate } : {},
166290
166420
  ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
166291
166421
  };
@@ -166565,6 +166695,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
166565
166695
  return lines.join(`
166566
166696
  `);
166567
166697
  }
166698
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
166699
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
166700
+ if (!row)
166701
+ return "";
166702
+ const title = typeof row.title === "string" ? row.title.trim() : "";
166703
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
166704
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
166705
+ return [title, body].filter((s) => s.length > 0).join(`
166706
+ `);
166707
+ }
166568
166708
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
166569
166709
  const lines = [];
166570
166710
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -167149,7 +167289,7 @@ class LocalEmbeddingProvider {
167149
167289
  waiter();
167150
167290
  }
167151
167291
  }
167152
- async embed(text, signal) {
167292
+ async embed(text, signal, _purpose) {
167153
167293
  if (signal?.aborted)
167154
167294
  return null;
167155
167295
  if (this.disposing)
@@ -167175,7 +167315,7 @@ class LocalEmbeddingProvider {
167175
167315
  this.finishInFlight();
167176
167316
  }
167177
167317
  }
167178
- async embedBatch(texts, signal) {
167318
+ async embedBatch(texts, signal, _purpose) {
167179
167319
  if (texts.length === 0) {
167180
167320
  return [];
167181
167321
  }
@@ -167314,6 +167454,7 @@ class OpenAICompatibleEmbeddingProvider {
167314
167454
  model;
167315
167455
  apiKey;
167316
167456
  inputType;
167457
+ queryInputType;
167317
167458
  truncate;
167318
167459
  initialized = false;
167319
167460
  failureTimes = [];
@@ -167326,6 +167467,7 @@ class OpenAICompatibleEmbeddingProvider {
167326
167467
  this.model = options.model?.trim() ?? "";
167327
167468
  this.apiKey = options.apiKey?.trim() ?? "";
167328
167469
  this.inputType = options.inputType?.trim() ?? "";
167470
+ this.queryInputType = options.queryInputType?.trim() ?? "";
167329
167471
  this.truncate = options.truncate?.trim() ?? "";
167330
167472
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
167331
167473
  this.modelId = getEmbeddingProviderIdentity({
@@ -167353,11 +167495,17 @@ class OpenAICompatibleEmbeddingProvider {
167353
167495
  this.initialized = true;
167354
167496
  return true;
167355
167497
  }
167356
- async embed(text, signal) {
167357
- const [embedding] = await this.embedBatch([text], signal);
167498
+ resolveInputTypeForPurpose(purpose = "passage") {
167499
+ if (purpose === "query") {
167500
+ return this.queryInputType || this.inputType;
167501
+ }
167502
+ return this.inputType;
167503
+ }
167504
+ async embed(text, signal, purpose) {
167505
+ const [embedding] = await this.embedBatch([text], signal, purpose);
167358
167506
  return embedding ?? null;
167359
167507
  }
167360
- async embedBatch(texts, signal) {
167508
+ async embedBatch(texts, signal, purpose) {
167361
167509
  if (texts.length === 0) {
167362
167510
  return [];
167363
167511
  }
@@ -167383,6 +167531,7 @@ class OpenAICompatibleEmbeddingProvider {
167383
167531
  if (signal) {
167384
167532
  signal.addEventListener("abort", onOuterAbort, { once: true });
167385
167533
  }
167534
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
167386
167535
  const response = await fetch(`${this.endpoint}/embeddings`, {
167387
167536
  method: "POST",
167388
167537
  headers: {
@@ -167392,7 +167541,7 @@ class OpenAICompatibleEmbeddingProvider {
167392
167541
  body: JSON.stringify({
167393
167542
  model: this.model,
167394
167543
  input: texts,
167395
- ...this.inputType ? { input_type: this.inputType } : {},
167544
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
167396
167545
  ...this.truncate ? { truncate: this.truncate } : {}
167397
167546
  }),
167398
167547
  redirect: "error",
@@ -167966,6 +168115,7 @@ function resolveEmbeddingConfig(config2) {
167966
168115
  if (config2.provider === "openai-compatible") {
167967
168116
  const apiKey = config2.api_key?.trim();
167968
168117
  const inputType = config2.input_type?.trim();
168118
+ const queryInputType = config2.query_input_type?.trim();
167969
168119
  const truncate = config2.truncate?.trim();
167970
168120
  return {
167971
168121
  provider: "openai-compatible",
@@ -167973,6 +168123,7 @@ function resolveEmbeddingConfig(config2) {
167973
168123
  endpoint: config2.endpoint.trim(),
167974
168124
  ...apiKey ? { api_key: apiKey } : {},
167975
168125
  ...inputType ? { input_type: inputType } : {},
168126
+ ...queryInputType ? { query_input_type: queryInputType } : {},
167976
168127
  ...truncate ? { truncate } : {},
167977
168128
  ...config2.max_input_tokens ? {
167978
168129
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -167994,6 +168145,7 @@ function createProvider(config2) {
167994
168145
  model: config2.model,
167995
168146
  apiKey: config2.api_key,
167996
168147
  inputType: config2.input_type,
168148
+ queryInputType: config2.query_input_type,
167997
168149
  truncate: config2.truncate,
167998
168150
  maxInputTokens: config2.max_input_tokens
167999
168151
  });
@@ -168176,7 +168328,7 @@ function getOrCreateProjectProvider(registration) {
168176
168328
  registration.provider = provider;
168177
168329
  return provider;
168178
168330
  }
168179
- async function embedTextForProject(projectIdentity, text, signal) {
168331
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
168180
168332
  const registration = projectRegistrations.get(projectIdentity);
168181
168333
  if (!registration)
168182
168334
  return null;
@@ -168185,7 +168337,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
168185
168337
  const provider = getOrCreateProjectProvider(registration);
168186
168338
  if (!provider)
168187
168339
  return null;
168188
- const vector = await provider.embed(text, signal);
168340
+ const vector = await provider.embed(text, signal, purpose);
168189
168341
  if (!vector)
168190
168342
  return null;
168191
168343
  const current = projectRegistrations.get(projectIdentity);
@@ -168194,7 +168346,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
168194
168346
  }
168195
168347
  return { vector, modelId, generation };
168196
168348
  }
168197
- async function embedBatchForProject(projectIdentity, texts, signal) {
168349
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
168198
168350
  if (texts.length === 0) {
168199
168351
  const registration2 = projectRegistrations.get(projectIdentity);
168200
168352
  if (!registration2 || registration2.observationMode)
@@ -168210,7 +168362,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
168210
168362
  const provider = getOrCreateProjectProvider(registration);
168211
168363
  if (!provider)
168212
168364
  return null;
168213
- const vectors = await provider.embedBatch(texts, signal);
168365
+ const vectors = await provider.embedBatch(texts, signal, purpose);
168214
168366
  const current = projectRegistrations.get(projectIdentity);
168215
168367
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
168216
168368
  return null;
@@ -168267,7 +168419,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
168267
168419
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
168268
168420
  const prepared = [];
168269
168421
  for (const candidate of candidates) {
168270
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
168422
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
168271
168423
  if (canonicalText.length === 0) {
168272
168424
  noWork.push(candidate.id);
168273
168425
  continue;
@@ -168476,6 +168628,7 @@ function createProvider2(config2) {
168476
168628
  model: config2.model,
168477
168629
  apiKey: config2.api_key,
168478
168630
  inputType: config2.input_type,
168631
+ queryInputType: config2.query_input_type,
168479
168632
  truncate: config2.truncate,
168480
168633
  maxInputTokens: config2.max_input_tokens
168481
168634
  });
@@ -168912,7 +169065,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
168912
169065
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
168913
169066
  projectIdentity: reg.projectIdentity,
168914
169067
  sessionDirectoryOverride: reg.directory,
168915
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
169068
+ fallbackModels: resolveFallbackChain(reg.dreamerConfig.fallback_models)
168916
169069
  });
168917
169070
  } catch (error51) {
168918
169071
  log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
@@ -169792,7 +169945,7 @@ async function awaitInFlightDreamers() {
169792
169945
  function createPiDreamerClient(opts) {
169793
169946
  const runner2 = piSubagentRunnerFactory();
169794
169947
  const model = opts.config.model;
169795
- const fallbackModels = resolveFallbackChain(DREAMER_AGENT, opts.config.fallback_models);
169948
+ const fallbackModels = resolveFallbackChain(opts.config.fallback_models);
169796
169949
  const session = {
169797
169950
  create: async (args) => {
169798
169951
  const sessionId = `magic-context-pi-dream-${++sessionCounter}`;
@@ -170353,10 +170506,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
170353
170506
  }
170354
170507
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
170355
170508
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
170509
+ var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
170356
170510
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
170357
- var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
170358
170511
  var probeDataVersionStatements = new WeakMap;
170359
- var probeTotalChangesStatements = new WeakMap;
170360
170512
  function getProbeDataVersionStatement(db) {
170361
170513
  let stmt = probeDataVersionStatements.get(db);
170362
170514
  if (!stmt) {
@@ -170365,14 +170517,6 @@ function getProbeDataVersionStatement(db) {
170365
170517
  }
170366
170518
  return stmt;
170367
170519
  }
170368
- function getProbeTotalChangesStatement(db) {
170369
- let stmt = probeTotalChangesStatements.get(db);
170370
- if (!stmt) {
170371
- stmt = db.prepare(PROBE_TOTAL_CHANGES_SQL);
170372
- probeTotalChangesStatements.set(db, stmt);
170373
- }
170374
- return stmt;
170375
- }
170376
170520
  function isAssignmentRow(row) {
170377
170521
  if (row === null || typeof row !== "object") {
170378
170522
  return false;
@@ -170565,20 +170709,18 @@ function createTagger() {
170565
170709
  }
170566
170710
  function probeSignature(db) {
170567
170711
  const dvRow = getProbeDataVersionStatement(db).get();
170568
- const tcRow = getProbeTotalChangesStatement(db).get();
170569
170712
  return {
170570
- dataVersion: dvRow?.data_version ?? 0,
170571
- totalChanges: tcRow?.tc ?? 0
170713
+ dataVersion: dvRow?.data_version ?? 0
170572
170714
  };
170573
170715
  }
170574
- function initFromDb(sessionId, db) {
170716
+ function initFromDb(sessionId, db, floor = 0) {
170575
170717
  const probe = probeSignature(db);
170576
170718
  const cached2 = loadSignatures.get(sessionId);
170577
- if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.totalChanges === probe.totalChanges) {
170719
+ if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.floor === floor) {
170578
170720
  return;
170579
170721
  }
170580
170722
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
170581
- const assignmentRows = db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId).filter(isAssignmentRow);
170723
+ const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
170582
170724
  const sessionAssignments = getSessionAssignments(sessionId);
170583
170725
  sessionAssignments.clear();
170584
170726
  let maxTagNumber = 0;
@@ -170599,7 +170741,7 @@ function createTagger() {
170599
170741
  loadSignatures.set(sessionId, {
170600
170742
  db,
170601
170743
  dataVersion: probe.dataVersion,
170602
- totalChanges: probe.totalChanges
170744
+ floor
170603
170745
  });
170604
170746
  }
170605
170747
  function cleanup(sessionId) {
@@ -170623,6 +170765,162 @@ function createTagger() {
170623
170765
  };
170624
170766
  }
170625
170767
 
170768
+ // ../plugin/src/features/magic-context/transform-decision-log.ts
170769
+ var canonicalReasons = new Set([
170770
+ "system_hash",
170771
+ "model_change",
170772
+ "project_memory_epoch",
170773
+ "ttl_idle",
170774
+ "explicit_flush",
170775
+ "max_mutation_id",
170776
+ "first_render",
170777
+ "pressure_refold",
170778
+ "upgrade_state",
170779
+ "cached_m1_missing"
170780
+ ]);
170781
+ var piReasonAliases = {
170782
+ project_memory_change: "project_memory_epoch",
170783
+ pending_mutations: "max_mutation_id",
170784
+ renderer_upgrade: "upgrade_state",
170785
+ cache_invalid: "cached_m1_missing",
170786
+ drift: "pressure_refold"
170787
+ };
170788
+ var sharedReasonAliases = {
170789
+ model_key: "model_change",
170790
+ pressure: "pressure_refold"
170791
+ };
170792
+ var pendingDecisionBySession = new Map;
170793
+ var pendingPiDecisionBySession = new Map;
170794
+ var lastBoundMessageIdBySession = new Map;
170795
+ var scheduledWriteTokensBySession = new Map;
170796
+ var writerOverrideForTests = null;
170797
+ function normalizeMaterializeReason(harness, reason, rematerialized) {
170798
+ const raw = typeof reason === "string" ? reason.trim() : "";
170799
+ if (raw.length > 0) {
170800
+ const alias = sharedReasonAliases[raw] ?? (harness === "pi" ? piReasonAliases[raw] : undefined) ?? undefined;
170801
+ if (alias)
170802
+ return alias;
170803
+ if (canonicalReasons.has(raw))
170804
+ return raw;
170805
+ return null;
170806
+ }
170807
+ return rematerialized ? "pressure_refold" : null;
170808
+ }
170809
+ function recordPendingPiTransformDecision(sessionId, decision, snapshotNewestAssistantEntryId) {
170810
+ if (!decision.bustedThisPass)
170811
+ return;
170812
+ pendingPiDecisionBySession.set(sessionId, {
170813
+ ...decision,
170814
+ snapshotNewestAssistantEntryId
170815
+ });
170816
+ }
170817
+ function findNewestPiAssistantEntryId(entries) {
170818
+ if (!Array.isArray(entries))
170819
+ return null;
170820
+ for (let i = entries.length - 1;i >= 0; i--) {
170821
+ const entry = entries[i];
170822
+ if (!entry || typeof entry !== "object")
170823
+ continue;
170824
+ const row = entry;
170825
+ if (row.type !== "message" || typeof row.id !== "string" || row.id.length === 0) {
170826
+ continue;
170827
+ }
170828
+ const message = row.message;
170829
+ if (message && typeof message === "object" && message.role === "assistant") {
170830
+ return row.id;
170831
+ }
170832
+ }
170833
+ return null;
170834
+ }
170835
+ function schedulePiTransformDecisionResolve(args) {
170836
+ const pending = pendingPiDecisionBySession.get(args.sessionId);
170837
+ if (!pending)
170838
+ return false;
170839
+ const targetMessageId = findNewestPiAssistantEntryIdAfter(args.branchEntries, pending.snapshotNewestAssistantEntryId);
170840
+ if (!targetMessageId)
170841
+ return false;
170842
+ const dbPath = getDatabasePath(args.db);
170843
+ if (!dbPath)
170844
+ return false;
170845
+ pendingPiDecisionBySession.delete(args.sessionId);
170846
+ const token = addScheduledWriteToken(args.sessionId);
170847
+ setTimeout(() => {
170848
+ try {
170849
+ if (!hasScheduledWriteToken(args.sessionId, token))
170850
+ return;
170851
+ writeTransformDecisionBestEffort(dbPath, {
170852
+ ...pending,
170853
+ sessionId: args.sessionId,
170854
+ harness: "pi",
170855
+ messageId: targetMessageId
170856
+ });
170857
+ } finally {
170858
+ deleteScheduledWriteToken(args.sessionId, token);
170859
+ }
170860
+ }, 0);
170861
+ return true;
170862
+ }
170863
+ function addScheduledWriteToken(sessionId) {
170864
+ const token = Symbol(sessionId);
170865
+ let tokens = scheduledWriteTokensBySession.get(sessionId);
170866
+ if (!tokens) {
170867
+ tokens = new Set;
170868
+ scheduledWriteTokensBySession.set(sessionId, tokens);
170869
+ }
170870
+ tokens.add(token);
170871
+ return token;
170872
+ }
170873
+ function hasScheduledWriteToken(sessionId, token) {
170874
+ return scheduledWriteTokensBySession.get(sessionId)?.has(token) === true;
170875
+ }
170876
+ function deleteScheduledWriteToken(sessionId, token) {
170877
+ const tokens = scheduledWriteTokensBySession.get(sessionId);
170878
+ if (!tokens)
170879
+ return;
170880
+ tokens.delete(token);
170881
+ if (tokens.size === 0)
170882
+ scheduledWriteTokensBySession.delete(sessionId);
170883
+ }
170884
+ function findNewestPiAssistantEntryIdAfter(entries, snapshotNewestAssistantEntryId) {
170885
+ if (!Array.isArray(entries))
170886
+ return null;
170887
+ for (let i = entries.length - 1;i >= 0; i--) {
170888
+ const entry = entries[i];
170889
+ if (!entry || typeof entry !== "object")
170890
+ continue;
170891
+ const row = entry;
170892
+ if (row.type !== "message" || typeof row.id !== "string" || row.id.length === 0) {
170893
+ continue;
170894
+ }
170895
+ if (snapshotNewestAssistantEntryId !== null && row.id === snapshotNewestAssistantEntryId) {
170896
+ continue;
170897
+ }
170898
+ const message = row.message;
170899
+ if (message && typeof message === "object" && message.role === "assistant") {
170900
+ return row.id;
170901
+ }
170902
+ }
170903
+ return null;
170904
+ }
170905
+ function writeTransformDecisionBestEffort(dbPath, row) {
170906
+ try {
170907
+ const writer = writerOverrideForTests ?? writeTransformDecisionRow;
170908
+ writer(dbPath, row);
170909
+ } catch {}
170910
+ }
170911
+ function writeTransformDecisionRow(dbPath, row) {
170912
+ const db = new Database(dbPath);
170913
+ try {
170914
+ db.exec("PRAGMA busy_timeout=0");
170915
+ db.prepare(`INSERT OR REPLACE INTO transform_decisions (
170916
+ session_id, harness, message_id, ts_ms, decision, materialized,
170917
+ materialize_reason, emergency, dropped_tokens, dropped_count, input_tokens
170918
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.sessionId, row.harness, row.messageId, row.tsMs, row.decision, row.materialized ? 1 : 0, row.materializeReason, row.emergency ? 1 : 0, Math.max(0, Math.floor(row.droppedTokens)), Math.max(0, Math.floor(row.droppedCount)), Math.max(0, Math.floor(row.inputTokens)));
170919
+ } finally {
170920
+ closeQuietly(db);
170921
+ }
170922
+ }
170923
+
170626
170924
  // ../plugin/src/features/magic-context/work-metrics.ts
170627
170925
  function asNumber2(value) {
170628
170926
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -171954,7 +172252,7 @@ function resolveBoundaryContext(args) {
171954
172252
  }
171955
172253
  let storedTokenTotals;
171956
172254
  try {
171957
- storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId).totals;
172255
+ storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId, args.taggerFloor ?? 0).totals;
171958
172256
  } catch (error51) {
171959
172257
  sessionLog(args.sessionId, "protected-tail stored-token map unavailable (live fallback):", error51);
171960
172258
  }
@@ -172071,6 +172369,38 @@ var DEFAULT_MIN_COMMIT_CLUSTERS_FOR_TRIGGER = 3;
172071
172369
  var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
172072
172370
  var FORCE_COMPARTMENT_PERCENTAGE = 80;
172073
172371
  var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
172372
+ var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
172373
+ function tagOwnerMessageId(row) {
172374
+ if (row.type === "tool")
172375
+ return row.tool_owner_message_id ?? row.message_id;
172376
+ return row.message_id.replace(CONTENT_TAG_OWNER_SUFFIX, "");
172377
+ }
172378
+ function getActiveOrDroppedTagOwnerMessageIds(db, sessionId, floor = 0) {
172379
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id
172380
+ FROM tags
172381
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id
172382
+ FROM tags
172383
+ WHERE session_id = ? AND status IN ('active', 'dropped')`).all(sessionId);
172384
+ const owners = new Set;
172385
+ for (const row of rows)
172386
+ owners.add(tagOwnerMessageId(row));
172387
+ return owners;
172388
+ }
172389
+ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor = 0) {
172390
+ const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
172391
+ const coveredOwnerMessageIds = getActiveOrDroppedTagOwnerMessageIds(db, sessionId, taggerFloor);
172392
+ let total = 0;
172393
+ for (const message of inMemoryTail.messages) {
172394
+ if (message.ordinal <= lastCompartmentEnd)
172395
+ continue;
172396
+ if (coveredOwnerMessageIds.has(message.id))
172397
+ continue;
172398
+ total += estimateTrueRawMessageTokens(message, {
172399
+ providerShapeVersion: "opencode-v1"
172400
+ }).total;
172401
+ }
172402
+ return total;
172403
+ }
172074
172404
  function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
172075
172405
  return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
172076
172406
  }
@@ -172124,7 +172454,7 @@ function resolveBoundaryContextLimit(usage, fallbackContextLimit) {
172124
172454
  }
172125
172455
  return 128000;
172126
172456
  }
172127
- function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail) {
172457
+ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor = 0) {
172128
172458
  return withRawSessionMessageCache(() => {
172129
172459
  try {
172130
172460
  const memoryPrimed = inMemoryTail ? primeInMemoryTailRawMessageCache({
@@ -172153,7 +172483,8 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
172153
172483
  contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
172154
172484
  executeThresholdPercentage,
172155
172485
  usage,
172156
- usageSource: "live"
172486
+ usageSource: "live",
172487
+ taggerFloor
172157
172488
  });
172158
172489
  const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
172159
172490
  if (!hasProtectedEligibleHead) {
@@ -172184,7 +172515,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
172184
172515
  }
172185
172516
  });
172186
172517
  }
172187
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail) {
172518
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride) {
172188
172519
  if (sessionMeta.compartmentInProgress) {
172189
172520
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
172190
172521
  return { shouldFire: false };
@@ -172198,14 +172529,17 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
172198
172529
  inMemoryTail = undefined;
172199
172530
  }
172200
172531
  }
172532
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
172201
172533
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
172202
- if (!inMemoryTail && usage.percentage < proactiveFloorForGate) {
172534
+ if (usage.percentage < proactiveFloorForGate) {
172203
172535
  try {
172204
- const { bound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId);
172536
+ const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, taggerFloor);
172205
172537
  if (nullCount === 0) {
172206
- const eligibleUpperBound = bound;
172538
+ const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
172539
+ const eligibleUpperBound = persistedBound + untaggedUpperBound;
172207
172540
  if (eligibleUpperBound < triggerBudget) {
172208
- sessionLog(sessionId, `compartment trigger: cheap-skip at ${usage.percentage.toFixed(1)}% (below proactive floor ${proactiveFloorForGate}%) — live-tail upper bound ${eligibleUpperBound} < triggerBudget ${triggerBudget}; no size trigger possible, skipped full raw read`);
172541
+ const memorySuffix = inMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
172542
+ sessionLog(sessionId, `compartment trigger: cheap-skip at ${usage.percentage.toFixed(1)}% (below proactive floor ${proactiveFloorForGate}%) — live-tail upper bound ${eligibleUpperBound}${memorySuffix} < triggerBudget ${triggerBudget}; no size trigger possible, skipped full raw read`);
172209
172543
  return { shouldFire: false };
172210
172544
  }
172211
172545
  }
@@ -172213,7 +172547,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
172213
172547
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
172214
172548
  }
172215
172549
  }
172216
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail);
172550
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
172217
172551
  if (!tailInfo.hasNewRawHistory) {
172218
172552
  try {
172219
172553
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -174527,7 +174861,7 @@ async function runAutoSearchHintForPi(args) {
174527
174861
  embeddingEnabled,
174528
174862
  gitCommitsEnabled,
174529
174863
  embedQuery: async (text, signal) => {
174530
- const result = await embedTextForProject(options.projectPath, text, signal);
174864
+ const result = await embedTextForProject(options.projectPath, text, signal, "query");
174531
174865
  return result?.vector ?? null;
174532
174866
  },
174533
174867
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -175157,6 +175491,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
175157
175491
  const protectedCutoff = maxTag - config2.protectedTags;
175158
175492
  const toolAgeCutoff = protectedCutoff;
175159
175493
  let droppedTools = 0;
175494
+ let emergencyDroppedTools = 0;
175160
175495
  let deduplicatedTools = 0;
175161
175496
  let droppedInjections = 0;
175162
175497
  let droppedStaleReduceCalls = 0;
@@ -175189,6 +175524,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
175189
175524
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
175190
175525
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
175191
175526
  droppedTools++;
175527
+ emergencyDroppedTools++;
175192
175528
  }
175193
175529
  }
175194
175530
  setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
@@ -175317,6 +175653,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
175317
175653
  deduplicatedTools,
175318
175654
  droppedInjections,
175319
175655
  droppedStaleReduceCalls,
175656
+ emergencyDroppedTools,
175320
175657
  compressedTextTags,
175321
175658
  mutatedTextTags
175322
175659
  };
@@ -176614,7 +176951,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
176614
176951
  for (const compartment of compartments) {
176615
176952
  try {
176616
176953
  const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
176617
- const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage);
176954
+ const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage) || buildCompartmentSummaryFallbackText(db, compartment.id);
176618
176955
  if (canonicalText.length === 0)
176619
176956
  continue;
176620
176957
  const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
@@ -179807,6 +180144,7 @@ async function runPiHistorian(deps) {
179807
180144
  } else {
179808
180145
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
179809
180146
  }
180147
+ clearEmergencyDrainLatch(db, sessionId);
179810
180148
  return;
179811
180149
  }
179812
180150
  const perRunCap = selectPerRunCap(boundarySnapshot);
@@ -179818,7 +180156,8 @@ async function runPiHistorian(deps) {
179818
180156
  trueRawTokens: boundarySnapshot.trueRawEligibleTokens,
179819
180157
  usagePercentage: boundarySnapshot.usagePercentage,
179820
180158
  usable,
179821
- perRunCap
180159
+ perRunCap,
180160
+ executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
179822
180161
  });
179823
180162
  if (!reserve.ok) {
179824
180163
  sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
@@ -179835,6 +180174,7 @@ async function runPiHistorian(deps) {
179835
180174
  } else {
179836
180175
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
179837
180176
  }
180177
+ clearEmergencyDrainLatch(db, sessionId);
179838
180178
  telemetry.status = "noop";
179839
180179
  telemetry.failureReason = "chunk empty after filtering";
179840
180180
  rollbackDrainReservation();
@@ -180066,6 +180406,7 @@ ${chunkText}`,
180066
180406
  }
180067
180407
  appendCompartments(db, sessionId, newCompartments);
180068
180408
  clearHistorianFailureState(db, sessionId);
180409
+ clearHistorianDrainFailure(db, sessionId);
180069
180410
  recordProtectedTailPublicationFloor(db, sessionId, lastNewEnd + 1);
180070
180411
  clearEmergencyRecovery(db, sessionId);
180071
180412
  if (firstKeptEntryId && lastNewEndMessageId) {
@@ -180162,8 +180503,12 @@ ${chunkText}`,
180162
180503
  await notify(buildHistorianFailureNotice(failCount, desc.brief));
180163
180504
  }
180164
180505
  } finally {
180165
- if (!completedSuccessfully && !retainDrainReservationForRetryThrottle) {
180166
- rollbackDrainReservation();
180506
+ if (!completedSuccessfully) {
180507
+ if (!retainDrainReservationForRetryThrottle) {
180508
+ rollbackDrainReservation();
180509
+ } else {
180510
+ recordHistorianDrainFailure(db, sessionId);
180511
+ }
180167
180512
  }
180168
180513
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
180169
180514
  try {
@@ -180402,7 +180747,7 @@ var INLINE_THINKING_PATTERNS = [
180402
180747
  /<thinking>[\s\S]*?<\/thinking>\s*/gi,
180403
180748
  /<think>[\s\S]*?<\/think>\s*/gi
180404
180749
  ];
180405
- var CLEARED = "[cleared]";
180750
+ var CLEARED = "";
180406
180751
  function stripInlineThinkingMarkup(text) {
180407
180752
  let cleaned = text;
180408
180753
  for (const pattern of INLINE_THINKING_PATTERNS) {
@@ -180451,8 +180796,11 @@ function clearOldReasoningPi(args) {
180451
180796
  for (const part of msg.content) {
180452
180797
  if (part && typeof part === "object" && part.type === "thinking") {
180453
180798
  const tp = part;
180454
- if (tp.thinking !== CLEARED) {
180799
+ if (tp.redacted)
180800
+ continue;
180801
+ if (tp.thinking !== CLEARED || tp.thinkingSignature !== undefined) {
180455
180802
  tp.thinking = CLEARED;
180803
+ tp.thinkingSignature = undefined;
180456
180804
  cleared++;
180457
180805
  }
180458
180806
  }
@@ -180530,8 +180878,11 @@ function replayClearedReasoningPi(args) {
180530
180878
  for (const part of msg.content) {
180531
180879
  if (part && typeof part === "object" && part.type === "thinking") {
180532
180880
  const tp = part;
180533
- if (tp.thinking !== CLEARED) {
180881
+ if (tp.redacted)
180882
+ continue;
180883
+ if (tp.thinking !== CLEARED || tp.thinkingSignature !== undefined) {
180534
180884
  tp.thinking = CLEARED;
180885
+ tp.thinkingSignature = undefined;
180535
180886
  cleared++;
180536
180887
  }
180537
180888
  }
@@ -181846,6 +182197,11 @@ function registerPiContextHandler(pi, baseOptions) {
181846
182197
  updateSessionProjectTracking(sessionId, projectIdentity, options.db);
181847
182198
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
181848
182199
  const branchEntries = readPiBranchEntriesForContext(ctx, sessionId);
182200
+ schedulePiTransformDecisionResolve({
182201
+ db: options.db,
182202
+ sessionId,
182203
+ branchEntries
182204
+ });
181849
182205
  const rawMessageProvider = {
181850
182206
  readMessages: () => branchEntries !== null ? convertEntriesToRawMessages([...branchEntries]) : readPiSessionMessages(ctx),
181851
182207
  readMessageById: (messageId) => readPiSessionMessageById(ctx, messageId)
@@ -182082,6 +182438,20 @@ function registerPiContextHandler(pi, baseOptions) {
182082
182438
  appendCompaction: resolvePiAppendCompaction(ctx),
182083
182439
  readBranchEntries: resolvePiReadBranchEntries(ctx)
182084
182440
  });
182441
+ const piDecisionSnapshotNewestAssistant = branchEntries === null ? undefined : findNewestPiAssistantEntryId(branchEntries);
182442
+ if (result.bustedThisPass && piDecisionSnapshotNewestAssistant !== undefined) {
182443
+ recordPendingPiTransformDecision(sessionId, {
182444
+ tsMs: Date.now(),
182445
+ decision: schedulerDecision,
182446
+ materialized: result.materialized,
182447
+ materializeReason: normalizeMaterializeReason("pi", result.materializeReason, result.materialized),
182448
+ emergency: result.emergency,
182449
+ droppedTokens: result.droppedTokens,
182450
+ droppedCount: result.droppedCount,
182451
+ inputTokens: usageInputTokens,
182452
+ bustedThisPass: true
182453
+ }, piDecisionSnapshotNewestAssistant);
182454
+ }
182085
182455
  if (options.historian) {
182086
182456
  maybeFireHistorian({
182087
182457
  ctx,
@@ -182578,6 +182948,11 @@ async function runPipeline(args) {
182578
182948
  let pendingOpsAppliedThisPass = false;
182579
182949
  let pendingOpsDidMutate = false;
182580
182950
  let heuristicOrReasoningDidMutate = false;
182951
+ let didMutateFromFlushedStatuses = false;
182952
+ let droppedCount = 0;
182953
+ const droppedTokens = 0;
182954
+ let emergency = false;
182955
+ let autoReclaimDidMutateThisPass = false;
182581
182956
  let suppressDeferredHistoryDrain = false;
182582
182957
  let casLost = false;
182583
182958
  const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
@@ -182664,6 +183039,9 @@ async function runPipeline(args) {
182664
183039
  try {
182665
183040
  const tApplyPending = performance.now();
182666
183041
  pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
183042
+ if (pendingOpsDidMutate) {
183043
+ droppedCount += pendingOps.length;
183044
+ }
182667
183045
  logTransformTiming(args.sessionId, "applyPendingOperations", tApplyPending);
182668
183046
  executedWorkThisPass = true;
182669
183047
  materializationSatisfiedThisPass = true;
@@ -182685,7 +183063,7 @@ async function runPipeline(args) {
182685
183063
  const flushedSliceTags = getTagsByNumbers(args.db, args.sessionId, targetTagNumbers);
182686
183064
  logTransformTiming(args.sessionId, "getTagsByNumbers", tGetTags, `targets=${targetTagNumbers.length} fetched=${flushedSliceTags.length}`);
182687
183065
  const tFlushed = performance.now();
182688
- applyFlushedStatuses(args.sessionId, args.db, targets, flushedSliceTags);
183066
+ didMutateFromFlushedStatuses = applyFlushedStatuses(args.sessionId, args.db, targets, flushedSliceTags);
182689
183067
  logTransformTiming(args.sessionId, "applyFlushedStatuses", tFlushed);
182690
183068
  logTransformTiming(args.sessionId, "batchFinalize:flushed", tFlushed);
182691
183069
  const messageIdToMaxTag = buildMessageIdToMaxTag(targets);
@@ -182748,6 +183126,8 @@ async function runPipeline(args) {
182748
183126
  caveman: args.heuristics.caveman
182749
183127
  }, activeTags, stableIdResolver);
182750
183128
  const heuristicMutationCount = heuristicsResult.droppedTools + heuristicsResult.deduplicatedTools + heuristicsResult.droppedInjections + heuristicsResult.droppedStaleReduceCalls + heuristicsResult.mutatedTextTags;
183129
+ droppedCount += heuristicsResult.droppedTools + heuristicsResult.deduplicatedTools + heuristicsResult.droppedInjections + heuristicsResult.droppedStaleReduceCalls + heuristicsResult.mutatedTextTags;
183130
+ emergency ||= heuristicsResult.emergencyDroppedTools > 0;
182751
183131
  if (heuristicMutationCount > 0)
182752
183132
  heuristicOrReasoningDidMutate = true;
182753
183133
  heuristicsExecuted = true;
@@ -182797,6 +183177,7 @@ async function runPipeline(args) {
182797
183177
  logTransformTiming(args.sessionId, "watermarkCleanup", tClearReasoning);
182798
183178
  if (clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
182799
183179
  heuristicOrReasoningDidMutate = true;
183180
+ droppedCount += clearOutcome.cleared + stripOutcome.stripped;
182800
183181
  }
182801
183182
  if (combinedWatermark > prevWatermark || clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
182802
183183
  executedWorkThisPass = true;
@@ -182822,6 +183203,10 @@ async function runPipeline(args) {
182822
183203
  autoReclaimTargetCount = syntheticPendingOps.length;
182823
183204
  if (syntheticPendingOps.length > 0) {
182824
183205
  autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, [], syntheticPendingOps);
183206
+ if (autoReclaimDidMutate) {
183207
+ droppedCount += syntheticPendingOps.length;
183208
+ autoReclaimDidMutateThisPass = true;
183209
+ }
182825
183210
  }
182826
183211
  }
182827
183212
  transcript.commit();
@@ -182930,6 +183315,9 @@ async function runPipeline(args) {
182930
183315
  } catch (err) {
182931
183316
  sessionLog(args.sessionId, `token accounting failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182932
183317
  }
183318
+ const materialized = injectionResult?.m0Materialized === true;
183319
+ const materializeReason = injectionResult?.m0Reason ?? null;
183320
+ const bustedThisPass = didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || materialized || historyWasConsumedThisPass;
182933
183321
  return {
182934
183322
  messages: outputMessages,
182935
183323
  heuristicsExecuted,
@@ -182938,6 +183326,12 @@ async function runPipeline(args) {
182938
183326
  syntheticLeadingCount: injectionResult?.syntheticLeadingCount ?? 0,
182939
183327
  heuristicsResult,
182940
183328
  injectionResult,
183329
+ materialized,
183330
+ materializeReason,
183331
+ droppedTokens,
183332
+ droppedCount,
183333
+ emergency,
183334
+ bustedThisPass,
182941
183335
  targetCount: targets.size,
182942
183336
  reasoningWatermark: getOrCreateSessionMeta(args.db, args.sessionId).clearedReasoningThroughTag ?? 0,
182943
183337
  activeTags,
@@ -183364,6 +183758,12 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
183364
183758
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
183365
183759
  import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
183366
183760
  import { join as join16 } from "node:path";
183761
+
183762
+ // ../plugin/src/agents/historian.ts
183763
+ var HISTORIAN_AGENT = "historian";
183764
+ var HISTORIAN_EDITOR_AGENT = "historian-editor";
183765
+
183766
+ // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
183367
183767
  init_data_path();
183368
183768
  function historianResponseDumpDir(directory) {
183369
183769
  return getProjectMagicContextHistorianDir(directory);
@@ -185617,7 +186017,7 @@ function formatThresholdPercent(value) {
185617
186017
  // package.json
185618
186018
  var package_default = {
185619
186019
  name: "@wolfx/pi-magic-context",
185620
- version: "0.25.0",
186020
+ version: "0.26.0",
185621
186021
  type: "module",
185622
186022
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
185623
186023
  main: "dist/index.js",
@@ -185643,7 +186043,7 @@ var package_default = {
185643
186043
  "README.md"
185644
186044
  ],
185645
186045
  scripts: {
185646
- build: "bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external better-sqlite3",
186046
+ build: "bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external node:sqlite",
185647
186047
  typecheck: "tsc --noEmit",
185648
186048
  test: "bun test",
185649
186049
  lint: "biome check src",
@@ -185666,7 +186066,6 @@ var package_default = {
185666
186066
  "@earendil-works/pi-tui": "^0.74.0",
185667
186067
  "@types/better-sqlite3": "^7.6.13",
185668
186068
  "@types/bun": "^1.3.10",
185669
- "better-sqlite3": "^12.9.0",
185670
186069
  "@types/node": "^22.0.0",
185671
186070
  typescript: "^5.8.0"
185672
186071
  },
@@ -191519,7 +191918,7 @@ function createCtxSearchTool(deps) {
191519
191918
  memoryEnabled,
191520
191919
  embeddingEnabled,
191521
191920
  embedQuery: async (text, signal) => {
191522
- const result = await embedTextForProject(projectIdentity, text, signal);
191921
+ const result = await embedTextForProject(projectIdentity, text, signal, "query");
191523
191922
  return result?.vector ?? null;
191524
191923
  },
191525
191924
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -191747,7 +192146,7 @@ function resolveSidekickFromConfig(config2) {
191747
192146
  systemPrompt: sidekick.system_prompt,
191748
192147
  timeoutMs: sidekick.timeout_ms,
191749
192148
  thinking_level: sidekick.thinking_level,
191750
- fallbackModels: resolveFallbackChain("sidekick", sidekick.fallback_models)
192149
+ fallbackModels: resolveFallbackChain(sidekick.fallback_models)
191751
192150
  };
191752
192151
  }
191753
192152
  function resolveHistorianFromConfig(config2) {
@@ -191759,7 +192158,7 @@ function resolveHistorianFromConfig(config2) {
191759
192158
  return;
191760
192159
  const historianContextLimit = resolveHistorianContextLimit(model);
191761
192160
  const historianChunkTokens = deriveHistorianChunkTokens(historianContextLimit);
191762
- const fallbackModels = resolveFallbackChain("historian", historian?.fallback_models);
192161
+ const fallbackModels = resolveFallbackChain(historian?.fallback_models);
191763
192162
  return {
191764
192163
  runner: new PiSubagentRunner,
191765
192164
  model,