@wolfx/pi-magic-context 0.24.1 → 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
@@ -8275,6 +8275,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
8275
8275
  }
8276
8276
  super(typeof filename === "string" ? filename : ":memory:", translated);
8277
8277
  }
8278
+ prepare(sql) {
8279
+ const stmt = super.prepare(sql);
8280
+ for (const method of ["run", "get", "all"]) {
8281
+ const original = stmt[method].bind(stmt);
8282
+ stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
8283
+ }
8284
+ return stmt;
8285
+ }
8278
8286
  transaction(fn) {
8279
8287
  const self = this;
8280
8288
  const wrapped = function(...args) {
@@ -141059,6 +141067,8 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
141059
141067
  var encoder = new TextEncoder;
141060
141068
  var TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
141061
141069
  var MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
141070
+ var DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
141071
+ var DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
141062
141072
  var COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
141063
141073
  var MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
141064
141074
  var STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
@@ -141071,6 +141081,9 @@ function stripCompleteTagPairsGlobally(value) {
141071
141081
  function stripMalformedTagNotationGlobally(value) {
141072
141082
  return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
141073
141083
  }
141084
+ function stripDanglingTagNotationGlobally(value) {
141085
+ return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
141086
+ }
141074
141087
  function stripTagSectionCharacters(value) {
141075
141088
  return value.replace(STRAY_SECTION_CHAR_REGEX, "");
141076
141089
  }
@@ -141078,6 +141091,7 @@ function stripPersistedAssistantText(value) {
141078
141091
  let text = stripWellFormedLeadingTagPrefix(value);
141079
141092
  text = stripCompleteTagPairsGlobally(text);
141080
141093
  text = stripMalformedTagNotationGlobally(text);
141094
+ text = stripDanglingTagNotationGlobally(text);
141081
141095
  text = stripTagSectionCharacters(text);
141082
141096
  return text.trim();
141083
141097
  }
@@ -141090,6 +141104,7 @@ function stripTagPrefix(value) {
141090
141104
  const prev = stripped;
141091
141105
  stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
141092
141106
  stripped = stripped.replace(TAG_PREFIX_REGEX, "");
141107
+ stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
141093
141108
  if (stripped === prev)
141094
141109
  break;
141095
141110
  }
@@ -141197,6 +141212,24 @@ class ToolMutationBatch {
141197
141212
  }
141198
141213
 
141199
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
+ }
141200
141233
  var activeRawMessageCache = null;
141201
141234
  var activeAbsoluteCountCache = null;
141202
141235
  var sessionProviders = new Map;
@@ -141379,7 +141412,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
141379
141412
  if (!currentBlock)
141380
141413
  return true;
141381
141414
  const blockText = formatBlock(currentBlock);
141382
- const blockTokens = estimateTokens(blockText);
141415
+ const blockTokens = estimateBlockTokens(blockText);
141383
141416
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
141384
141417
  return false;
141385
141418
  }
@@ -141957,6 +141990,7 @@ var SESSION_META_SELECT_COLUMNS = [
141957
141990
  "conversation_tokens",
141958
141991
  "tool_call_tokens",
141959
141992
  "cleared_reasoning_through_tag",
141993
+ "tool_reclaim_watermark",
141960
141994
  "last_todo_state",
141961
141995
  "cached_m0_bytes",
141962
141996
  "cached_m1_bytes",
@@ -141983,6 +142017,8 @@ var SESSION_META_SELECT_COLUMNS = [
141983
142017
  "recovery_no_eligible_head_count",
141984
142018
  "force_emergency_bypass_window_start",
141985
142019
  "force_emergency_bypass_used",
142020
+ "emergency_drain_active",
142021
+ "historian_drain_failure_at",
141986
142022
  "upgrade_reminded_at",
141987
142023
  "pi_stable_id_scheme"
141988
142024
  ];
@@ -142005,6 +142041,7 @@ var META_COLUMNS = {
142005
142041
  conversationTokens: "conversation_tokens",
142006
142042
  toolCallTokens: "tool_call_tokens",
142007
142043
  clearedReasoningThroughTag: "cleared_reasoning_through_tag",
142044
+ toolReclaimWatermark: "tool_reclaim_watermark",
142008
142045
  lastTodoState: "last_todo_state",
142009
142046
  cachedM0Bytes: "cached_m0_bytes",
142010
142047
  cachedM1Bytes: "cached_m1_bytes",
@@ -142031,6 +142068,8 @@ var META_COLUMNS = {
142031
142068
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
142032
142069
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
142033
142070
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
142071
+ emergencyDrainActive: "emergency_drain_active",
142072
+ historianDrainFailureAt: "historian_drain_failure_at",
142034
142073
  upgradeRemindedAt: "upgrade_reminded_at",
142035
142074
  piStableIdScheme: "pi_stable_id_scheme"
142036
142075
  };
@@ -142073,7 +142112,7 @@ function isSessionMetaRow(row) {
142073
142112
  if (row === null || typeof row !== "object")
142074
142113
  return false;
142075
142114
  const r = row;
142076
- 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);
142115
+ 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);
142077
142116
  }
142078
142117
  function getDefaultSessionMeta(sessionId) {
142079
142118
  return {
@@ -142096,6 +142135,7 @@ function getDefaultSessionMeta(sessionId) {
142096
142135
  conversationTokens: 0,
142097
142136
  toolCallTokens: 0,
142098
142137
  clearedReasoningThroughTag: 0,
142138
+ toolReclaimWatermark: 0,
142099
142139
  lastTodoState: "",
142100
142140
  cachedM0Bytes: null,
142101
142141
  cachedM1Bytes: null,
@@ -142159,6 +142199,7 @@ function toSessionMeta(row) {
142159
142199
  conversationTokens: numOrZero(row.conversation_tokens),
142160
142200
  toolCallTokens: numOrZero(row.tool_call_tokens),
142161
142201
  clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
142202
+ toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
142162
142203
  lastTodoState: lastTodoStateRaw,
142163
142204
  cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
142164
142205
  cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
@@ -142930,8 +142971,9 @@ function backfillToolOwnersInChunks(db, result) {
142930
142971
  var databases = new Map;
142931
142972
  var persistenceByDatabase = new WeakMap;
142932
142973
  var persistenceErrorByDatabase = new WeakMap;
142974
+ var pathByDatabase = new WeakMap;
142933
142975
  var lastSchemaFenceRejection = null;
142934
- var LATEST_SUPPORTED_VERSION = 36;
142976
+ var LATEST_SUPPORTED_VERSION = 38;
142935
142977
  function resolveDatabasePath(dbPathOverride) {
142936
142978
  if (dbPathOverride) {
142937
142979
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -142944,6 +142986,9 @@ function resolveDatabasePath(dbPathOverride) {
142944
142986
  const dbDir = getMagicContextStorageDir();
142945
142987
  return { dbDir, dbPath: join5(dbDir, "context.db") };
142946
142988
  }
142989
+ function getDatabasePath(db) {
142990
+ return pathByDatabase.get(db) ?? null;
142991
+ }
142947
142992
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
142948
142993
  if (existsSync4(targetDbPath))
142949
142994
  return;
@@ -143458,6 +143503,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143458
143503
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
143459
143504
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
143460
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,
143461
143508
  cached_m0_materialized_at INTEGER,
143462
143509
  cached_m0_session_facts_version INTEGER,
143463
143510
  cached_m0_upgrade_state TEXT,
@@ -143521,6 +143568,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143521
143568
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
143522
143569
  ON historian_runs(status, created_at DESC);
143523
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
+
143524
143588
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
143525
143589
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
143526
143590
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -143598,6 +143662,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143598
143662
  ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
143599
143663
  ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
143600
143664
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
143665
+ ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
143601
143666
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
143602
143667
  ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
143603
143668
  ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
@@ -143671,6 +143736,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143671
143736
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
143672
143737
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
143673
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");
143674
143741
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
143675
143742
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
143676
143743
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -143749,6 +143816,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143749
143816
  failed_at INTEGER NOT NULL,
143750
143817
  UNIQUE(table_name, row_id)
143751
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);
143752
143835
  `);
143753
143836
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143754
143837
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -143835,7 +143918,9 @@ function healNullIntegerColumns(db) {
143835
143918
  ["protected_tail_drain_tokens", 0],
143836
143919
  ["recovery_no_eligible_head_count", 0],
143837
143920
  ["force_emergency_bypass_window_start", 0],
143838
- ["force_emergency_bypass_used", 0]
143921
+ ["force_emergency_bypass_used", 0],
143922
+ ["emergency_drain_active", 0],
143923
+ ["historian_drain_failure_at", 0]
143839
143924
  ];
143840
143925
  for (const [column, fallback] of columns) {
143841
143926
  try {
@@ -143911,6 +143996,7 @@ function openDatabase(dbPathOrOptions) {
143911
143996
  setDatabase(db);
143912
143997
  loadToolDefinitionMeasurements(db);
143913
143998
  databases.set(dbPath, db);
143999
+ pathByDatabase.set(db, dbPath);
143914
144000
  persistenceByDatabase.set(db, true);
143915
144001
  persistenceErrorByDatabase.delete(db);
143916
144002
  return db;
@@ -145152,6 +145238,41 @@ var MIGRATIONS = [
145152
145238
  `);
145153
145239
  }
145154
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
+ }
145155
145276
  }
145156
145277
  ];
145157
145278
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -145583,7 +145704,9 @@ var DEFAULT_PROTECTED_TAIL_META = {
145583
145704
  protectedTailDrainTokens: 0,
145584
145705
  recoveryNoEligibleHeadCount: 0,
145585
145706
  forceEmergencyBypassWindowStart: 0,
145586
- forceEmergencyBypassUsed: 0
145707
+ forceEmergencyBypassUsed: 0,
145708
+ emergencyDrainActive: 0,
145709
+ historianDrainFailureAt: 0
145587
145710
  };
145588
145711
  function toProtectedTailMeta(row) {
145589
145712
  if (row === null || typeof row !== "object")
@@ -145597,7 +145720,9 @@ function toProtectedTailMeta(row) {
145597
145720
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
145598
145721
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
145599
145722
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
145600
- 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)
145601
145726
  };
145602
145727
  }
145603
145728
  function loadProtectedTailMeta(db, sessionId) {
@@ -145605,7 +145730,7 @@ function loadProtectedTailMeta(db, sessionId) {
145605
145730
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
145606
145731
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
145607
145732
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
145608
- force_emergency_bypass_used
145733
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
145609
145734
  FROM session_meta WHERE session_id = ?`).get(sessionId);
145610
145735
  return toProtectedTailMeta(row);
145611
145736
  }
@@ -145649,6 +145774,17 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
145649
145774
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
145650
145775
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
145651
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
+ }
145652
145788
  function reserveProtectedTailDrainTokens(args) {
145653
145789
  const now = args.now ?? Date.now();
145654
145790
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -145667,18 +145803,30 @@ function reserveProtectedTailDrainTokens(args) {
145667
145803
  let meta = loadProtectedTailMeta(args.db, args.sessionId);
145668
145804
  if (now - meta.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
145669
145805
  args.db.prepare(`UPDATE session_meta
145670
- SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0,
145671
- force_emergency_bypass_window_start = ?, force_emergency_bypass_used = 0
145672
- 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);
145673
145808
  meta = loadProtectedTailMeta(args.db, args.sessionId);
145674
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;
145675
145824
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
145676
145825
  const remaining = Math.max(0, budget - meta.protectedTailDrainTokens);
145677
145826
  let reserved = Math.min(requested, args.perRunCap, remaining);
145678
145827
  let bypass = false;
145679
- const bypassWindowExpired = now - meta.forceEmergencyBypassWindowStart > DRAIN_WINDOW_MS;
145680
- const bypassUsed = bypassWindowExpired ? 0 : meta.forceEmergencyBypassUsed;
145681
- 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) {
145682
145830
  reserved = Math.min(requested, args.perRunCap);
145683
145831
  bypass = true;
145684
145832
  }
@@ -145686,10 +145834,8 @@ function reserveProtectedTailDrainTokens(args) {
145686
145834
  return;
145687
145835
  args.db.prepare(`UPDATE session_meta
145688
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,
145689
- protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?,
145690
- force_emergency_bypass_window_start = CASE WHEN ? THEN ? ELSE force_emergency_bypass_window_start END,
145691
- force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
145692
- 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);
145693
145839
  result = {
145694
145840
  ok: true,
145695
145841
  reservedTokens: reserved,
@@ -145699,6 +145845,25 @@ function reserveProtectedTailDrainTokens(args) {
145699
145845
  })();
145700
145846
  return result;
145701
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
+ }
145702
145867
  function rollbackProtectedTailDrainReservation(db, reservation) {
145703
145868
  if (!reservation || reservation.tokens <= 0)
145704
145869
  return;
@@ -146217,6 +146382,7 @@ var SESSION_META_FALLBACK_SELECTS = {
146217
146382
  last_transform_error: "'' AS last_transform_error",
146218
146383
  system_prompt_hash: "'' AS system_prompt_hash",
146219
146384
  last_todo_state: "'' AS last_todo_state",
146385
+ tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
146220
146386
  cached_m0_bytes: "NULL AS cached_m0_bytes",
146221
146387
  cached_m1_bytes: "NULL AS cached_m1_bytes",
146222
146388
  cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
@@ -146294,6 +146460,14 @@ function updateSessionMeta(db, sessionId, updates) {
146294
146460
  db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
146295
146461
  })();
146296
146462
  }
146463
+ function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
146464
+ if (maxTagNumber <= 0)
146465
+ return;
146466
+ db.transaction(() => {
146467
+ ensureSessionMetaRow(db, sessionId);
146468
+ db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
146469
+ })();
146470
+ }
146297
146471
  // ../plugin/src/features/magic-context/storage-notes.ts
146298
146472
  var NOTE_TYPES = new Set(["session", "smart"]);
146299
146473
  var NOTE_STATUSES = new Set(["active", "pending", "ready", "dismissed"]);
@@ -146691,12 +146865,37 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
146691
146865
  nullCount: row?.null_count ?? 0
146692
146866
  };
146693
146867
  }
146694
- function getTriggerTagTokenUpperBound(db, sessionId) {
146695
- const row = db.prepare(`SELECT
146868
+ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
146869
+ if (limit <= 0)
146870
+ return [];
146871
+ const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
146872
+ const whereProtected = protectedTags > 0 ? `AND tag_number < (
146873
+ SELECT tag_number FROM tags
146874
+ WHERE session_id = ? AND status = 'active'
146875
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
146876
+ )` : "";
146877
+ const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
146878
+ const rows = db.prepare(`SELECT tag_number, tool_name
146879
+ FROM tags
146880
+ WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
146881
+ ORDER BY tag_number ASC, id ASC
146882
+ LIMIT ?`).all(...params);
146883
+ return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
146884
+ tagNumber: row.tag_number,
146885
+ toolName: typeof row.tool_name === "string" ? row.tool_name : null
146886
+ }));
146887
+ }
146888
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
146889
+ const sql = floor > 0 ? `SELECT
146696
146890
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
146697
146891
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
146698
146892
  FROM tags
146699
- 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);
146700
146899
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
146701
146900
  }
146702
146901
  function updateTagInputByteSize(db, sessionId, tagNumber, newInputByteSize) {
@@ -146723,10 +146922,12 @@ function getUpdateTagInputTokenCountStatement(db) {
146723
146922
  function updateTagTokenCount(db, sessionId, tagNumber, newTokenCount) {
146724
146923
  getUpdateTagTokenCountStatement(db).run(newTokenCount, sessionId, tagNumber);
146725
146924
  }
146726
- function getAllStatusTagTokenTotalsFlat(db, sessionId) {
146727
- const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
146728
- FROM tags
146729
- 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);
146730
146931
  const totals = new Map;
146731
146932
  const nullMessageIds = new Set;
146732
146933
  for (const row of rows) {
@@ -146845,6 +147046,52 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
146845
147046
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
146846
147047
  return isTagNumberRow(row) ? row.tag_number : null;
146847
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
+ }
146848
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";
146849
147096
  function getTagsBySession(db, sessionId) {
146850
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);
@@ -146948,6 +147195,11 @@ init_logger();
146948
147195
  import { createHash as createHash5 } from "node:crypto";
146949
147196
  import { realpathSync as realpathSync2 } from "node:fs";
146950
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
146951
147203
  var BATCH_SIZE = 25;
146952
147204
  var YIELD_EVERY_N_ROWS = 5;
146953
147205
  var BACKFILL_META_KEY = "v22_legacy_memory_backfill";
@@ -147163,65 +147415,6 @@ async function runDeferredV22Backfill(db, options = {}) {
147163
147415
  };
147164
147416
  }
147165
147417
 
147166
- // ../plugin/src/agents/historian.ts
147167
- var HISTORIAN_AGENT = "historian";
147168
- var HISTORIAN_EDITOR_AGENT = "historian-editor";
147169
-
147170
- // ../plugin/src/agents/dreamer.ts
147171
- var DREAMER_AGENT = "dreamer";
147172
-
147173
- // ../plugin/src/agents/sidekick.ts
147174
- var SIDEKICK_AGENT = "sidekick";
147175
-
147176
- // ../plugin/src/shared/model-requirements.ts
147177
- var HISTORIAN_FALLBACK_CHAIN = [
147178
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
147179
- { providers: ["opencode-go"], model: "minimax-m2.7" },
147180
- {
147181
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
147182
- model: "glm-5"
147183
- },
147184
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4" },
147185
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" }
147186
- ];
147187
- var DREAMER_FALLBACK_CHAIN = [
147188
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
147189
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
147190
- {
147191
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
147192
- model: "glm-5"
147193
- },
147194
- { providers: ["opencode-go"], model: "minimax-m2.7" },
147195
- { providers: ["github-copilot", "openai", "opencode"], model: "gpt-5.4-mini" }
147196
- ];
147197
- var SIDEKICK_FALLBACK_CHAIN = [
147198
- { providers: ["cerebras"], model: "qwen-3-235b-a22b-instruct-2507" },
147199
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
147200
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4-mini" },
147201
- { providers: ["opencode"], model: "gpt-5-nano" }
147202
- ];
147203
- var AGENT_MODEL_REQUIREMENTS = {
147204
- [HISTORIAN_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
147205
- [HISTORIAN_EDITOR_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
147206
- [DREAMER_AGENT]: { fallbackChain: DREAMER_FALLBACK_CHAIN },
147207
- [SIDEKICK_AGENT]: { fallbackChain: SIDEKICK_FALLBACK_CHAIN }
147208
- };
147209
- function expandFallbackChain(chain) {
147210
- const models = [];
147211
- for (const entry of chain) {
147212
- for (const provider of entry.providers) {
147213
- models.push(`${provider}/${entry.model}`);
147214
- }
147215
- }
147216
- return models;
147217
- }
147218
- function getAgentFallbackModels(agent) {
147219
- const requirement = AGENT_MODEL_REQUIREMENTS[agent];
147220
- if (!requirement)
147221
- return;
147222
- return expandFallbackChain(requirement.fallbackChain);
147223
- }
147224
-
147225
147418
  // ../plugin/src/shared/models-dev-cache.ts
147226
147419
  init_data_path();
147227
147420
  init_logger();
@@ -147312,25 +147505,9 @@ function resolveHistorianContextLimit(historianModelOverride) {
147312
147505
  return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147313
147506
  }
147314
147507
  if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
147315
- 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.`);
147316
147509
  }
147317
- const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
147318
- if (!chain || chain.length === 0)
147319
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147320
- const expanded = expandFallbackChain(chain);
147321
- let minLimit;
147322
- for (const key of expanded) {
147323
- const [providerID, ...rest] = key.split("/");
147324
- const modelID = rest.join("/");
147325
- if (!providerID || !modelID)
147326
- continue;
147327
- const limit = getSdkContextLimit(providerID, modelID);
147328
- if (typeof limit !== "number" || limit <= 0)
147329
- continue;
147330
- if (minLimit === undefined || limit < minLimit)
147331
- minLimit = limit;
147332
- }
147333
- return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147510
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
147334
147511
  }
147335
147512
 
147336
147513
  // ../plugin/src/hooks/magic-context/event-resolvers.ts
@@ -147743,13 +147920,13 @@ async function maybeSendUpgradeReminder(deps, sessionId) {
147743
147920
  init_data_path();
147744
147921
  import * as fs2 from "node:fs";
147745
147922
  import * as path6 from "node:path";
147746
- var ANNOUNCEMENT_VERSION = "0.24.0";
147923
+ var ANNOUNCEMENT_VERSION = "0.26.0";
147747
147924
  var ANNOUNCEMENT_FEATURES = [
147748
- "Searchable session history: ctx_search can now find older discussion by meaning, not just keywords. New history is embedded automatically to backfill an EXISTING session's older history, run /ctx-embed-history once (it works in the background).",
147749
- "Cross-project workspaces: group related repos and share project memories across them, with per-category control over what's shared. Set them up in the dashboard's Workspaces panel.",
147750
- "Pi: fixed sessions overflowing the model context while still showing moderate usage Pi now sheds context before a tool-heavy turn overflows.",
147751
- "Fewer prompt-cache busts: doc edits, processed screenshots, and a rebuild-then-bust-again case no longer re-bill large prompt prefixes.",
147752
- "Setup wizard now lists your actual models with type-ahead instead of fixed recommendations, and explains the historian/dreamer roles (issue #144). Plus a GitHub Copilot tool-pairing fix (#135)."
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."
147753
147930
  ];
147754
147931
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
147755
147932
  var STATE_FILENAME = "last_announced_version";
@@ -147808,15 +147985,9 @@ function shouldKeepSubagents() {
147808
147985
  init_logger();
147809
147986
 
147810
147987
  // ../plugin/src/shared/resolve-fallbacks.ts
147811
- function resolveFallbackChain(agentName, userFallbacks) {
147988
+ function resolveFallbackChain(userFallbacks) {
147812
147989
  const userList = normalizeUserFallbacks(userFallbacks);
147813
- if (userList.length > 0) {
147814
- return dedupe(userList.filter(isValidModelSpec));
147815
- }
147816
- const builtin = getAgentFallbackModels(agentName);
147817
- if (!builtin || builtin.length === 0)
147818
- return [];
147819
- return dedupe(builtin.filter(isValidModelSpec));
147990
+ return dedupe(userList.filter(isValidModelSpec));
147820
147991
  }
147821
147992
  function normalizeUserFallbacks(userFallbacks) {
147822
147993
  if (!userFallbacks)
@@ -148693,7 +148864,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
148693
148864
  return db.transaction(() => {
148694
148865
  if (!hasActiveDreamLease(db)) {
148695
148866
  const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
148696
- db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
148867
+ db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
148697
148868
  }
148698
148869
  const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
148699
148870
  if (existing) {
@@ -148755,6 +148926,9 @@ init_logger();
148755
148926
  import { existsSync as existsSync8 } from "node:fs";
148756
148927
  import { join as join11 } from "node:path";
148757
148928
 
148929
+ // ../plugin/src/agents/dreamer.ts
148930
+ var DREAMER_AGENT = "dreamer";
148931
+
148758
148932
  // ../plugin/src/shared/index.ts
148759
148933
  init_logger();
148760
148934
 
@@ -148828,9 +149002,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
148828
149002
  });
148829
149003
  } catch (error) {
148830
149004
  if (signal?.aborted) {
149005
+ await abortChildRun(client, args.path.id);
148831
149006
  throw new Error("prompt aborted by external signal");
148832
149007
  }
148833
149008
  if (controller.signal.aborted) {
149009
+ await abortChildRun(client, args.path.id);
148834
149010
  throw new Error(`prompt timed out after ${timeoutMs}ms`);
148835
149011
  }
148836
149012
  throw error;
@@ -148839,6 +149015,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
148839
149015
  signal?.removeEventListener("abort", onExternalAbort);
148840
149016
  }
148841
149017
  }
149018
+ async function abortChildRun(client, sessionId) {
149019
+ try {
149020
+ await client.session.abort({ path: { id: sessionId } });
149021
+ } catch (error) {
149022
+ log(`[model-retry] child session abort failed for ${sessionId}: ${String(error)}`);
149023
+ }
149024
+ }
148842
149025
  function isNonRetryable(error, externalSignal) {
148843
149026
  if (externalSignal?.aborted)
148844
149027
  return true;
@@ -149695,6 +149878,20 @@ function getDistinctStoredModelIds(db, projectPath) {
149695
149878
  const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
149696
149879
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
149697
149880
  }
149881
+ function getMemoryEmbedCoverage(db, projectPath, modelId) {
149882
+ const row = db.prepare(`SELECT
149883
+ COUNT(*) AS total,
149884
+ SUM(CASE WHEN EXISTS (
149885
+ SELECT 1 FROM memory_embeddings e
149886
+ WHERE e.memory_id = m.id AND e.model_id = ?
149887
+ ) THEN 1 ELSE 0 END) AS embedded
149888
+ FROM memories m
149889
+ WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
149890
+ return {
149891
+ total: typeof row?.total === "number" ? row.total : 0,
149892
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
149893
+ };
149894
+ }
149698
149895
 
149699
149896
  // ../plugin/src/features/magic-context/memory/embedding-cache.ts
149700
149897
  var DEFAULT_EMBEDDING_CACHE_TTL_MS = 60000;
@@ -150532,6 +150729,7 @@ ${modeIntro}
150532
150729
  4. **Write or update** using the Write tool. Always write to project root, NOT to .planning/.
150533
150730
 
150534
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.
150535
150733
  - **Be prescriptive**: "Use X pattern" not "X pattern is used"
150536
150734
  - **Always include file paths** in backticks
150537
150735
  - **Write current state only**: no temporal language, no history
@@ -166170,14 +166368,16 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
166170
166368
  }).optional();
166171
166369
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
166172
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)"),
166173
- 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: [])`)
166174
166373
  }).optional();
166175
166374
  var BaseEmbeddingConfigSchema = exports_external.object({
166176
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."),
166177
166376
  model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
166178
166377
  endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
166179
166378
  api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
166180
- 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."),
166181
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."),
166182
166382
  max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
166183
166383
  }).superRefine((data, ctx) => {
@@ -166207,6 +166407,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
166207
166407
  if (data.provider === "openai-compatible") {
166208
166408
  const apiKey = data.api_key?.trim();
166209
166409
  const inputType = data.input_type?.trim();
166410
+ const queryInputType = data.query_input_type?.trim();
166210
166411
  const truncate = data.truncate?.trim();
166211
166412
  return {
166212
166413
  provider: "openai-compatible",
@@ -166214,6 +166415,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
166214
166415
  endpoint: data.endpoint?.trim() ?? "",
166215
166416
  ...apiKey ? { api_key: apiKey } : {},
166216
166417
  ...inputType ? { input_type: inputType } : {},
166418
+ ...queryInputType ? { query_input_type: queryInputType } : {},
166217
166419
  ...truncate ? { truncate } : {},
166218
166420
  ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
166219
166421
  };
@@ -166493,6 +166695,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
166493
166695
  return lines.join(`
166494
166696
  `);
166495
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
+ }
166496
166708
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
166497
166709
  const lines = [];
166498
166710
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -166736,6 +166948,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
166736
166948
  )`).get(projectPath, sessionId, projectPath, modelId);
166737
166949
  return typeof row?.n === "number" ? row.n : 0;
166738
166950
  }
166951
+ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
166952
+ const row = db.prepare(`SELECT
166953
+ COUNT(*) AS total,
166954
+ SUM(CASE WHEN EXISTS (
166955
+ SELECT 1 FROM compartment_chunk_embeddings e
166956
+ WHERE e.compartment_id = c.id
166957
+ AND e.project_path = ?
166958
+ AND e.model_id = ?
166959
+ ) THEN 1 ELSE 0 END) AS embedded
166960
+ FROM compartments c
166961
+ JOIN session_projects sp
166962
+ ON sp.session_id = c.session_id
166963
+ AND sp.harness = c.harness
166964
+ AND sp.project_path = ?
166965
+ WHERE c.session_id = ?
166966
+ AND c.start_message IS NOT NULL
166967
+ AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
166968
+ return {
166969
+ total: typeof row?.total === "number" ? row.total : 0,
166970
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
166971
+ };
166972
+ }
166739
166973
 
166740
166974
  // ../plugin/src/features/magic-context/memory/cosine-similarity.ts
166741
166975
  function cosineSimilarity(a, b) {
@@ -166878,6 +167112,19 @@ async function withQuietConsole(fn) {
166878
167112
  console.error = origError;
166879
167113
  }
166880
167114
  }
167115
+ var nativeRuntimeMissing = false;
167116
+ function isNativeRuntimeMissingError(error51) {
167117
+ const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
167118
+ const lower = message.toLowerCase();
167119
+ const code = error51?.code;
167120
+ const name2 = error51?.name;
167121
+ if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
167122
+ return true;
167123
+ }
167124
+ if (!lower.includes("onnxruntime-node"))
167125
+ return false;
167126
+ return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
167127
+ }
166881
167128
  function isTransientLoadError(error51) {
166882
167129
  const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
166883
167130
  if (!message)
@@ -166942,6 +167189,9 @@ class LocalEmbeddingProvider {
166942
167189
  if (this.pipeline) {
166943
167190
  return true;
166944
167191
  }
167192
+ if (nativeRuntimeMissing) {
167193
+ return false;
167194
+ }
166945
167195
  if (this.initPromise) {
166946
167196
  await this.initPromise;
166947
167197
  return this.pipeline !== null;
@@ -167008,7 +167258,12 @@ class LocalEmbeddingProvider {
167008
167258
  await releaseLock();
167009
167259
  }
167010
167260
  } catch (error51) {
167011
- log("[magic-context] embedding model failed to load:", error51);
167261
+ if (isNativeRuntimeMissingError(error51)) {
167262
+ nativeRuntimeMissing = true;
167263
+ log("[magic-context] local embedding runtime is not installed (onnxruntime-node missing from this install). Local embeddings are disabled. Fix: reinstall the plugin (run `npx @wolfx/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
167264
+ } else {
167265
+ log("[magic-context] embedding model failed to load:", error51);
167266
+ }
167012
167267
  this.pipeline = null;
167013
167268
  } finally {
167014
167269
  this.initPromise = null;
@@ -167034,7 +167289,7 @@ class LocalEmbeddingProvider {
167034
167289
  waiter();
167035
167290
  }
167036
167291
  }
167037
- async embed(text, signal) {
167292
+ async embed(text, signal, _purpose) {
167038
167293
  if (signal?.aborted)
167039
167294
  return null;
167040
167295
  if (this.disposing)
@@ -167060,7 +167315,7 @@ class LocalEmbeddingProvider {
167060
167315
  this.finishInFlight();
167061
167316
  }
167062
167317
  }
167063
- async embedBatch(texts, signal) {
167318
+ async embedBatch(texts, signal, _purpose) {
167064
167319
  if (texts.length === 0) {
167065
167320
  return [];
167066
167321
  }
@@ -167199,6 +167454,7 @@ class OpenAICompatibleEmbeddingProvider {
167199
167454
  model;
167200
167455
  apiKey;
167201
167456
  inputType;
167457
+ queryInputType;
167202
167458
  truncate;
167203
167459
  initialized = false;
167204
167460
  failureTimes = [];
@@ -167211,6 +167467,7 @@ class OpenAICompatibleEmbeddingProvider {
167211
167467
  this.model = options.model?.trim() ?? "";
167212
167468
  this.apiKey = options.apiKey?.trim() ?? "";
167213
167469
  this.inputType = options.inputType?.trim() ?? "";
167470
+ this.queryInputType = options.queryInputType?.trim() ?? "";
167214
167471
  this.truncate = options.truncate?.trim() ?? "";
167215
167472
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
167216
167473
  this.modelId = getEmbeddingProviderIdentity({
@@ -167238,11 +167495,17 @@ class OpenAICompatibleEmbeddingProvider {
167238
167495
  this.initialized = true;
167239
167496
  return true;
167240
167497
  }
167241
- async embed(text, signal) {
167242
- 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);
167243
167506
  return embedding ?? null;
167244
167507
  }
167245
- async embedBatch(texts, signal) {
167508
+ async embedBatch(texts, signal, purpose) {
167246
167509
  if (texts.length === 0) {
167247
167510
  return [];
167248
167511
  }
@@ -167268,6 +167531,7 @@ class OpenAICompatibleEmbeddingProvider {
167268
167531
  if (signal) {
167269
167532
  signal.addEventListener("abort", onOuterAbort, { once: true });
167270
167533
  }
167534
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
167271
167535
  const response = await fetch(`${this.endpoint}/embeddings`, {
167272
167536
  method: "POST",
167273
167537
  headers: {
@@ -167277,7 +167541,7 @@ class OpenAICompatibleEmbeddingProvider {
167277
167541
  body: JSON.stringify({
167278
167542
  model: this.model,
167279
167543
  input: texts,
167280
- ...this.inputType ? { input_type: this.inputType } : {},
167544
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
167281
167545
  ...this.truncate ? { truncate: this.truncate } : {}
167282
167546
  }),
167283
167547
  redirect: "error",
@@ -167522,6 +167786,118 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
167522
167786
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
167523
167787
  }
167524
167788
 
167789
+ // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
167790
+ init_logger();
167791
+ var insertStatements = new WeakMap;
167792
+ var existingShasStatements = new WeakMap;
167793
+ var projectCountStatements = new WeakMap;
167794
+ var evictStatements = new WeakMap;
167795
+ var evictOverflowStatements = new WeakMap;
167796
+ var latestCommitTimeStatements = new WeakMap;
167797
+ function getInsertStatement(db) {
167798
+ let stmt = insertStatements.get(db);
167799
+ if (!stmt) {
167800
+ stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
167801
+ VALUES (?, ?, ?, ?, ?, ?, ?)
167802
+ ON CONFLICT(sha) DO UPDATE SET
167803
+ project_path = excluded.project_path,
167804
+ short_sha = excluded.short_sha,
167805
+ message = excluded.message,
167806
+ author = excluded.author,
167807
+ committed_at = excluded.committed_at,
167808
+ indexed_at = excluded.indexed_at
167809
+ WHERE git_commits.message != excluded.message`);
167810
+ insertStatements.set(db, stmt);
167811
+ }
167812
+ return stmt;
167813
+ }
167814
+ function getExistingShasStatement(db) {
167815
+ let stmt = existingShasStatements.get(db);
167816
+ if (!stmt) {
167817
+ stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
167818
+ existingShasStatements.set(db, stmt);
167819
+ }
167820
+ return stmt;
167821
+ }
167822
+ function getProjectCountStatement(db) {
167823
+ let stmt = projectCountStatements.get(db);
167824
+ if (!stmt) {
167825
+ stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
167826
+ projectCountStatements.set(db, stmt);
167827
+ }
167828
+ return stmt;
167829
+ }
167830
+ function getLatestCommitTimeStatement(db) {
167831
+ let stmt = latestCommitTimeStatements.get(db);
167832
+ if (!stmt) {
167833
+ stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
167834
+ latestCommitTimeStatements.set(db, stmt);
167835
+ }
167836
+ return stmt;
167837
+ }
167838
+ function getEvictOverflowStatement(db) {
167839
+ let stmt = evictOverflowStatements.get(db);
167840
+ if (!stmt) {
167841
+ stmt = db.prepare(`DELETE FROM git_commits
167842
+ WHERE rowid IN (
167843
+ SELECT rowid FROM git_commits
167844
+ WHERE project_path = ?
167845
+ ORDER BY committed_at DESC, sha DESC
167846
+ LIMIT -1 OFFSET ?
167847
+ )`);
167848
+ evictOverflowStatements.set(db, stmt);
167849
+ }
167850
+ return stmt;
167851
+ }
167852
+ function upsertCommits(db, projectPath, commits) {
167853
+ if (commits.length === 0)
167854
+ return { inserted: 0, updated: 0 };
167855
+ const existing = new Set;
167856
+ for (const row of getExistingShasStatement(db).all(projectPath)) {
167857
+ existing.add(row.sha);
167858
+ }
167859
+ let inserted = 0;
167860
+ let updated = 0;
167861
+ const now = Date.now();
167862
+ const insertStmt = getInsertStatement(db);
167863
+ db.transaction(() => {
167864
+ for (const commit of commits) {
167865
+ const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
167866
+ if (result.changes > 0) {
167867
+ if (existing.has(commit.sha)) {
167868
+ updated++;
167869
+ } else {
167870
+ inserted++;
167871
+ existing.add(commit.sha);
167872
+ }
167873
+ }
167874
+ }
167875
+ })();
167876
+ return { inserted, updated };
167877
+ }
167878
+ function getCommitCount(db, projectPath) {
167879
+ const row = getProjectCountStatement(db).get(projectPath);
167880
+ return row?.count ?? 0;
167881
+ }
167882
+ function getLatestIndexedCommitTimeMs(db, projectPath) {
167883
+ const row = getLatestCommitTimeStatement(db).get(projectPath);
167884
+ return row?.latest ?? null;
167885
+ }
167886
+ function enforceProjectCap(db, projectPath, maxCommits) {
167887
+ if (maxCommits <= 0)
167888
+ return 0;
167889
+ const count = getCommitCount(db, projectPath);
167890
+ if (count <= maxCommits)
167891
+ return 0;
167892
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
167893
+ const after = getCommitCount(db, projectPath);
167894
+ const evicted = Math.max(0, count - after);
167895
+ if (evicted > 0) {
167896
+ log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
167897
+ }
167898
+ return evicted;
167899
+ }
167900
+
167525
167901
  // ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
167526
167902
  var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
167527
167903
  var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
@@ -167716,8 +168092,12 @@ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
167716
168092
  var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
167717
168093
  var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
167718
168094
  var CHUNK_DRAIN_BATCH_SIZE = 8;
167719
- var MAX_WINDOWS_PER_EMBED_CALL = 16;
168095
+ var MAX_WINDOWS_PER_EMBED_CALL = 2;
167720
168096
  var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
168097
+ var EMBED_SLICE_RETRY_ATTEMPTS = 3;
168098
+ var EMBED_SLICE_RETRY_BASE_MS = 250;
168099
+ var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
168100
+ var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
167721
168101
  var projectRegistrations = new Map;
167722
168102
  var loadUnembeddedMemoriesStatements = new WeakMap;
167723
168103
  var globalRegistrationGeneration = 0;
@@ -167735,6 +168115,7 @@ function resolveEmbeddingConfig(config2) {
167735
168115
  if (config2.provider === "openai-compatible") {
167736
168116
  const apiKey = config2.api_key?.trim();
167737
168117
  const inputType = config2.input_type?.trim();
168118
+ const queryInputType = config2.query_input_type?.trim();
167738
168119
  const truncate = config2.truncate?.trim();
167739
168120
  return {
167740
168121
  provider: "openai-compatible",
@@ -167742,6 +168123,7 @@ function resolveEmbeddingConfig(config2) {
167742
168123
  endpoint: config2.endpoint.trim(),
167743
168124
  ...apiKey ? { api_key: apiKey } : {},
167744
168125
  ...inputType ? { input_type: inputType } : {},
168126
+ ...queryInputType ? { query_input_type: queryInputType } : {},
167745
168127
  ...truncate ? { truncate } : {},
167746
168128
  ...config2.max_input_tokens ? {
167747
168129
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -167763,6 +168145,7 @@ function createProvider(config2) {
167763
168145
  model: config2.model,
167764
168146
  apiKey: config2.api_key,
167765
168147
  inputType: config2.input_type,
168148
+ queryInputType: config2.query_input_type,
167766
168149
  truncate: config2.truncate,
167767
168150
  maxInputTokens: config2.max_input_tokens
167768
168151
  });
@@ -167817,7 +168200,9 @@ function snapshotFor(registration) {
167817
168200
  enabled,
167818
168201
  gitCommitEnabled,
167819
168202
  modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
167820
- chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
168203
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
168204
+ model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
168205
+ provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
167821
168206
  };
167822
168207
  }
167823
168208
  function disposeProvider(provider) {
@@ -167943,7 +168328,7 @@ function getOrCreateProjectProvider(registration) {
167943
168328
  registration.provider = provider;
167944
168329
  return provider;
167945
168330
  }
167946
- async function embedTextForProject(projectIdentity, text, signal) {
168331
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
167947
168332
  const registration = projectRegistrations.get(projectIdentity);
167948
168333
  if (!registration)
167949
168334
  return null;
@@ -167952,7 +168337,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
167952
168337
  const provider = getOrCreateProjectProvider(registration);
167953
168338
  if (!provider)
167954
168339
  return null;
167955
- const vector = await provider.embed(text, signal);
168340
+ const vector = await provider.embed(text, signal, purpose);
167956
168341
  if (!vector)
167957
168342
  return null;
167958
168343
  const current = projectRegistrations.get(projectIdentity);
@@ -167961,7 +168346,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
167961
168346
  }
167962
168347
  return { vector, modelId, generation };
167963
168348
  }
167964
- async function embedBatchForProject(projectIdentity, texts, signal) {
168349
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
167965
168350
  if (texts.length === 0) {
167966
168351
  const registration2 = projectRegistrations.get(projectIdentity);
167967
168352
  if (!registration2 || registration2.observationMode)
@@ -167977,7 +168362,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
167977
168362
  const provider = getOrCreateProjectProvider(registration);
167978
168363
  if (!provider)
167979
168364
  return null;
167980
- const vectors = await provider.embedBatch(texts, signal);
168365
+ const vectors = await provider.embedBatch(texts, signal, purpose);
167981
168366
  const current = projectRegistrations.get(projectIdentity);
167982
168367
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
167983
168368
  return null;
@@ -168028,12 +168413,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
168028
168413
  }
168029
168414
  async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
168030
168415
  const noWork = [];
168416
+ const failed = [];
168031
168417
  if (candidates.length === 0)
168032
- return { embedded: 0, noWork };
168418
+ return { embedded: 0, noWork, failed };
168033
168419
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
168034
168420
  const prepared = [];
168035
168421
  for (const candidate of candidates) {
168036
- 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);
168037
168423
  if (canonicalText.length === 0) {
168038
168424
  noWork.push(candidate.id);
168039
168425
  continue;
@@ -168046,7 +168432,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
168046
168432
  prepared.push({ candidate, windows });
168047
168433
  }
168048
168434
  if (prepared.length === 0)
168049
- return { embedded: 0, noWork };
168435
+ return { embedded: 0, noWork, failed };
168050
168436
  let embedded = 0;
168051
168437
  let i = 0;
168052
168438
  while (i < prepared.length) {
@@ -168063,35 +168449,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
168063
168449
  const texts = [];
168064
168450
  for (const item of slice)
168065
168451
  texts.push(...item.windows.map((w) => w.text));
168066
- try {
168067
- const result = await embedBatchForProject(projectIdentity, texts, signal);
168068
- if (!result)
168069
- continue;
168452
+ const persistedIds = new Set;
168453
+ for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
168070
168454
  if (signal?.aborted)
168071
168455
  break;
168072
- let offset = 0;
168073
- for (const item of slice) {
168074
- const vectors = result.vectors.slice(offset, offset + item.windows.length);
168075
- offset += item.windows.length;
168076
- if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
168077
- continue;
168456
+ let result = null;
168457
+ const attemptStart = Date.now();
168458
+ try {
168459
+ result = await embedBatchForProject(projectIdentity, texts, signal);
168460
+ } catch (error51) {
168461
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
168462
+ }
168463
+ if (signal?.aborted)
168464
+ break;
168465
+ if (result) {
168466
+ let offset = 0;
168467
+ for (const item of slice) {
168468
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
168469
+ offset += item.windows.length;
168470
+ if (persistedIds.has(item.candidate.id))
168471
+ continue;
168472
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
168473
+ continue;
168474
+ }
168475
+ const rows = item.windows.map((window, index) => ({
168476
+ compartmentId: item.candidate.id,
168477
+ sessionId: item.candidate.sessionId,
168478
+ projectPath: projectIdentity,
168479
+ window,
168480
+ modelId,
168481
+ vector: vectors[index]
168482
+ }));
168483
+ replaceCompartmentChunkEmbeddings(db, rows);
168484
+ persistedIds.add(item.candidate.id);
168078
168485
  }
168079
- const rows = item.windows.map((window, index) => ({
168080
- compartmentId: item.candidate.id,
168081
- sessionId: item.candidate.sessionId,
168082
- projectPath: projectIdentity,
168083
- window,
168084
- modelId,
168085
- vector: vectors[index]
168086
- }));
168087
- replaceCompartmentChunkEmbeddings(db, rows);
168088
- embedded += 1;
168089
168486
  }
168090
- } catch (error51) {
168091
- log("[magic-context] failed to proactively embed compartment chunks:", error51);
168487
+ if (persistedIds.size === slice.length)
168488
+ break;
168489
+ if (persistedIds.size > 0)
168490
+ break;
168491
+ if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
168492
+ break;
168493
+ if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
168494
+ await new Promise((resolve4) => setTimeout(resolve4, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
168495
+ }
168496
+ }
168497
+ embedded += persistedIds.size;
168498
+ if (!signal?.aborted) {
168499
+ for (const item of slice) {
168500
+ if (!persistedIds.has(item.candidate.id))
168501
+ failed.push(item.candidate.id);
168502
+ }
168092
168503
  }
168093
168504
  }
168094
- return { embedded, noWork };
168505
+ return { embedded, noWork, failed };
168095
168506
  }
168096
168507
  async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
168097
168508
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
@@ -168114,9 +168525,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
168114
168525
  renewal.unref?.();
168115
168526
  const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
168116
168527
  const skipIds = [];
168528
+ const failedIds = [];
168117
168529
  let embedded = 0;
168118
168530
  let aborted2 = false;
168119
- let providerStalled = false;
168531
+ let providerDown = false;
168532
+ let consecutiveFailedBatches = 0;
168120
168533
  try {
168121
168534
  options?.onProgress?.({ embedded, total });
168122
168535
  for (;; ) {
@@ -168124,15 +168537,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
168124
168537
  aborted2 = true;
168125
168538
  break;
168126
168539
  }
168127
- const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
168540
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
168128
168541
  if (candidates.length === 0)
168129
168542
  break;
168130
- const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
168543
+ const {
168544
+ embedded: n,
168545
+ noWork,
168546
+ failed
168547
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
168131
168548
  for (const id of noWork)
168132
168549
  skipIds.push(id);
168550
+ for (const id of failed)
168551
+ failedIds.push(id);
168133
168552
  if (n === 0 && noWork.length === 0) {
168134
- providerStalled = true;
168135
- break;
168553
+ consecutiveFailedBatches += 1;
168554
+ if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
168555
+ providerDown = true;
168556
+ break;
168557
+ }
168558
+ } else {
168559
+ consecutiveFailedBatches = 0;
168136
168560
  }
168137
168561
  embedded += n;
168138
168562
  options?.onProgress?.({ embedded: Math.min(embedded, total), total });
@@ -168140,16 +168564,50 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
168140
168564
  }
168141
168565
  } finally {
168142
168566
  clearInterval(renewal);
168143
- releaseGitSweepLease(db, projectIdentity, holderId);
168567
+ try {
168568
+ releaseGitSweepLease(db, projectIdentity, holderId);
168569
+ } catch (error51) {
168570
+ log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
168571
+ }
168144
168572
  }
168145
168573
  if (aborted2)
168146
- return { status: "aborted", embedded, total };
168147
- if (providerStalled) {
168574
+ return { status: "aborted", embedded, total, failed: failedIds.length };
168575
+ if (providerDown || failedIds.length > 0) {
168148
168576
  const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
168149
- if (remaining > 0)
168150
- return { status: "stalled", embedded, total, remaining };
168577
+ if (remaining > 0) {
168578
+ return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
168579
+ }
168151
168580
  }
168152
- return { status: "done", embedded, total };
168581
+ return { status: "done", embedded, total, failed: failedIds.length };
168582
+ }
168583
+ function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
168584
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
168585
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
168586
+ return {
168587
+ enabled: false,
168588
+ model: snapshot?.model ?? "off",
168589
+ provider: snapshot?.provider ?? "off",
168590
+ session: { embedded: 0, total: 0 },
168591
+ memories: { embedded: 0, total: 0 },
168592
+ commits: { embedded: 0, total: 0, gitEnabled: false }
168593
+ };
168594
+ }
168595
+ const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
168596
+ const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
168597
+ const gitEnabled = snapshot.gitCommitEnabled;
168598
+ const commits = gitEnabled ? {
168599
+ embedded: countEmbeddedCommits(db, projectIdentity),
168600
+ total: getCommitCount(db, projectIdentity),
168601
+ gitEnabled: true
168602
+ } : { embedded: 0, total: 0, gitEnabled: false };
168603
+ return {
168604
+ enabled: true,
168605
+ model: snapshot.model,
168606
+ provider: snapshot.provider,
168607
+ session,
168608
+ memories,
168609
+ commits
168610
+ };
168153
168611
  }
168154
168612
 
168155
168613
  // ../plugin/src/features/magic-context/memory/embedding.ts
@@ -168170,6 +168628,7 @@ function createProvider2(config2) {
168170
168628
  model: config2.model,
168171
168629
  apiKey: config2.api_key,
168172
168630
  inputType: config2.input_type,
168631
+ queryInputType: config2.query_input_type,
168173
168632
  truncate: config2.truncate,
168174
168633
  maxInputTokens: config2.max_input_tokens
168175
168634
  });
@@ -168198,118 +168657,6 @@ async function embedText(text, signal) {
168198
168657
  }
168199
168658
  var SWEEP_MAX_WALL_CLOCK_MS2 = 10 * 60 * 1000;
168200
168659
 
168201
- // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
168202
- init_logger();
168203
- var insertStatements = new WeakMap;
168204
- var existingShasStatements = new WeakMap;
168205
- var projectCountStatements = new WeakMap;
168206
- var evictStatements = new WeakMap;
168207
- var evictOverflowStatements = new WeakMap;
168208
- var latestCommitTimeStatements = new WeakMap;
168209
- function getInsertStatement(db) {
168210
- let stmt = insertStatements.get(db);
168211
- if (!stmt) {
168212
- stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
168213
- VALUES (?, ?, ?, ?, ?, ?, ?)
168214
- ON CONFLICT(sha) DO UPDATE SET
168215
- project_path = excluded.project_path,
168216
- short_sha = excluded.short_sha,
168217
- message = excluded.message,
168218
- author = excluded.author,
168219
- committed_at = excluded.committed_at,
168220
- indexed_at = excluded.indexed_at
168221
- WHERE git_commits.message != excluded.message`);
168222
- insertStatements.set(db, stmt);
168223
- }
168224
- return stmt;
168225
- }
168226
- function getExistingShasStatement(db) {
168227
- let stmt = existingShasStatements.get(db);
168228
- if (!stmt) {
168229
- stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
168230
- existingShasStatements.set(db, stmt);
168231
- }
168232
- return stmt;
168233
- }
168234
- function getProjectCountStatement(db) {
168235
- let stmt = projectCountStatements.get(db);
168236
- if (!stmt) {
168237
- stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
168238
- projectCountStatements.set(db, stmt);
168239
- }
168240
- return stmt;
168241
- }
168242
- function getLatestCommitTimeStatement(db) {
168243
- let stmt = latestCommitTimeStatements.get(db);
168244
- if (!stmt) {
168245
- stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
168246
- latestCommitTimeStatements.set(db, stmt);
168247
- }
168248
- return stmt;
168249
- }
168250
- function getEvictOverflowStatement(db) {
168251
- let stmt = evictOverflowStatements.get(db);
168252
- if (!stmt) {
168253
- stmt = db.prepare(`DELETE FROM git_commits
168254
- WHERE rowid IN (
168255
- SELECT rowid FROM git_commits
168256
- WHERE project_path = ?
168257
- ORDER BY committed_at DESC, sha DESC
168258
- LIMIT -1 OFFSET ?
168259
- )`);
168260
- evictOverflowStatements.set(db, stmt);
168261
- }
168262
- return stmt;
168263
- }
168264
- function upsertCommits(db, projectPath, commits) {
168265
- if (commits.length === 0)
168266
- return { inserted: 0, updated: 0 };
168267
- const existing = new Set;
168268
- for (const row of getExistingShasStatement(db).all(projectPath)) {
168269
- existing.add(row.sha);
168270
- }
168271
- let inserted = 0;
168272
- let updated = 0;
168273
- const now = Date.now();
168274
- const insertStmt = getInsertStatement(db);
168275
- db.transaction(() => {
168276
- for (const commit of commits) {
168277
- const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
168278
- if (result.changes > 0) {
168279
- if (existing.has(commit.sha)) {
168280
- updated++;
168281
- } else {
168282
- inserted++;
168283
- existing.add(commit.sha);
168284
- }
168285
- }
168286
- }
168287
- })();
168288
- return { inserted, updated };
168289
- }
168290
- function getCommitCount(db, projectPath) {
168291
- const row = getProjectCountStatement(db).get(projectPath);
168292
- return row?.count ?? 0;
168293
- }
168294
- function getLatestIndexedCommitTimeMs(db, projectPath) {
168295
- const row = getLatestCommitTimeStatement(db).get(projectPath);
168296
- return row?.latest ?? null;
168297
- }
168298
- function enforceProjectCap(db, projectPath, maxCommits) {
168299
- if (maxCommits <= 0)
168300
- return 0;
168301
- const count = getCommitCount(db, projectPath);
168302
- if (count <= maxCommits)
168303
- return 0;
168304
- getEvictOverflowStatement(db).run(projectPath, maxCommits);
168305
- const after = getCommitCount(db, projectPath);
168306
- const evicted = Math.max(0, count - after);
168307
- if (evicted > 0) {
168308
- log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
168309
- }
168310
- return evicted;
168311
- }
168312
-
168313
168660
  // ../plugin/src/features/magic-context/git-commits/indexer.ts
168314
168661
  var MS_PER_DAY = 24 * 60 * 60 * 1000;
168315
168662
  var EMBED_BATCH_SIZE = 16;
@@ -168718,7 +169065,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
168718
169065
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
168719
169066
  projectIdentity: reg.projectIdentity,
168720
169067
  sessionDirectoryOverride: reg.directory,
168721
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
169068
+ fallbackModels: resolveFallbackChain(reg.dreamerConfig.fallback_models)
168722
169069
  });
168723
169070
  } catch (error51) {
168724
169071
  log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
@@ -169598,7 +169945,7 @@ async function awaitInFlightDreamers() {
169598
169945
  function createPiDreamerClient(opts) {
169599
169946
  const runner2 = piSubagentRunnerFactory();
169600
169947
  const model = opts.config.model;
169601
- const fallbackModels = resolveFallbackChain(DREAMER_AGENT, opts.config.fallback_models);
169948
+ const fallbackModels = resolveFallbackChain(opts.config.fallback_models);
169602
169949
  const session = {
169603
169950
  create: async (args) => {
169604
169951
  const sessionId = `magic-context-pi-dream-${++sessionCounter}`;
@@ -169843,87 +170190,229 @@ function registerCtxDreamCommand(pi, deps) {
169843
170190
  });
169844
170191
  }
169845
170192
 
169846
- // src/commands/ctx-embed-history.ts
169847
- function registerCtxEmbedHistoryCommand(pi, deps) {
169848
- pi.registerCommand("ctx-embed-history", {
169849
- description: "Embed all of this session's history compartments for semantic search, in one pass",
169850
- handler: async (_args, ctx) => {
170193
+ // ../plugin/src/hooks/magic-context/embed-session-state.ts
170194
+ var embedPauseBySession = new Set;
170195
+ var embedRunStateBySession = new Map;
170196
+ var autoEmbedAttemptedBySession = new Set;
170197
+
170198
+ // ../plugin/src/hooks/magic-context/format-embed-status.ts
170199
+ function formatEmbedStatusText(coverage, drain) {
170200
+ if (!coverage.enabled) {
170201
+ return "Embedding is off (no provider configured).";
170202
+ }
170203
+ const lines = [];
170204
+ lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
170205
+ lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
170206
+ lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
170207
+ if (coverage.commits.gitEnabled) {
170208
+ lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
170209
+ } else {
170210
+ lines.push("Git commits: 0 / 0 (git indexing off)");
170211
+ }
170212
+ let drainLine = "Drain: idle";
170213
+ switch (drain.status) {
170214
+ case "running": {
170215
+ const e = drain.embedded ?? coverage.session.embedded;
170216
+ const t = drain.total ?? coverage.session.total;
170217
+ const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
170218
+ drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
170219
+ break;
170220
+ }
170221
+ case "paused": {
170222
+ const e = drain.embedded ?? coverage.session.embedded;
170223
+ const t = drain.total ?? coverage.session.total;
170224
+ drainLine = `Drain: paused ${e}/${t}`;
170225
+ break;
170226
+ }
170227
+ case "stopped":
170228
+ drainLine = "Drain: stopped (provider down)";
170229
+ break;
170230
+ default:
170231
+ drainLine = "Drain: idle";
170232
+ }
170233
+ lines.push(drainLine);
170234
+ return lines.join(`
170235
+ `);
170236
+ }
170237
+
170238
+ // src/commands/ctx-embed.ts
170239
+ function clearPiEmbedSessionState(sessionId) {
170240
+ embedPauseBySession.delete(sessionId);
170241
+ const ctrl = embedRunStateBySession.get(sessionId);
170242
+ if (ctrl) {
170243
+ ctrl.abort();
170244
+ embedRunStateBySession.delete(sessionId);
170245
+ }
170246
+ autoEmbedAttemptedBySession.delete(sessionId);
170247
+ }
170248
+ async function runEmbedDrain(db, projectIdentity, sessionId) {
170249
+ const activeCtrl = embedRunStateBySession.get(sessionId);
170250
+ if (activeCtrl && !activeCtrl.signal.aborted) {
170251
+ return {
170252
+ text: `## /ctx-embed
170253
+
170254
+ Embedding is already running for this session.`,
170255
+ level: "info"
170256
+ };
170257
+ }
170258
+ embedPauseBySession.delete(sessionId);
170259
+ const prior = embedRunStateBySession.get(sessionId);
170260
+ if (prior)
170261
+ prior.abort();
170262
+ const controller = new AbortController;
170263
+ embedRunStateBySession.set(sessionId, controller);
170264
+ let outcome;
170265
+ try {
170266
+ outcome = await embedSessionCompartmentChunks(db, projectIdentity, sessionId, {
170267
+ signal: controller.signal
170268
+ });
170269
+ } finally {
170270
+ if (embedRunStateBySession.get(sessionId) === controller) {
170271
+ embedRunStateBySession.delete(sessionId);
170272
+ }
170273
+ }
170274
+ switch (outcome.status) {
170275
+ case "nothing":
170276
+ return {
170277
+ text: `## /ctx-embed
170278
+
170279
+ All of this session's history is already embedded.`,
170280
+ level: "info"
170281
+ };
170282
+ case "disabled":
170283
+ return {
170284
+ text: `## /ctx-embed
170285
+
170286
+ No embedding provider is configured, so there is nothing to embed.`,
170287
+ level: "info"
170288
+ };
170289
+ case "busy":
170290
+ return {
170291
+ text: `## /ctx-embed
170292
+
170293
+ Embedding is already running for this project. Try again shortly.`,
170294
+ level: "info"
170295
+ };
170296
+ case "aborted": {
170297
+ const cov = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
170298
+ return {
170299
+ text: `## /ctx-embed
170300
+
170301
+ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
170302
+ level: "info"
170303
+ };
170304
+ }
170305
+ case "stalled":
170306
+ return {
170307
+ text: `## /ctx-embed
170308
+
170309
+ Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"}; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed start again to retry them.`,
170310
+ level: "info"
170311
+ };
170312
+ default:
170313
+ return {
170314
+ text: `## /ctx-embed
170315
+
170316
+ Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`,
170317
+ level: "success"
170318
+ };
170319
+ }
170320
+ }
170321
+ function registerCtxEmbedCommand(pi, deps) {
170322
+ pi.registerCommand("ctx-embed", {
170323
+ description: "Embedding status, or start/pause history compartment embedding (start | pause)",
170324
+ handler: async (args, ctx) => {
169851
170325
  const sessionId = resolveSessionId(ctx);
169852
170326
  if (!sessionId) {
169853
170327
  sendCtxStatusMessage(pi, {
169854
- title: "/ctx-embed-history",
169855
- text: `## /ctx-embed-history
170328
+ title: "/ctx-embed",
170329
+ text: `## /ctx-embed
169856
170330
 
169857
170331
  No active Pi session is available.`,
169858
170332
  level: "error"
169859
170333
  });
169860
170334
  return;
169861
170335
  }
170336
+ const project = deps.resolveProject?.(ctx) ?? {
170337
+ projectDir: deps.projectDir,
170338
+ projectIdentity: deps.projectIdentity
170339
+ };
170340
+ const sub = args.trim().toLowerCase();
170341
+ if (sub === "pause") {
170342
+ embedPauseBySession.add(sessionId);
170343
+ const ctrl = embedRunStateBySession.get(sessionId);
170344
+ if (ctrl)
170345
+ ctrl.abort();
170346
+ const cov = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
170347
+ sendCtxStatusMessage(pi, {
170348
+ title: "/ctx-embed",
170349
+ text: `## /ctx-embed
170350
+
170351
+ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
170352
+ level: "info"
170353
+ });
170354
+ return;
170355
+ }
169862
170356
  if (deps.memoryEnabled === false) {
169863
170357
  sendCtxStatusMessage(pi, {
169864
- title: "/ctx-embed-history",
169865
- text: `## /ctx-embed-history
170358
+ title: "/ctx-embed",
170359
+ text: `## /ctx-embed
169866
170360
 
169867
170361
  Memory is disabled for this project, so there is no semantic embedding to backfill.`,
169868
170362
  level: "info"
169869
170363
  });
169870
170364
  return;
169871
170365
  }
169872
- const project = deps.resolveProject?.(ctx) ?? {
169873
- projectDir: deps.projectDir,
169874
- projectIdentity: deps.projectIdentity
169875
- };
169876
170366
  await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
169877
- const outcome = await embedSessionCompartmentChunks(deps.db, project.projectIdentity, sessionId);
169878
- const { text, level } = (() => {
169879
- switch (outcome.status) {
169880
- case "nothing":
169881
- return {
169882
- text: `## /ctx-embed-history
169883
-
169884
- All of this session's history is already embedded.`,
169885
- level: "info"
169886
- };
169887
- case "disabled":
169888
- return {
169889
- text: `## /ctx-embed-history
169890
-
169891
- No embedding provider is configured, so there is nothing to embed.`,
169892
- level: "info"
169893
- };
169894
- case "busy":
169895
- return {
169896
- text: `## /ctx-embed-history
169897
-
169898
- Embedding is already running for this project — ${outcome.total} compartment${outcome.total === 1 ? "" : "s"} still pending. Try again shortly.`,
169899
- level: "info"
169900
- };
169901
- case "stalled":
169902
- return {
169903
- text: `## /ctx-embed-history
169904
-
169905
- Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"}; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed-history again to retry them.`,
169906
- level: "info"
169907
- };
169908
- default:
169909
- return {
169910
- text: `## /ctx-embed-history
170367
+ if (sub === "start") {
170368
+ const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId);
170369
+ sendCtxStatusMessage(pi, { title: "/ctx-embed", text, level });
170370
+ return;
170371
+ }
170372
+ if (sub !== "") {
170373
+ sendCtxStatusMessage(pi, {
170374
+ title: "/ctx-embed",
170375
+ text: "## /ctx-embed\n\nUsage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.",
170376
+ level: "info"
170377
+ });
170378
+ return;
170379
+ }
170380
+ const coverage = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
170381
+ const statusText = formatEmbedStatusText(coverage, { status: "idle" });
170382
+ sendCtxStatusMessage(pi, {
170383
+ title: "/ctx-embed",
170384
+ text: `## Embedding Status
169911
170385
 
169912
- Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`,
169913
- level: "success"
169914
- };
169915
- }
169916
- })();
169917
- sendCtxStatusMessage(pi, { title: "/ctx-embed-history", text, level }, {
169918
- sessionId,
169919
- projectIdentity: project.projectIdentity,
169920
- status: outcome.status,
169921
- embedded: outcome.embedded,
169922
- total: outcome.total
170386
+ ${statusText}`,
170387
+ level: "info"
169923
170388
  });
169924
170389
  }
169925
170390
  });
169926
170391
  }
170392
+ function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity, notify) {
170393
+ if (autoEmbedAttemptedBySession.has(sessionId))
170394
+ return;
170395
+ if (embedPauseBySession.has(sessionId))
170396
+ return;
170397
+ if (deps.memoryEnabled === false)
170398
+ return;
170399
+ autoEmbedAttemptedBySession.add(sessionId);
170400
+ (async () => {
170401
+ try {
170402
+ await new Promise((resolve5) => setTimeout(resolve5, 0));
170403
+ await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
170404
+ const coverage = getEmbeddingCoverageStatus(deps.db, projectIdentity, sessionId);
170405
+ if (!coverage.enabled)
170406
+ return;
170407
+ const remaining = coverage.session.total - coverage.session.embedded;
170408
+ if (remaining <= 0)
170409
+ return;
170410
+ notify(`Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`);
170411
+ const { text } = await runEmbedDrain(deps.db, projectIdentity, sessionId);
170412
+ notify(text.replace(/^## \/ctx-embed\n\n/, ""));
170413
+ } catch {}
170414
+ })();
170415
+ }
169927
170416
 
169928
170417
  // ../plugin/src/hooks/magic-context/execute-flush.ts
169929
170418
  init_logger();
@@ -170017,10 +170506,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
170017
170506
  }
170018
170507
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
170019
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";
170020
170510
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
170021
- var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
170022
170511
  var probeDataVersionStatements = new WeakMap;
170023
- var probeTotalChangesStatements = new WeakMap;
170024
170512
  function getProbeDataVersionStatement(db) {
170025
170513
  let stmt = probeDataVersionStatements.get(db);
170026
170514
  if (!stmt) {
@@ -170029,14 +170517,6 @@ function getProbeDataVersionStatement(db) {
170029
170517
  }
170030
170518
  return stmt;
170031
170519
  }
170032
- function getProbeTotalChangesStatement(db) {
170033
- let stmt = probeTotalChangesStatements.get(db);
170034
- if (!stmt) {
170035
- stmt = db.prepare(PROBE_TOTAL_CHANGES_SQL);
170036
- probeTotalChangesStatements.set(db, stmt);
170037
- }
170038
- return stmt;
170039
- }
170040
170520
  function isAssignmentRow(row) {
170041
170521
  if (row === null || typeof row !== "object") {
170042
170522
  return false;
@@ -170229,20 +170709,18 @@ function createTagger() {
170229
170709
  }
170230
170710
  function probeSignature(db) {
170231
170711
  const dvRow = getProbeDataVersionStatement(db).get();
170232
- const tcRow = getProbeTotalChangesStatement(db).get();
170233
170712
  return {
170234
- dataVersion: dvRow?.data_version ?? 0,
170235
- totalChanges: tcRow?.tc ?? 0
170713
+ dataVersion: dvRow?.data_version ?? 0
170236
170714
  };
170237
170715
  }
170238
- function initFromDb(sessionId, db) {
170716
+ function initFromDb(sessionId, db, floor = 0) {
170239
170717
  const probe = probeSignature(db);
170240
170718
  const cached2 = loadSignatures.get(sessionId);
170241
- 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) {
170242
170720
  return;
170243
170721
  }
170244
170722
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
170245
- 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);
170246
170724
  const sessionAssignments = getSessionAssignments(sessionId);
170247
170725
  sessionAssignments.clear();
170248
170726
  let maxTagNumber = 0;
@@ -170263,7 +170741,7 @@ function createTagger() {
170263
170741
  loadSignatures.set(sessionId, {
170264
170742
  db,
170265
170743
  dataVersion: probe.dataVersion,
170266
- totalChanges: probe.totalChanges
170744
+ floor
170267
170745
  });
170268
170746
  }
170269
170747
  function cleanup(sessionId) {
@@ -170287,6 +170765,162 @@ function createTagger() {
170287
170765
  };
170288
170766
  }
170289
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
+
170290
170924
  // ../plugin/src/features/magic-context/work-metrics.ts
170291
170925
  function asNumber2(value) {
170292
170926
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -170338,76 +170972,12 @@ function computePiWorkMetrics(sessionEntries) {
170338
170972
  return { newWorkTokens, totalInputTokens };
170339
170973
  }
170340
170974
 
170341
- // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
170342
- var SYSTEM_INJECTION_MARKERS = [
170343
- "<!-- OMO_INTERNAL_INITIATOR -->",
170344
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
170345
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
170346
- "[Category+Skill Reminder]",
170347
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
170348
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
170349
- "[EMERGENCY CONTEXT WINDOW WARNING]",
170350
- "Unstable background agent appears idle",
170351
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
170352
- ];
170353
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
170354
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
170355
- function stripSystemInjection(text) {
170356
- let hasInjection = false;
170357
- for (const marker of SYSTEM_INJECTION_MARKERS) {
170358
- if (text.includes(marker)) {
170359
- hasInjection = true;
170360
- break;
170361
- }
170362
- }
170363
- if (SYSTEM_REMINDER_REGEX.test(text))
170364
- hasInjection = true;
170365
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
170366
- if (!hasInjection)
170367
- return null;
170368
- let cleaned = text;
170369
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
170370
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
170371
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
170372
- for (const marker of SYSTEM_INJECTION_MARKERS) {
170373
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
170374
- continue;
170375
- const idx = cleaned.indexOf(marker);
170376
- if (idx === -1)
170377
- continue;
170378
- const blockEnd = cleaned.indexOf(`
170379
-
170380
- `, idx + marker.length);
170381
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
170382
- }
170383
- return cleaned.trim();
170384
- }
170385
-
170386
170975
  // ../plugin/src/hooks/magic-context/apply-operations.ts
170387
- var USER_DROP_PREVIEW_CHARS = 250;
170388
170976
  var RECENT_TOOL_SKELETON_WINDOW = 20;
170389
- function buildReplacementContent(tagId, target) {
170390
- const role = target.message?.info.role;
170391
- if (role !== "user") {
170392
- return `[dropped §${tagId}§]`;
170393
- }
170394
- const currentContent = target.getContent?.() ?? "";
170395
- const strippedInjection = stripSystemInjection(currentContent);
170396
- if (strippedInjection !== null && stripTagPrefix(strippedInjection).trim().length === 0) {
170397
- return `[dropped §${tagId}§]`;
170398
- }
170399
- const originalText = stripTagPrefix(currentContent);
170400
- if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
170401
- return `[truncated §${tagId}§]
170402
- ${originalText}`;
170403
- }
170404
- const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
170405
- const softCutIndex = hardCut.search(/\s\S*$/);
170406
- const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
170407
- return `[truncated §${tagId}§]
170408
- ${preview}…`;
170409
- }
170410
- function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
170977
+ function buildReplacementContent(tagId) {
170978
+ return `[dropped §${tagId}§]`;
170979
+ }
170980
+ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
170411
170981
  let didMutateMessage = false;
170412
170982
  db.transaction(() => {
170413
170983
  const tags = preloadedTags ?? getTagsBySession(db, sessionId);
@@ -170415,11 +170985,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
170415
170985
  const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
170416
170986
  const protectedTagIds = protectedTags > 0 ? new Set(tags.filter((tag) => tag.status === "active").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, protectedTags)) : new Set;
170417
170987
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
170988
+ const opsToApply = [
170989
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
170990
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
170991
+ ];
170418
170992
  const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
170419
- for (const pendingOp of pendingOps) {
170993
+ for (const { op: pendingOp, synthetic } of opsToApply) {
170420
170994
  const tagStatus = tagStatusById.get(pendingOp.tagId);
170421
170995
  if (tagStatus === "compacted" || tagStatus === "dropped") {
170422
- removePendingOp(db, sessionId, pendingOp.tagId);
170996
+ if (!synthetic)
170997
+ removePendingOp(db, sessionId, pendingOp.tagId);
170423
170998
  continue;
170424
170999
  }
170425
171000
  if (protectedTagIds.has(pendingOp.tagId)) {
@@ -170427,33 +171002,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
170427
171002
  }
170428
171003
  const target = targets.get(pendingOp.tagId);
170429
171004
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
171005
+ if (synthetic) {
171006
+ if (!isToolTag || target?.canDrop?.() !== true)
171007
+ continue;
171008
+ }
171009
+ let shouldPersistDrop = false;
170430
171010
  if (isToolTag) {
170431
171011
  if (skeletonWindow.has(pendingOp.tagId)) {
170432
171012
  const truncResult = target?.truncate?.() ?? "absent";
170433
- if (truncResult === "incomplete") {
171013
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
170434
171014
  continue;
170435
171015
  }
170436
171016
  if (truncResult === "truncated") {
170437
171017
  didMutateMessage = true;
170438
171018
  }
170439
171019
  updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
171020
+ shouldPersistDrop = true;
170440
171021
  } else {
170441
171022
  const dropResult = target?.drop?.() ?? "absent";
170442
- if (dropResult === "incomplete") {
171023
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
170443
171024
  continue;
170444
171025
  }
170445
171026
  if (dropResult === "removed") {
170446
171027
  didMutateMessage = true;
170447
171028
  }
170448
171029
  updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
171030
+ shouldPersistDrop = true;
170449
171031
  }
170450
171032
  } else if (target) {
170451
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId, target));
171033
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
170452
171034
  if (changed)
170453
171035
  didMutateMessage = true;
171036
+ shouldPersistDrop = true;
171037
+ } else if (!synthetic) {
171038
+ shouldPersistDrop = true;
170454
171039
  }
171040
+ if (!shouldPersistDrop)
171041
+ continue;
170455
171042
  updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
170456
- removePendingOp(db, sessionId, pendingOp.tagId);
171043
+ if (!synthetic)
171044
+ removePendingOp(db, sessionId, pendingOp.tagId);
170457
171045
  }
170458
171046
  })();
170459
171047
  return didMutateMessage;
@@ -170477,7 +171065,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
170477
171065
  }
170478
171066
  }
170479
171067
  } else if (target) {
170480
- const changed = target.setContent(buildReplacementContent(tag.tagNumber, target));
171068
+ const changed = target.setContent(buildReplacementContent(tag.tagNumber));
170481
171069
  if (changed)
170482
171070
  didMutateMessage = true;
170483
171071
  }
@@ -170765,7 +171353,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
170765
171353
  const result = {
170766
171354
  compressedToLite: 0,
170767
171355
  compressedToFull: 0,
170768
- compressedToUltra: 0
171356
+ compressedToUltra: 0,
171357
+ mutatedTextTags: 0
170769
171358
  };
170770
171359
  if (!config2.enabled)
170771
171360
  return result;
@@ -170806,7 +171395,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
170806
171395
  const target = targets.get(tag.tagNumber);
170807
171396
  if (!target)
170808
171397
  continue;
170809
- target.setContent(compressed);
171398
+ const didMutate = target.setContent(compressed);
171399
+ if (didMutate)
171400
+ result.mutatedTextTags += 1;
170810
171401
  updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
170811
171402
  if (targetDepth === DEPTH_LITE)
170812
171403
  result.compressedToLite += 1;
@@ -171184,7 +171775,7 @@ function buildToolArcs(messages) {
171184
171775
  }
171185
171776
  return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
171186
171777
  }
171187
- function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
171778
+ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
171188
171779
  let boundary = candidate;
171189
171780
  for (const arc of arcs) {
171190
171781
  if (arc.resOrdinal !== null) {
@@ -171193,6 +171784,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
171193
171784
  }
171194
171785
  continue;
171195
171786
  }
171787
+ if (arc.invOrdinal < recentOpenArcCutoff)
171788
+ continue;
171196
171789
  if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
171197
171790
  return arc.invOrdinal;
171198
171791
  }
@@ -171430,7 +172023,7 @@ function semanticSnapBoundary(args) {
171430
172023
  return snapped;
171431
172024
  }
171432
172025
  function applyHeadCap(args) {
171433
- const { index, protectedTailStart, offset, arcs, capTokens } = args;
172026
+ const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
171434
172027
  if (offset >= protectedTailStart)
171435
172028
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
171436
172029
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
@@ -171438,7 +172031,7 @@ function applyHeadCap(args) {
171438
172031
  for (const arc of arcs) {
171439
172032
  const resOrdinal = arc.resOrdinal;
171440
172033
  if (resOrdinal === null) {
171441
- if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
172034
+ if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
171442
172035
  end = Math.min(end, arc.invOrdinal);
171443
172036
  }
171444
172037
  continue;
@@ -171505,7 +172098,14 @@ function resolveProtectedTailBoundary(ctx) {
171505
172098
  }
171506
172099
  if (ctx.mode === "manual-full-recomp") {
171507
172100
  const arcs2 = buildToolArcs(messages);
171508
- const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset);
172101
+ const recompTarget = deriveProtectedTailTokenTarget({
172102
+ contextLimit: ctx.contextLimit,
172103
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
172104
+ usagePercentage: 0,
172105
+ triggerBudget: ctx.triggerBudget
172106
+ });
172107
+ const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
172108
+ const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
171509
172109
  const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
171510
172110
  const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
171511
172111
  return {
@@ -171547,13 +172147,14 @@ function resolveProtectedTailBoundary(ctx) {
171547
172147
  const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
171548
172148
  const arcs = buildToolArcs(messages);
171549
172149
  let boundary = index.findSuffixStartForTokens(scaledN);
172150
+ const recentOpenArcCutoff = boundary;
171550
172151
  let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
171551
172152
  const tokenAtBoundary = index.tokenForOrdinal(boundary);
171552
172153
  if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
171553
172154
  boundary += 1;
171554
172155
  boundaryReason = "huge-message-exception";
171555
172156
  }
171556
- boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
172157
+ boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171557
172158
  const snapped = semanticSnapBoundary({
171558
172159
  messages,
171559
172160
  index,
@@ -171563,7 +172164,7 @@ function resolveProtectedTailBoundary(ctx) {
171563
172164
  });
171564
172165
  if (snapped !== boundary)
171565
172166
  boundaryReason = "semantic-snap";
171566
- boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
172167
+ boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171567
172168
  let runtimeFloor = offset;
171568
172169
  if (ctx.migrationFloorActive)
171569
172170
  runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
@@ -171599,7 +172200,8 @@ function resolveProtectedTailBoundary(ctx) {
171599
172200
  offset,
171600
172201
  arcs,
171601
172202
  lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
171602
- capTokens: perRunCap
172203
+ capTokens: perRunCap,
172204
+ recentOpenArcCutoff
171603
172205
  });
171604
172206
  const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
171605
172207
  return {
@@ -171650,7 +172252,7 @@ function resolveBoundaryContext(args) {
171650
172252
  }
171651
172253
  let storedTokenTotals;
171652
172254
  try {
171653
- storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId).totals;
172255
+ storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId, args.taggerFloor ?? 0).totals;
171654
172256
  } catch (error51) {
171655
172257
  sessionLog(args.sessionId, "protected-tail stored-token map unavailable (live fallback):", error51);
171656
172258
  }
@@ -171767,6 +172369,38 @@ var DEFAULT_MIN_COMMIT_CLUSTERS_FOR_TRIGGER = 3;
171767
172369
  var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
171768
172370
  var FORCE_COMPARTMENT_PERCENTAGE = 80;
171769
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
+ }
171770
172404
  function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
171771
172405
  return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
171772
172406
  }
@@ -171820,7 +172454,7 @@ function resolveBoundaryContextLimit(usage, fallbackContextLimit) {
171820
172454
  }
171821
172455
  return 128000;
171822
172456
  }
171823
- function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail) {
172457
+ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor = 0) {
171824
172458
  return withRawSessionMessageCache(() => {
171825
172459
  try {
171826
172460
  const memoryPrimed = inMemoryTail ? primeInMemoryTailRawMessageCache({
@@ -171849,7 +172483,8 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
171849
172483
  contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
171850
172484
  executeThresholdPercentage,
171851
172485
  usage,
171852
- usageSource: "live"
172486
+ usageSource: "live",
172487
+ taggerFloor
171853
172488
  });
171854
172489
  const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
171855
172490
  if (!hasProtectedEligibleHead) {
@@ -171880,7 +172515,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
171880
172515
  }
171881
172516
  });
171882
172517
  }
171883
- 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) {
171884
172519
  if (sessionMeta.compartmentInProgress) {
171885
172520
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
171886
172521
  return { shouldFire: false };
@@ -171894,14 +172529,17 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
171894
172529
  inMemoryTail = undefined;
171895
172530
  }
171896
172531
  }
172532
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
171897
172533
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
171898
- if (!inMemoryTail && usage.percentage < proactiveFloorForGate) {
172534
+ if (usage.percentage < proactiveFloorForGate) {
171899
172535
  try {
171900
- const { bound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId);
172536
+ const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, taggerFloor);
171901
172537
  if (nullCount === 0) {
171902
- const eligibleUpperBound = bound;
172538
+ const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
172539
+ const eligibleUpperBound = persistedBound + untaggedUpperBound;
171903
172540
  if (eligibleUpperBound < triggerBudget) {
171904
- 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`);
171905
172543
  return { shouldFire: false };
171906
172544
  }
171907
172545
  }
@@ -171909,7 +172547,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
171909
172547
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
171910
172548
  }
171911
172549
  }
171912
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail);
172550
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
171913
172551
  if (!tailInfo.hasNewRawHistory) {
171914
172552
  try {
171915
172553
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -172073,6 +172711,13 @@ function computePressure(input) {
172073
172711
  function approxThousands(tokens) {
172074
172712
  return `${Math.round(tokens / 1000)}k`;
172075
172713
  }
172714
+ function formatOldestReclaimableHint(hint) {
172715
+ if (!hint || hint.length === 0)
172716
+ return "";
172717
+ const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
172718
+ return rendered.length > 0 ? `
172719
+ oldest reclaimable: ${rendered}.` : "";
172720
+ }
172076
172721
  var CHANNEL2_USABLE_FRACTION = 1 / 3;
172077
172722
  var CHANNEL2_MIN_RECLAIMABLE = 1e4;
172078
172723
  function shouldTriggerChannel2(input) {
@@ -172082,14 +172727,16 @@ function shouldTriggerChannel2(input) {
172082
172727
  return true;
172083
172728
  return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
172084
172729
  }
172085
- function buildChannel2Reminder(undroppedTokens) {
172730
+ function buildChannel2Reminder(undroppedTokens, hint) {
172086
172731
  const amount = approxThousands(undroppedTokens);
172732
+ const hintText = formatOldestReclaimableHint(hint);
172087
172733
  return `<system-reminder>
172088
- ` + `Routine context housekeeping is near: a large span of this session will be comparted soon, ` + `and ~${amount} tokens of tool output remain unreduced. Drop spent outputs with ctx_reduce ` + `first so the archived span is the part that matters.
172734
+ ` + `Routine context housekeeping is near: a large span of this session will be comparted soon, ` + `and ~${amount} tokens of tool output remain unreduced. Drop spent outputs with ctx_reduce ` + `first so the archived span is the part that matters.${hintText}
172089
172735
  ` + `</system-reminder>`;
172090
172736
  }
172091
- function buildChannel1Reminder(level, undroppedTokens) {
172737
+ function buildChannel1Reminder(level, undroppedTokens, hint) {
172092
172738
  const amount = approxThousands(undroppedTokens);
172739
+ const hintText = formatOldestReclaimableHint(hint);
172093
172740
  let body;
172094
172741
  switch (level) {
172095
172742
  case "gentle":
@@ -172105,7 +172752,7 @@ function buildChannel1Reminder(level, undroppedTokens) {
172105
172752
  return `
172106
172753
 
172107
172754
  <system-reminder>
172108
- ${body}
172755
+ ${body}${hintText}
172109
172756
  </system-reminder>`;
172110
172757
  }
172111
172758
 
@@ -172653,6 +173300,41 @@ function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions
172653
173300
  `);
172654
173301
  }
172655
173302
 
173303
+ // ../plugin/src/hooks/magic-context/tool-reclaim.ts
173304
+ function buildSyntheticToolReclaimOps(input) {
173305
+ const watermark = Math.max(0, input.watermark);
173306
+ if (watermark <= 0)
173307
+ return [];
173308
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
173309
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
173310
+ const synthetic = [];
173311
+ for (const tag of tags) {
173312
+ if (tag.type !== "tool")
173313
+ continue;
173314
+ if (tag.status !== "active")
173315
+ continue;
173316
+ if (tag.tagNumber > watermark)
173317
+ continue;
173318
+ if (realPendingTagIds.has(tag.tagNumber))
173319
+ continue;
173320
+ if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
173321
+ continue;
173322
+ synthetic.push({
173323
+ id: 0,
173324
+ sessionId: input.sessionId,
173325
+ tagId: tag.tagNumber,
173326
+ operation: "drop",
173327
+ queuedAt: 0
173328
+ });
173329
+ }
173330
+ return synthetic;
173331
+ }
173332
+ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
173333
+ const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
173334
+ advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
173335
+ return maxTagNumber;
173336
+ }
173337
+
172656
173338
  // src/context-handler.ts
172657
173339
  init_logger();
172658
173340
 
@@ -173063,7 +173745,7 @@ function tagToolPart(args) {
173063
173745
  const tagged = prependTag(tagId, text);
173064
173746
  args.part.setText(tagged);
173065
173747
  }
173066
- args.targets.set(tagId, buildToolTarget(args.part, args.message));
173748
+ args.targets.set(tagId, buildToolTarget(args.part, args.message, tagId));
173067
173749
  }
173068
173750
  function setToolContentOrText(part, content) {
173069
173751
  try {
@@ -173103,7 +173785,7 @@ function buildAggregateTarget(tagId, occurrences) {
173103
173785
  return any2 ? "removed" : "absent";
173104
173786
  },
173105
173787
  truncate() {
173106
- const sentinel = "[truncated]";
173788
+ const sentinel = `[dropped §${tagId}§]`;
173107
173789
  let any2 = false;
173108
173790
  for (const occ of occurrences) {
173109
173791
  if (setToolContentOrText(occ.part, sentinel)) {
@@ -173135,7 +173817,7 @@ function buildTextTarget(part, message) {
173135
173817
  }
173136
173818
  };
173137
173819
  }
173138
- function buildToolTarget(part, message) {
173820
+ function buildToolTarget(part, message, tagId) {
173139
173821
  return {
173140
173822
  setContent(content) {
173141
173823
  return setToolContentOrText(part, content);
@@ -173144,11 +173826,11 @@ function buildToolTarget(part, message) {
173144
173826
  return part.getText() ?? null;
173145
173827
  },
173146
173828
  drop() {
173147
- const replaced = part.replaceWithSentinel(`[dropped §${part.id ?? "?"}§]`);
173829
+ const replaced = part.replaceWithSentinel(`[dropped §${tagId}§]`);
173148
173830
  return replaced ? "removed" : "absent";
173149
173831
  },
173150
173832
  truncate() {
173151
- const ok = setToolContentOrText(part, "[truncated]");
173833
+ const ok = setToolContentOrText(part, `[dropped §${tagId}§]`);
173152
173834
  return ok ? "truncated" : "absent";
173153
173835
  },
173154
173836
  message: {
@@ -174179,7 +174861,7 @@ async function runAutoSearchHintForPi(args) {
174179
174861
  embeddingEnabled,
174180
174862
  gitCommitsEnabled,
174181
174863
  embedQuery: async (text, signal) => {
174182
- const result = await embedTextForProject(options.projectPath, text, signal);
174864
+ const result = await embedTextForProject(options.projectPath, text, signal, "query");
174183
174865
  return result?.vector ?? null;
174184
174866
  },
174185
174867
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -174433,9 +175115,10 @@ function maybeChannel1ReminderForToolResult(args) {
174433
175115
  return null;
174434
175116
  return {
174435
175117
  type: "text",
174436
- text: buildChannel1Reminder(decision.level, decision.undroppedTokens)
175118
+ text: buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags)
174437
175119
  };
174438
175120
  }
175121
+ var CHANNEL2_NUDGE_CUSTOM_TYPE = "magic-context:ceiling-nudge";
174439
175122
  function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
174440
175123
  let state;
174441
175124
  try {
@@ -174463,9 +175146,12 @@ function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
174463
175146
  if (!casChannel2NudgeState(db, sessionId, "pending", "claimed"))
174464
175147
  return false;
174465
175148
  try {
174466
- pi.sendUserMessage(buildChannel2Reminder(undropped), {
174467
- deliverAs
174468
- });
175149
+ pi.sendMessage({
175150
+ customType: CHANNEL2_NUDGE_CUSTOM_TYPE,
175151
+ content: buildChannel2Reminder(undropped, baseline.oldestReclaimableToolTags),
175152
+ display: false,
175153
+ details: { kind: "channel-2-ceiling-nudge" }
175154
+ }, { deliverAs });
174469
175155
  } catch (error51) {
174470
175156
  try {
174471
175157
  casChannel2NudgeState(db, sessionId, "claimed", "pending");
@@ -174657,6 +175343,51 @@ function planEmergencyDrop(input) {
174657
175343
  };
174658
175344
  }
174659
175345
 
175346
+ // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
175347
+ var SYSTEM_INJECTION_MARKERS = [
175348
+ "<!-- OMO_INTERNAL_INITIATOR -->",
175349
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
175350
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
175351
+ "[Category+Skill Reminder]",
175352
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
175353
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
175354
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
175355
+ "Unstable background agent appears idle",
175356
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
175357
+ ];
175358
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
175359
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
175360
+ function stripSystemInjection(text) {
175361
+ let hasInjection = false;
175362
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
175363
+ if (text.includes(marker)) {
175364
+ hasInjection = true;
175365
+ break;
175366
+ }
175367
+ }
175368
+ if (SYSTEM_REMINDER_REGEX.test(text))
175369
+ hasInjection = true;
175370
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
175371
+ if (!hasInjection)
175372
+ return null;
175373
+ let cleaned = text;
175374
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
175375
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
175376
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
175377
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
175378
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
175379
+ continue;
175380
+ const idx = cleaned.indexOf(marker);
175381
+ if (idx === -1)
175382
+ continue;
175383
+ const blockEnd = cleaned.indexOf(`
175384
+
175385
+ `, idx + marker.length);
175386
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
175387
+ }
175388
+ return cleaned.trim();
175389
+ }
175390
+
174660
175391
  // src/heuristic-cleanup-pi.ts
174661
175392
  init_logger();
174662
175393
  var DEDUP_SAFE_TOOLS = new Set([
@@ -174760,6 +175491,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174760
175491
  const protectedCutoff = maxTag - config2.protectedTags;
174761
175492
  const toolAgeCutoff = protectedCutoff;
174762
175493
  let droppedTools = 0;
175494
+ let emergencyDroppedTools = 0;
174763
175495
  let deduplicatedTools = 0;
174764
175496
  let droppedInjections = 0;
174765
175497
  let droppedStaleReduceCalls = 0;
@@ -174792,6 +175524,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174792
175524
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
174793
175525
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
174794
175526
  droppedTools++;
175527
+ emergencyDroppedTools++;
174795
175528
  }
174796
175529
  }
174797
175530
  setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
@@ -174815,10 +175548,14 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174815
175548
  if (!matched)
174816
175549
  continue;
174817
175550
  const target = targets.get(tag.tagNumber);
174818
- target?.drop?.();
175551
+ const result = target?.drop?.() ?? "absent";
175552
+ if (result === "incomplete")
175553
+ continue;
174819
175554
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
174820
175555
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
174821
- droppedStaleReduceCalls++;
175556
+ if (result === "removed" || result === "truncated") {
175557
+ droppedStaleReduceCalls++;
175558
+ }
174822
175559
  }
174823
175560
  })();
174824
175561
  }
@@ -174890,7 +175627,9 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174890
175627
  continue;
174891
175628
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
174892
175629
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
174893
- deduplicatedTools++;
175630
+ if (result === "removed" || result === "truncated") {
175631
+ deduplicatedTools++;
175632
+ }
174894
175633
  }
174895
175634
  }
174896
175635
  })();
@@ -174899,6 +175638,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174899
175638
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
174900
175639
  }
174901
175640
  let compressedTextTags = 0;
175641
+ let mutatedTextTags = 0;
174902
175642
  if (config2.caveman?.enabled) {
174903
175643
  const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
174904
175644
  enabled: true,
@@ -174906,13 +175646,16 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174906
175646
  protectedTags: config2.protectedTags
174907
175647
  });
174908
175648
  compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
175649
+ mutatedTextTags = cavemanResult.mutatedTextTags;
174909
175650
  }
174910
175651
  return {
174911
175652
  droppedTools,
174912
175653
  deduplicatedTools,
174913
175654
  droppedInjections,
174914
175655
  droppedStaleReduceCalls,
174915
- compressedTextTags
175656
+ emergencyDroppedTools,
175657
+ compressedTextTags,
175658
+ mutatedTextTags
174916
175659
  };
174917
175660
  }
174918
175661
  function buildMessageIdToMaxTagFromTargets(targets) {
@@ -176208,7 +176951,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
176208
176951
  for (const compartment of compartments) {
176209
176952
  try {
176210
176953
  const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
176211
- 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);
176212
176955
  if (canonicalText.length === 0)
176213
176956
  continue;
176214
176957
  const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
@@ -179401,6 +180144,7 @@ async function runPiHistorian(deps) {
179401
180144
  } else {
179402
180145
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
179403
180146
  }
180147
+ clearEmergencyDrainLatch(db, sessionId);
179404
180148
  return;
179405
180149
  }
179406
180150
  const perRunCap = selectPerRunCap(boundarySnapshot);
@@ -179412,7 +180156,8 @@ async function runPiHistorian(deps) {
179412
180156
  trueRawTokens: boundarySnapshot.trueRawEligibleTokens,
179413
180157
  usagePercentage: boundarySnapshot.usagePercentage,
179414
180158
  usable,
179415
- perRunCap
180159
+ perRunCap,
180160
+ executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
179416
180161
  });
179417
180162
  if (!reserve.ok) {
179418
180163
  sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
@@ -179429,6 +180174,7 @@ async function runPiHistorian(deps) {
179429
180174
  } else {
179430
180175
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
179431
180176
  }
180177
+ clearEmergencyDrainLatch(db, sessionId);
179432
180178
  telemetry.status = "noop";
179433
180179
  telemetry.failureReason = "chunk empty after filtering";
179434
180180
  rollbackDrainReservation();
@@ -179660,6 +180406,7 @@ ${chunkText}`,
179660
180406
  }
179661
180407
  appendCompartments(db, sessionId, newCompartments);
179662
180408
  clearHistorianFailureState(db, sessionId);
180409
+ clearHistorianDrainFailure(db, sessionId);
179663
180410
  recordProtectedTailPublicationFloor(db, sessionId, lastNewEnd + 1);
179664
180411
  clearEmergencyRecovery(db, sessionId);
179665
180412
  if (firstKeptEntryId && lastNewEndMessageId) {
@@ -179756,8 +180503,12 @@ ${chunkText}`,
179756
180503
  await notify(buildHistorianFailureNotice(failCount, desc.brief));
179757
180504
  }
179758
180505
  } finally {
179759
- if (!completedSuccessfully && !retainDrainReservationForRetryThrottle) {
179760
- rollbackDrainReservation();
180506
+ if (!completedSuccessfully) {
180507
+ if (!retainDrainReservationForRetryThrottle) {
180508
+ rollbackDrainReservation();
180509
+ } else {
180510
+ recordHistorianDrainFailure(db, sessionId);
180511
+ }
179761
180512
  }
179762
180513
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
179763
180514
  try {
@@ -179996,7 +180747,7 @@ var INLINE_THINKING_PATTERNS = [
179996
180747
  /<thinking>[\s\S]*?<\/thinking>\s*/gi,
179997
180748
  /<think>[\s\S]*?<\/think>\s*/gi
179998
180749
  ];
179999
- var CLEARED = "[cleared]";
180750
+ var CLEARED = "";
180000
180751
  function stripInlineThinkingMarkup(text) {
180001
180752
  let cleaned = text;
180002
180753
  for (const pattern of INLINE_THINKING_PATTERNS) {
@@ -180045,8 +180796,11 @@ function clearOldReasoningPi(args) {
180045
180796
  for (const part of msg.content) {
180046
180797
  if (part && typeof part === "object" && part.type === "thinking") {
180047
180798
  const tp = part;
180048
- if (tp.thinking !== CLEARED) {
180799
+ if (tp.redacted)
180800
+ continue;
180801
+ if (tp.thinking !== CLEARED || tp.thinkingSignature !== undefined) {
180049
180802
  tp.thinking = CLEARED;
180803
+ tp.thinkingSignature = undefined;
180050
180804
  cleared++;
180051
180805
  }
180052
180806
  }
@@ -180124,8 +180878,11 @@ function replayClearedReasoningPi(args) {
180124
180878
  for (const part of msg.content) {
180125
180879
  if (part && typeof part === "object" && part.type === "thinking") {
180126
180880
  const tp = part;
180127
- if (tp.thinking !== CLEARED) {
180881
+ if (tp.redacted)
180882
+ continue;
180883
+ if (tp.thinking !== CLEARED || tp.thinkingSignature !== undefined) {
180128
180884
  tp.thinking = CLEARED;
180885
+ tp.thinkingSignature = undefined;
180129
180886
  cleared++;
180130
180887
  }
180131
180888
  }
@@ -180289,9 +181046,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
180289
181046
  var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
180290
181047
  var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
180291
181048
  var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
180292
- Use \`ctx_reduce\` to manage context size. It supports one operation:
180293
- - \`drop\`: Remove entirely (best for tool outputs you already acted on).
180294
- Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
181049
+ Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking is NOT an immediate delete — it queues the content, which stays fully visible until space is actually needed (as soon as the next turn if you're already under pressure, much later if not), so mark a tool output as soon as you're done with it rather than hoarding the call for the end of the turn. The last ${protectedTags} tags are protected (marking one just queues it until it ages out). Syntax: "3-5", "1,2,9", or "1-5,8,12-15".
180295
181050
  Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
180296
181051
  ${CTX_NOTE_GUIDANCE}
180297
181052
  Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
@@ -180310,7 +181065,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
180310
181065
  \`ctx_search\` returns ranked results from memories, git commits, and raw message history. Use message ordinals from results with \`ctx_expand\` to retrieve surrounding conversation context.
180311
181066
  ${TOOL_HISTORY_GUIDANCE}
180312
181067
  NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
180313
- NEVER drop user messagesthey are short and will be summarized by compartmentalization automatically. Dropping them loses context the historian needs.
181068
+ Keep your user's instructions and intent never drop a user message for its directive, even an old one. But a large block of pasted content inside a user message (logs, data dumps, long code, attachments) is fair to mark discardable once you've extracted what you need — it stays searchable via \`ctx_search\`.
180314
181069
  NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
180315
181070
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
180316
181071
  var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
@@ -181442,6 +182197,11 @@ function registerPiContextHandler(pi, baseOptions) {
181442
182197
  updateSessionProjectTracking(sessionId, projectIdentity, options.db);
181443
182198
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
181444
182199
  const branchEntries = readPiBranchEntriesForContext(ctx, sessionId);
182200
+ schedulePiTransformDecisionResolve({
182201
+ db: options.db,
182202
+ sessionId,
182203
+ branchEntries
182204
+ });
181445
182205
  const rawMessageProvider = {
181446
182206
  readMessages: () => branchEntries !== null ? convertEntriesToRawMessages([...branchEntries]) : readPiSessionMessages(ctx),
181447
182207
  readMessageById: (messageId) => readPiSessionMessageById(ctx, messageId)
@@ -181678,6 +182438,20 @@ function registerPiContextHandler(pi, baseOptions) {
181678
182438
  appendCompaction: resolvePiAppendCompaction(ctx),
181679
182439
  readBranchEntries: resolvePiReadBranchEntries(ctx)
181680
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
+ }
181681
182455
  if (options.historian) {
181682
182456
  maybeFireHistorian({
181683
182457
  ctx,
@@ -181787,6 +182561,7 @@ function registerPiContextHandler(pi, baseOptions) {
181787
182561
  const executeThresholdTokensPi = Math.round((usageContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
181788
182562
  const usableTokensPi = Math.max(0, executeThresholdTokensPi - usageInputTokens + liveTailTokens);
181789
182563
  resetLastNudgeCycleIfTailShrank(options.db, sessionId, tailToolTokens);
182564
+ const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(options.db, sessionId, options.protectedTags ?? 20);
181790
182565
  setPiChannel1Baseline(sessionId, {
181791
182566
  tailToolTokens,
181792
182567
  historyBudgetTokens: historyBudgetTokens ?? 0,
@@ -181795,7 +182570,8 @@ function registerPiContextHandler(pi, baseOptions) {
181795
182570
  lastInputTokens: usageInputTokens,
181796
182571
  turnToolTokens: 0,
181797
182572
  usableTokens: usableTokensPi,
181798
- reducedSinceRefresh: false
182573
+ reducedSinceRefresh: false,
182574
+ oldestReclaimableToolTags
181799
182575
  });
181800
182576
  if (usageContextLimit && usageContextLimit > 0 && resolvedExecuteThresholdPct > 0) {
181801
182577
  const channel2ShouldTrigger = shouldTriggerChannel2({
@@ -181823,6 +182599,7 @@ function registerPiContextHandler(pi, baseOptions) {
181823
182599
  logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
181824
182600
  sessionLog(sessionId, `transform completed in ${(performance.now() - transformStartTime).toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
181825
182601
  clearLastTransformErrorIfSet(options.db, sessionId);
182602
+ options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity);
181826
182603
  return { messages: outputMessages };
181827
182604
  } catch (err) {
181828
182605
  const message = err instanceof Error ? err.message : String(err);
@@ -182169,6 +182946,13 @@ async function runPipeline(args) {
182169
182946
  let historyWasConsumedThisPass = false;
182170
182947
  let materializationSatisfiedThisPass = false;
182171
182948
  let pendingOpsAppliedThisPass = false;
182949
+ let pendingOpsDidMutate = false;
182950
+ let heuristicOrReasoningDidMutate = false;
182951
+ let didMutateFromFlushedStatuses = false;
182952
+ let droppedCount = 0;
182953
+ const droppedTokens = 0;
182954
+ let emergency = false;
182955
+ let autoReclaimDidMutateThisPass = false;
182172
182956
  let suppressDeferredHistoryDrain = false;
182173
182957
  let casLost = false;
182174
182958
  const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
@@ -182254,7 +183038,10 @@ async function runPipeline(args) {
182254
183038
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
182255
183039
  try {
182256
183040
  const tApplyPending = performance.now();
182257
- applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
183041
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
183042
+ if (pendingOpsDidMutate) {
183043
+ droppedCount += pendingOps.length;
183044
+ }
182258
183045
  logTransformTiming(args.sessionId, "applyPendingOperations", tApplyPending);
182259
183046
  executedWorkThisPass = true;
182260
183047
  materializationSatisfiedThisPass = true;
@@ -182276,7 +183063,7 @@ async function runPipeline(args) {
182276
183063
  const flushedSliceTags = getTagsByNumbers(args.db, args.sessionId, targetTagNumbers);
182277
183064
  logTransformTiming(args.sessionId, "getTagsByNumbers", tGetTags, `targets=${targetTagNumbers.length} fetched=${flushedSliceTags.length}`);
182278
183065
  const tFlushed = performance.now();
182279
- applyFlushedStatuses(args.sessionId, args.db, targets, flushedSliceTags);
183066
+ didMutateFromFlushedStatuses = applyFlushedStatuses(args.sessionId, args.db, targets, flushedSliceTags);
182280
183067
  logTransformTiming(args.sessionId, "applyFlushedStatuses", tFlushed);
182281
183068
  logTransformTiming(args.sessionId, "batchFinalize:flushed", tFlushed);
182282
183069
  const messageIdToMaxTag = buildMessageIdToMaxTag(targets);
@@ -182338,6 +183125,11 @@ async function runPipeline(args) {
182338
183125
  } : undefined,
182339
183126
  caveman: args.heuristics.caveman
182340
183127
  }, activeTags, stableIdResolver);
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;
183131
+ if (heuristicMutationCount > 0)
183132
+ heuristicOrReasoningDidMutate = true;
182341
183133
  heuristicsExecuted = true;
182342
183134
  executedWorkThisPass = true;
182343
183135
  if (hasPendingMaterializeSignal) {
@@ -182346,7 +183138,7 @@ async function runPipeline(args) {
182346
183138
  if (currentTurnId !== null) {
182347
183139
  lastHeuristicsTurnIdBySession.set(args.sessionId, currentTurnId);
182348
183140
  }
182349
- logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} compressedTextTags=${heuristicsResult.compressedTextTags}`);
183141
+ logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} staleReduce=${heuristicsResult.droppedStaleReduceCalls} compressedTextTags=${heuristicsResult.compressedTextTags} mutatedTextTags=${heuristicsResult.mutatedTextTags}`);
182350
183142
  } catch (err) {
182351
183143
  sessionLog(args.sessionId, `heuristic cleanup failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182352
183144
  }
@@ -182383,6 +183175,10 @@ async function runPipeline(args) {
182383
183175
  }
182384
183176
  logTransformTiming(args.sessionId, "clearOldReasoning", tClearReasoning);
182385
183177
  logTransformTiming(args.sessionId, "watermarkCleanup", tClearReasoning);
183178
+ if (clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
183179
+ heuristicOrReasoningDidMutate = true;
183180
+ droppedCount += clearOutcome.cleared + stripOutcome.stripped;
183181
+ }
182386
183182
  if (combinedWatermark > prevWatermark || clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
182387
183183
  executedWorkThisPass = true;
182388
183184
  }
@@ -182390,7 +183186,36 @@ async function runPipeline(args) {
182390
183186
  sessionLog(args.sessionId, `reasoning clearing failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182391
183187
  }
182392
183188
  }
183189
+ const toolReclaimExecutePass = args.schedulerDecision === "execute";
183190
+ const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
183191
+ const emergencyDropEligible = args.forceMaterialization === true || args.contextUsage.percentage >= FORCE_MATERIALIZATION_PERCENTAGE;
183192
+ let autoReclaimTargetCount = 0;
183193
+ let autoReclaimDidMutate = false;
183194
+ if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
183195
+ const reclaimMeta = getOrCreateSessionMeta(args.db, args.sessionId);
183196
+ const syntheticPendingOps = buildSyntheticToolReclaimOps({
183197
+ db: args.db,
183198
+ sessionId: args.sessionId,
183199
+ targets,
183200
+ watermark: reclaimMeta.toolReclaimWatermark ?? 0,
183201
+ pendingOps
183202
+ });
183203
+ autoReclaimTargetCount = syntheticPendingOps.length;
183204
+ if (syntheticPendingOps.length > 0) {
183205
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, [], syntheticPendingOps);
183206
+ if (autoReclaimDidMutate) {
183207
+ droppedCount += syntheticPendingOps.length;
183208
+ autoReclaimDidMutateThisPass = true;
183209
+ }
183210
+ }
183211
+ }
182393
183212
  transcript.commit();
183213
+ if (toolReclaimExecutePass) {
183214
+ advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
183215
+ }
183216
+ if (autoReclaimTargetCount > 0) {
183217
+ sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
183218
+ }
182394
183219
  const postCommitStableIdByRef = new Map;
182395
183220
  const postCommitEntryIdByRef = new Map;
182396
183221
  for (let i = 0;i < args.messages.length; i++) {
@@ -182490,6 +183315,9 @@ async function runPipeline(args) {
182490
183315
  } catch (err) {
182491
183316
  sessionLog(args.sessionId, `token accounting failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182492
183317
  }
183318
+ const materialized = injectionResult?.m0Materialized === true;
183319
+ const materializeReason = injectionResult?.m0Reason ?? null;
183320
+ const bustedThisPass = didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || materialized || historyWasConsumedThisPass;
182493
183321
  return {
182494
183322
  messages: outputMessages,
182495
183323
  heuristicsExecuted,
@@ -182498,6 +183326,12 @@ async function runPipeline(args) {
182498
183326
  syntheticLeadingCount: injectionResult?.syntheticLeadingCount ?? 0,
182499
183327
  heuristicsResult,
182500
183328
  injectionResult,
183329
+ materialized,
183330
+ materializeReason,
183331
+ droppedTokens,
183332
+ droppedCount,
183333
+ emergency,
183334
+ bustedThisPass,
182501
183335
  targetCount: targets.size,
182502
183336
  reasoningWatermark: getOrCreateSessionMeta(args.db, args.sessionId).clearedReasoningThroughTag ?? 0,
182503
183337
  activeTags,
@@ -182674,6 +183508,7 @@ function clearContextHandlerSession(sessionId) {
182674
183508
  rawMessageProviderUnregistersBySession.delete(sessionId);
182675
183509
  }
182676
183510
  clearSessionTracking(sessionId);
183511
+ clearPiEmbedSessionState(sessionId);
182677
183512
  }
182678
183513
 
182679
183514
  // src/commands/ctx-flush.ts
@@ -182923,6 +183758,12 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
182923
183758
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
182924
183759
  import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
182925
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
182926
183767
  init_data_path();
182927
183768
  function historianResponseDumpDir(directory) {
182928
183769
  return getProjectMagicContextHistorianDir(directory);
@@ -185176,7 +186017,7 @@ function formatThresholdPercent(value) {
185176
186017
  // package.json
185177
186018
  var package_default = {
185178
186019
  name: "@wolfx/pi-magic-context",
185179
- version: "0.24.1",
186020
+ version: "0.26.0",
185180
186021
  type: "module",
185181
186022
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
185182
186023
  main: "dist/index.js",
@@ -185202,7 +186043,7 @@ var package_default = {
185202
186043
  "README.md"
185203
186044
  ],
185204
186045
  scripts: {
185205
- 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",
185206
186047
  typecheck: "tsc --noEmit",
185207
186048
  test: "bun test",
185208
186049
  lint: "biome check src",
@@ -185225,13 +186066,12 @@ var package_default = {
185225
186066
  "@earendil-works/pi-tui": "^0.74.0",
185226
186067
  "@types/better-sqlite3": "^7.6.13",
185227
186068
  "@types/bun": "^1.3.10",
185228
- "better-sqlite3": "^12.9.0",
185229
186069
  "@types/node": "^22.0.0",
185230
186070
  typescript: "^5.8.0"
185231
186071
  },
185232
186072
  peerDependencies: {
185233
- "@earendil-works/pi-coding-agent": "^0.74.0",
185234
- "@earendil-works/pi-tui": "^0.74.0"
186073
+ "@earendil-works/pi-coding-agent": "*",
186074
+ "@earendil-works/pi-tui": "*"
185235
186075
  },
185236
186076
  exports: {
185237
186077
  ".": {
@@ -185816,9 +186656,184 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
185816
186656
 
185817
186657
  ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
185818
186658
 
185819
- 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.`;
186659
+ 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.
186660
+
186661
+ Two recovery modes for finer detail:
186662
+ - 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.
186663
+ - 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.`;
185820
186664
  var CTX_EXPAND_TOKEN_BUDGET = 15000;
185821
186665
 
186666
+ // ../plugin/src/tools/ctx-expand/render.ts
186667
+ function isRecord3(value) {
186668
+ return value !== null && typeof value === "object" && !Array.isArray(value);
186669
+ }
186670
+ function roleLabel(role) {
186671
+ if (role === "assistant")
186672
+ return "A (assistant)";
186673
+ if (role === "user")
186674
+ return "U (user)";
186675
+ return role;
186676
+ }
186677
+ function truncate2(value, max) {
186678
+ const t = value.trim();
186679
+ return t.length <= max ? t : `${t.slice(0, max)}…`;
186680
+ }
186681
+ function keyArg(input) {
186682
+ if (!input)
186683
+ return "";
186684
+ for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
186685
+ const v = input[k];
186686
+ if (typeof v === "string" && v.length > 0)
186687
+ return truncate2(v, 60);
186688
+ }
186689
+ if (typeof input.description === "string")
186690
+ return truncate2(input.description, 60);
186691
+ return "";
186692
+ }
186693
+ function asToolPart(part) {
186694
+ const type = typeof part.type === "string" ? part.type : "";
186695
+ if (type === "tool") {
186696
+ const state = isRecord3(part.state) ? part.state : null;
186697
+ const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
186698
+ const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
186699
+ const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
186700
+ return {
186701
+ name: typeof part.tool === "string" ? part.tool : "tool",
186702
+ callId: typeof part.callID === "string" ? part.callID : "",
186703
+ title,
186704
+ input: state && isRecord3(state.input) ? state.input : null,
186705
+ output
186706
+ };
186707
+ }
186708
+ if (type === "tool_use") {
186709
+ return {
186710
+ name: typeof part.name === "string" ? part.name : "tool",
186711
+ callId: typeof part.id === "string" ? part.id : "",
186712
+ title: null,
186713
+ input: isRecord3(part.input) ? part.input : null,
186714
+ output: null
186715
+ };
186716
+ }
186717
+ if (type === "tool_result") {
186718
+ const content = part.content;
186719
+ const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
186720
+ return {
186721
+ name: "tool_result",
186722
+ callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
186723
+ title: null,
186724
+ input: null,
186725
+ output
186726
+ };
186727
+ }
186728
+ return null;
186729
+ }
186730
+ function textOf(part) {
186731
+ if (part.type === "text" && typeof part.text === "string")
186732
+ return part.text;
186733
+ return null;
186734
+ }
186735
+ function reasoningOf(part) {
186736
+ if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
186737
+ return part.text;
186738
+ }
186739
+ return null;
186740
+ }
186741
+ function renderPartPreview(part) {
186742
+ if (!isRecord3(part))
186743
+ return null;
186744
+ const text = textOf(part);
186745
+ if (text !== null) {
186746
+ const t = truncate2(text, 200);
186747
+ return t.length > 0 ? ` • ${t}` : null;
186748
+ }
186749
+ const tool = asToolPart(part);
186750
+ if (tool) {
186751
+ const arg = keyArg(tool.input);
186752
+ const head = arg ? `${tool.name}(${arg})` : tool.name;
186753
+ return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
186754
+ }
186755
+ const reasoning = reasoningOf(part);
186756
+ if (reasoning !== null)
186757
+ return ` • [reasoning] ${truncate2(reasoning, 120)}`;
186758
+ const type = typeof part.type === "string" ? part.type : "part";
186759
+ if (type === "file")
186760
+ return " • [file]";
186761
+ if (type === "step-start" || type === "step-finish")
186762
+ return null;
186763
+ return ` • [${type}]`;
186764
+ }
186765
+ function renderPartFull(part) {
186766
+ if (!isRecord3(part))
186767
+ return null;
186768
+ const text = textOf(part);
186769
+ if (text !== null) {
186770
+ return text.trim().length > 0 ? ` [text]
186771
+ ${text}` : null;
186772
+ }
186773
+ const tool = asToolPart(part);
186774
+ if (tool) {
186775
+ const lines = [];
186776
+ const idSuffix = tool.callId ? ` #${tool.callId}` : "";
186777
+ lines.push(` [tool: ${tool.name}${idSuffix}]`);
186778
+ if (tool.title && tool.title.trim().length > 0) {
186779
+ lines.push(` description: ${tool.title.trim()}`);
186780
+ }
186781
+ if (tool.input)
186782
+ lines.push(` input: ${JSON.stringify(tool.input)}`);
186783
+ if (tool.output !== null)
186784
+ lines.push(` output:
186785
+ ${tool.output}`);
186786
+ return lines.join(`
186787
+ `);
186788
+ }
186789
+ const type = typeof part.type === "string" ? part.type : "part";
186790
+ if (type === "file") {
186791
+ const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
186792
+ return ` [file]${name2 ? ` ${name2}` : ""}`;
186793
+ }
186794
+ return null;
186795
+ }
186796
+ function renderMessageByOrdinal(sessionId, ordinal) {
186797
+ const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
186798
+ if (!msg) {
186799
+ 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.`;
186800
+ }
186801
+ const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
186802
+ const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
186803
+ if (rendered.length === 0) {
186804
+ lines.push(" (no recoverable content — message had only structural/reasoning parts)");
186805
+ } else {
186806
+ lines.push(...rendered);
186807
+ }
186808
+ return lines.join(`
186809
+ `);
186810
+ }
186811
+ function renderVerboseRange(sessionId, start, end, tokenBudget) {
186812
+ const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
186813
+ const out = [];
186814
+ let usedTokens = 0;
186815
+ let lastOrdinal = start - 1;
186816
+ let truncated = false;
186817
+ for (const msg of messages) {
186818
+ const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
186819
+ const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
186820
+ const block = partLines.length > 0 ? `${header}
186821
+ ${partLines.join(`
186822
+ `)}` : header;
186823
+ const blockTokens = estimateTokens(block);
186824
+ if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
186825
+ truncated = true;
186826
+ break;
186827
+ }
186828
+ out.push(block);
186829
+ usedTokens += blockTokens;
186830
+ lastOrdinal = msg.ordinal;
186831
+ }
186832
+ return { text: out.join(`
186833
+
186834
+ `), lastOrdinal, truncated };
186835
+ }
186836
+
185822
186837
  // ../../node_modules/.bun/typebox@1.1.38/node_modules/typebox/build/system/memory/memory.mjs
185823
186838
  var exports_memory = {};
185824
186839
  __export(exports_memory, {
@@ -189884,12 +190899,18 @@ __export(exports_typebox, {
189884
190899
  });
189885
190900
  // src/tools/ctx-expand.ts
189886
190901
  var ParamsSchema = exports_typebox.Object({
189887
- start: exports_typebox.Number({
190902
+ start: exports_typebox.Optional(exports_typebox.Number({
189888
190903
  description: "Start message ordinal (from compartment start attribute)"
189889
- }),
189890
- end: exports_typebox.Number({
190904
+ })),
190905
+ end: exports_typebox.Optional(exports_typebox.Number({
189891
190906
  description: "End message ordinal (from compartment end attribute)"
189892
- })
190907
+ })),
190908
+ verbose: exports_typebox.Optional(exports_typebox.Boolean({
190909
+ 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."
190910
+ })),
190911
+ message: exports_typebox.Optional(exports_typebox.Number({
190912
+ 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."
190913
+ }))
189893
190914
  });
189894
190915
  function ok(text) {
189895
190916
  return { content: [{ type: "text", text }], details: undefined };
@@ -189908,22 +190929,41 @@ function createCtxExpandTool(deps) {
189908
190929
  description: CTX_EXPAND_DESCRIPTION,
189909
190930
  parameters: ParamsSchema,
189910
190931
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
189911
- if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
189912
- return err("Error: start and end must be positive integers with start <= end.");
189913
- }
189914
190932
  const sessionId = ctx.sessionManager.getSessionId();
189915
190933
  if (!sessionId) {
189916
190934
  return err("Error: no active Pi session.");
189917
190935
  }
189918
- const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
189919
- if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
189920
- 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.`);
189921
- }
189922
- const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
189923
190936
  const unregister = setRawMessageProvider(sessionId, {
189924
190937
  readMessages: () => readPiSessionMessages(ctx)
189925
190938
  });
189926
190939
  try {
190940
+ if (typeof params.message === "number" && params.message >= 1) {
190941
+ return ok(renderMessageByOrdinal(sessionId, params.message));
190942
+ }
190943
+ if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
190944
+ return err("Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).");
190945
+ }
190946
+ const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
190947
+ if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
190948
+ 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.`);
190949
+ }
190950
+ const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
190951
+ if (params.verbose === true) {
190952
+ const v = renderVerboseRange(sessionId, params.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
190953
+ if (!v.text) {
190954
+ return ok(`No messages found in range ${params.start}-${effectiveEnd}. The range may be outside this session's history.`);
190955
+ }
190956
+ const out = [
190957
+ `Messages ${params.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
190958
+ "",
190959
+ v.text
190960
+ ];
190961
+ if (v.truncated) {
190962
+ 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.`);
190963
+ }
190964
+ return ok(out.join(`
190965
+ `));
190966
+ }
189927
190967
  const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, params.start, effectiveEnd + 1);
189928
190968
  if (!chunk.text || chunk.messageCount === 0) {
189929
190969
  return ok(`No messages found in range ${params.start}-${params.end}. The range may be outside this session's history.`);
@@ -190642,15 +191682,16 @@ function parseInteger(str) {
190642
191682
  }
190643
191683
 
190644
191684
  // ../plugin/src/tools/ctx-reduce/constants.ts
190645
- var CTX_REDUCE_DESCRIPTION = `Reduce context by dropping tagged content you no longer need.
190646
- Use §N§ identifiers visible in conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
191685
+ 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".
191686
+
191687
+ How it works:
191688
+ - 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.
191689
+ - The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
191690
+ - 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?".
190647
191691
 
190648
- CRITICAL RULES:
190649
- - NEVER blanket-drop large ranges (e.g., "1-50"). Always review what each tag contains first.
190650
- - Only drop tool outputs you have already processed and no longer need.
190651
- - Protected tags are accepted but deferred until they leave the last protected range.
190652
- - Keep recent context — only reduce OLD content that is no longer relevant to current work.
190653
- - Dropped content is gone forever.`;
191692
+ 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.
191693
+ Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
191694
+ Never blanket-mark large ranges (e.g. "1-50") review what each tag holds first.`;
190654
191695
 
190655
191696
  // src/tools/ctx-reduce.ts
190656
191697
  var ParamsSchema4 = exports_typebox.Object({
@@ -190877,7 +191918,7 @@ function createCtxSearchTool(deps) {
190877
191918
  memoryEnabled,
190878
191919
  embeddingEnabled,
190879
191920
  embedQuery: async (text, signal) => {
190880
- const result = await embedTextForProject(projectIdentity, text, signal);
191921
+ const result = await embedTextForProject(projectIdentity, text, signal, "query");
190881
191922
  return result?.vector ?? null;
190882
191923
  },
190883
191924
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -191105,7 +192146,7 @@ function resolveSidekickFromConfig(config2) {
191105
192146
  systemPrompt: sidekick.system_prompt,
191106
192147
  timeoutMs: sidekick.timeout_ms,
191107
192148
  thinking_level: sidekick.thinking_level,
191108
- fallbackModels: resolveFallbackChain("sidekick", sidekick.fallback_models)
192149
+ fallbackModels: resolveFallbackChain(sidekick.fallback_models)
191109
192150
  };
191110
192151
  }
191111
192152
  function resolveHistorianFromConfig(config2) {
@@ -191117,7 +192158,7 @@ function resolveHistorianFromConfig(config2) {
191117
192158
  return;
191118
192159
  const historianContextLimit = resolveHistorianContextLimit(model);
191119
192160
  const historianChunkTokens = deriveHistorianChunkTokens(historianContextLimit);
191120
- const fallbackModels = resolveFallbackChain("historian", historian?.fallback_models);
192161
+ const fallbackModels = resolveFallbackChain(historian?.fallback_models);
191121
192162
  return {
191122
192163
  runner: new PiSubagentRunner,
191123
192164
  model,
@@ -191251,7 +192292,21 @@ async function src_default2(pi) {
191251
192292
  },
191252
192293
  historian: hist,
191253
192294
  autoSearch: auto,
191254
- resolveForProject: resolveContextOptionsForProject
192295
+ resolveForProject: resolveContextOptionsForProject,
192296
+ maybeAutoEmbedSession: (sessionId, dir, identity) => {
192297
+ maybeAutoEmbedPiSession({
192298
+ db: database,
192299
+ projectDir: dir,
192300
+ projectIdentity: identity,
192301
+ memoryEnabled: cfg.memory.enabled
192302
+ }, sessionId, dir, identity, (text) => {
192303
+ pi.sendMessage({
192304
+ customType: "ctx-status",
192305
+ content: text,
192306
+ display: true
192307
+ }, { triggerTurn: false });
192308
+ });
192309
+ }
191255
192310
  });
191256
192311
  function resolveContextOptionsForProject(dir) {
191257
192312
  const cached2 = contextOptionsByDir.get(dir);
@@ -191333,14 +192388,14 @@ async function src_default2(pi) {
191333
192388
  onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
191334
192389
  });
191335
192390
  info("registered /ctx-dream");
191336
- registerCtxEmbedHistoryCommand(pi, {
192391
+ registerCtxEmbedCommand(pi, {
191337
192392
  db,
191338
192393
  projectDir,
191339
192394
  projectIdentity,
191340
192395
  memoryEnabled: config2.memory.enabled,
191341
192396
  resolveProject: resolveCurrentProject
191342
192397
  });
191343
- info("registered /ctx-embed-history");
192398
+ info("registered /ctx-embed");
191344
192399
  const dreamerConfig = resolveDreamerFromConfig(config2);
191345
192400
  if (dreamerConfig) {
191346
192401
  registerPiDreamerProject({