@cortexkit/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
  };
@@ -166490,6 +166692,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
166490
166692
  return lines.join(`
166491
166693
  `);
166492
166694
  }
166695
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
166696
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
166697
+ if (!row)
166698
+ return "";
166699
+ const title = typeof row.title === "string" ? row.title.trim() : "";
166700
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
166701
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
166702
+ return [title, body].filter((s) => s.length > 0).join(`
166703
+ `);
166704
+ }
166493
166705
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
166494
166706
  const lines = [];
166495
166707
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -166733,6 +166945,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
166733
166945
  )`).get(projectPath, sessionId, projectPath, modelId);
166734
166946
  return typeof row?.n === "number" ? row.n : 0;
166735
166947
  }
166948
+ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
166949
+ const row = db.prepare(`SELECT
166950
+ COUNT(*) AS total,
166951
+ SUM(CASE WHEN EXISTS (
166952
+ SELECT 1 FROM compartment_chunk_embeddings e
166953
+ WHERE e.compartment_id = c.id
166954
+ AND e.project_path = ?
166955
+ AND e.model_id = ?
166956
+ ) THEN 1 ELSE 0 END) AS embedded
166957
+ FROM compartments c
166958
+ JOIN session_projects sp
166959
+ ON sp.session_id = c.session_id
166960
+ AND sp.harness = c.harness
166961
+ AND sp.project_path = ?
166962
+ WHERE c.session_id = ?
166963
+ AND c.start_message IS NOT NULL
166964
+ AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
166965
+ return {
166966
+ total: typeof row?.total === "number" ? row.total : 0,
166967
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
166968
+ };
166969
+ }
166736
166970
 
166737
166971
  // ../plugin/src/features/magic-context/memory/cosine-similarity.ts
166738
166972
  function cosineSimilarity(a, b) {
@@ -166875,6 +167109,19 @@ async function withQuietConsole(fn) {
166875
167109
  console.error = origError;
166876
167110
  }
166877
167111
  }
167112
+ var nativeRuntimeMissing = false;
167113
+ function isNativeRuntimeMissingError(error51) {
167114
+ const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
167115
+ const lower = message.toLowerCase();
167116
+ const code = error51?.code;
167117
+ const name2 = error51?.name;
167118
+ if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
167119
+ return true;
167120
+ }
167121
+ if (!lower.includes("onnxruntime-node"))
167122
+ return false;
167123
+ return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
167124
+ }
166878
167125
  function isTransientLoadError(error51) {
166879
167126
  const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
166880
167127
  if (!message)
@@ -166939,6 +167186,9 @@ class LocalEmbeddingProvider {
166939
167186
  if (this.pipeline) {
166940
167187
  return true;
166941
167188
  }
167189
+ if (nativeRuntimeMissing) {
167190
+ return false;
167191
+ }
166942
167192
  if (this.initPromise) {
166943
167193
  await this.initPromise;
166944
167194
  return this.pipeline !== null;
@@ -167005,7 +167255,12 @@ class LocalEmbeddingProvider {
167005
167255
  await releaseLock();
167006
167256
  }
167007
167257
  } catch (error51) {
167008
- log("[magic-context] embedding model failed to load:", error51);
167258
+ if (isNativeRuntimeMissingError(error51)) {
167259
+ nativeRuntimeMissing = true;
167260
+ 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 @cortexkit/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
167261
+ } else {
167262
+ log("[magic-context] embedding model failed to load:", error51);
167263
+ }
167009
167264
  this.pipeline = null;
167010
167265
  } finally {
167011
167266
  this.initPromise = null;
@@ -167031,7 +167286,7 @@ class LocalEmbeddingProvider {
167031
167286
  waiter();
167032
167287
  }
167033
167288
  }
167034
- async embed(text, signal) {
167289
+ async embed(text, signal, _purpose) {
167035
167290
  if (signal?.aborted)
167036
167291
  return null;
167037
167292
  if (this.disposing)
@@ -167057,7 +167312,7 @@ class LocalEmbeddingProvider {
167057
167312
  this.finishInFlight();
167058
167313
  }
167059
167314
  }
167060
- async embedBatch(texts, signal) {
167315
+ async embedBatch(texts, signal, _purpose) {
167061
167316
  if (texts.length === 0) {
167062
167317
  return [];
167063
167318
  }
@@ -167196,6 +167451,7 @@ class OpenAICompatibleEmbeddingProvider {
167196
167451
  model;
167197
167452
  apiKey;
167198
167453
  inputType;
167454
+ queryInputType;
167199
167455
  truncate;
167200
167456
  initialized = false;
167201
167457
  failureTimes = [];
@@ -167208,6 +167464,7 @@ class OpenAICompatibleEmbeddingProvider {
167208
167464
  this.model = options.model?.trim() ?? "";
167209
167465
  this.apiKey = options.apiKey?.trim() ?? "";
167210
167466
  this.inputType = options.inputType?.trim() ?? "";
167467
+ this.queryInputType = options.queryInputType?.trim() ?? "";
167211
167468
  this.truncate = options.truncate?.trim() ?? "";
167212
167469
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
167213
167470
  this.modelId = getEmbeddingProviderIdentity({
@@ -167235,11 +167492,17 @@ class OpenAICompatibleEmbeddingProvider {
167235
167492
  this.initialized = true;
167236
167493
  return true;
167237
167494
  }
167238
- async embed(text, signal) {
167239
- const [embedding] = await this.embedBatch([text], signal);
167495
+ resolveInputTypeForPurpose(purpose = "passage") {
167496
+ if (purpose === "query") {
167497
+ return this.queryInputType || this.inputType;
167498
+ }
167499
+ return this.inputType;
167500
+ }
167501
+ async embed(text, signal, purpose) {
167502
+ const [embedding] = await this.embedBatch([text], signal, purpose);
167240
167503
  return embedding ?? null;
167241
167504
  }
167242
- async embedBatch(texts, signal) {
167505
+ async embedBatch(texts, signal, purpose) {
167243
167506
  if (texts.length === 0) {
167244
167507
  return [];
167245
167508
  }
@@ -167265,6 +167528,7 @@ class OpenAICompatibleEmbeddingProvider {
167265
167528
  if (signal) {
167266
167529
  signal.addEventListener("abort", onOuterAbort, { once: true });
167267
167530
  }
167531
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
167268
167532
  const response = await fetch(`${this.endpoint}/embeddings`, {
167269
167533
  method: "POST",
167270
167534
  headers: {
@@ -167274,7 +167538,7 @@ class OpenAICompatibleEmbeddingProvider {
167274
167538
  body: JSON.stringify({
167275
167539
  model: this.model,
167276
167540
  input: texts,
167277
- ...this.inputType ? { input_type: this.inputType } : {},
167541
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
167278
167542
  ...this.truncate ? { truncate: this.truncate } : {}
167279
167543
  }),
167280
167544
  redirect: "error",
@@ -167519,6 +167783,118 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
167519
167783
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
167520
167784
  }
167521
167785
 
167786
+ // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
167787
+ init_logger();
167788
+ var insertStatements = new WeakMap;
167789
+ var existingShasStatements = new WeakMap;
167790
+ var projectCountStatements = new WeakMap;
167791
+ var evictStatements = new WeakMap;
167792
+ var evictOverflowStatements = new WeakMap;
167793
+ var latestCommitTimeStatements = new WeakMap;
167794
+ function getInsertStatement(db) {
167795
+ let stmt = insertStatements.get(db);
167796
+ if (!stmt) {
167797
+ stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
167798
+ VALUES (?, ?, ?, ?, ?, ?, ?)
167799
+ ON CONFLICT(sha) DO UPDATE SET
167800
+ project_path = excluded.project_path,
167801
+ short_sha = excluded.short_sha,
167802
+ message = excluded.message,
167803
+ author = excluded.author,
167804
+ committed_at = excluded.committed_at,
167805
+ indexed_at = excluded.indexed_at
167806
+ WHERE git_commits.message != excluded.message`);
167807
+ insertStatements.set(db, stmt);
167808
+ }
167809
+ return stmt;
167810
+ }
167811
+ function getExistingShasStatement(db) {
167812
+ let stmt = existingShasStatements.get(db);
167813
+ if (!stmt) {
167814
+ stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
167815
+ existingShasStatements.set(db, stmt);
167816
+ }
167817
+ return stmt;
167818
+ }
167819
+ function getProjectCountStatement(db) {
167820
+ let stmt = projectCountStatements.get(db);
167821
+ if (!stmt) {
167822
+ stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
167823
+ projectCountStatements.set(db, stmt);
167824
+ }
167825
+ return stmt;
167826
+ }
167827
+ function getLatestCommitTimeStatement(db) {
167828
+ let stmt = latestCommitTimeStatements.get(db);
167829
+ if (!stmt) {
167830
+ stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
167831
+ latestCommitTimeStatements.set(db, stmt);
167832
+ }
167833
+ return stmt;
167834
+ }
167835
+ function getEvictOverflowStatement(db) {
167836
+ let stmt = evictOverflowStatements.get(db);
167837
+ if (!stmt) {
167838
+ stmt = db.prepare(`DELETE FROM git_commits
167839
+ WHERE rowid IN (
167840
+ SELECT rowid FROM git_commits
167841
+ WHERE project_path = ?
167842
+ ORDER BY committed_at DESC, sha DESC
167843
+ LIMIT -1 OFFSET ?
167844
+ )`);
167845
+ evictOverflowStatements.set(db, stmt);
167846
+ }
167847
+ return stmt;
167848
+ }
167849
+ function upsertCommits(db, projectPath, commits) {
167850
+ if (commits.length === 0)
167851
+ return { inserted: 0, updated: 0 };
167852
+ const existing = new Set;
167853
+ for (const row of getExistingShasStatement(db).all(projectPath)) {
167854
+ existing.add(row.sha);
167855
+ }
167856
+ let inserted = 0;
167857
+ let updated = 0;
167858
+ const now = Date.now();
167859
+ const insertStmt = getInsertStatement(db);
167860
+ db.transaction(() => {
167861
+ for (const commit of commits) {
167862
+ const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
167863
+ if (result.changes > 0) {
167864
+ if (existing.has(commit.sha)) {
167865
+ updated++;
167866
+ } else {
167867
+ inserted++;
167868
+ existing.add(commit.sha);
167869
+ }
167870
+ }
167871
+ }
167872
+ })();
167873
+ return { inserted, updated };
167874
+ }
167875
+ function getCommitCount(db, projectPath) {
167876
+ const row = getProjectCountStatement(db).get(projectPath);
167877
+ return row?.count ?? 0;
167878
+ }
167879
+ function getLatestIndexedCommitTimeMs(db, projectPath) {
167880
+ const row = getLatestCommitTimeStatement(db).get(projectPath);
167881
+ return row?.latest ?? null;
167882
+ }
167883
+ function enforceProjectCap(db, projectPath, maxCommits) {
167884
+ if (maxCommits <= 0)
167885
+ return 0;
167886
+ const count = getCommitCount(db, projectPath);
167887
+ if (count <= maxCommits)
167888
+ return 0;
167889
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
167890
+ const after = getCommitCount(db, projectPath);
167891
+ const evicted = Math.max(0, count - after);
167892
+ if (evicted > 0) {
167893
+ log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
167894
+ }
167895
+ return evicted;
167896
+ }
167897
+
167522
167898
  // ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
167523
167899
  var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
167524
167900
  var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
@@ -167713,8 +168089,12 @@ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
167713
168089
  var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
167714
168090
  var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
167715
168091
  var CHUNK_DRAIN_BATCH_SIZE = 8;
167716
- var MAX_WINDOWS_PER_EMBED_CALL = 16;
168092
+ var MAX_WINDOWS_PER_EMBED_CALL = 2;
167717
168093
  var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
168094
+ var EMBED_SLICE_RETRY_ATTEMPTS = 3;
168095
+ var EMBED_SLICE_RETRY_BASE_MS = 250;
168096
+ var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
168097
+ var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
167718
168098
  var projectRegistrations = new Map;
167719
168099
  var loadUnembeddedMemoriesStatements = new WeakMap;
167720
168100
  var globalRegistrationGeneration = 0;
@@ -167732,6 +168112,7 @@ function resolveEmbeddingConfig(config2) {
167732
168112
  if (config2.provider === "openai-compatible") {
167733
168113
  const apiKey = config2.api_key?.trim();
167734
168114
  const inputType = config2.input_type?.trim();
168115
+ const queryInputType = config2.query_input_type?.trim();
167735
168116
  const truncate = config2.truncate?.trim();
167736
168117
  return {
167737
168118
  provider: "openai-compatible",
@@ -167739,6 +168120,7 @@ function resolveEmbeddingConfig(config2) {
167739
168120
  endpoint: config2.endpoint.trim(),
167740
168121
  ...apiKey ? { api_key: apiKey } : {},
167741
168122
  ...inputType ? { input_type: inputType } : {},
168123
+ ...queryInputType ? { query_input_type: queryInputType } : {},
167742
168124
  ...truncate ? { truncate } : {},
167743
168125
  ...config2.max_input_tokens ? {
167744
168126
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -167760,6 +168142,7 @@ function createProvider(config2) {
167760
168142
  model: config2.model,
167761
168143
  apiKey: config2.api_key,
167762
168144
  inputType: config2.input_type,
168145
+ queryInputType: config2.query_input_type,
167763
168146
  truncate: config2.truncate,
167764
168147
  maxInputTokens: config2.max_input_tokens
167765
168148
  });
@@ -167814,7 +168197,9 @@ function snapshotFor(registration) {
167814
168197
  enabled,
167815
168198
  gitCommitEnabled,
167816
168199
  modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
167817
- chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
168200
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
168201
+ model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
168202
+ provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
167818
168203
  };
167819
168204
  }
167820
168205
  function disposeProvider(provider) {
@@ -167940,7 +168325,7 @@ function getOrCreateProjectProvider(registration) {
167940
168325
  registration.provider = provider;
167941
168326
  return provider;
167942
168327
  }
167943
- async function embedTextForProject(projectIdentity, text, signal) {
168328
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
167944
168329
  const registration = projectRegistrations.get(projectIdentity);
167945
168330
  if (!registration)
167946
168331
  return null;
@@ -167949,7 +168334,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
167949
168334
  const provider = getOrCreateProjectProvider(registration);
167950
168335
  if (!provider)
167951
168336
  return null;
167952
- const vector = await provider.embed(text, signal);
168337
+ const vector = await provider.embed(text, signal, purpose);
167953
168338
  if (!vector)
167954
168339
  return null;
167955
168340
  const current = projectRegistrations.get(projectIdentity);
@@ -167958,7 +168343,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
167958
168343
  }
167959
168344
  return { vector, modelId, generation };
167960
168345
  }
167961
- async function embedBatchForProject(projectIdentity, texts, signal) {
168346
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
167962
168347
  if (texts.length === 0) {
167963
168348
  const registration2 = projectRegistrations.get(projectIdentity);
167964
168349
  if (!registration2 || registration2.observationMode)
@@ -167974,7 +168359,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
167974
168359
  const provider = getOrCreateProjectProvider(registration);
167975
168360
  if (!provider)
167976
168361
  return null;
167977
- const vectors = await provider.embedBatch(texts, signal);
168362
+ const vectors = await provider.embedBatch(texts, signal, purpose);
167978
168363
  const current = projectRegistrations.get(projectIdentity);
167979
168364
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
167980
168365
  return null;
@@ -168025,12 +168410,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
168025
168410
  }
168026
168411
  async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
168027
168412
  const noWork = [];
168413
+ const failed = [];
168028
168414
  if (candidates.length === 0)
168029
- return { embedded: 0, noWork };
168415
+ return { embedded: 0, noWork, failed };
168030
168416
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
168031
168417
  const prepared = [];
168032
168418
  for (const candidate of candidates) {
168033
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
168419
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
168034
168420
  if (canonicalText.length === 0) {
168035
168421
  noWork.push(candidate.id);
168036
168422
  continue;
@@ -168043,7 +168429,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
168043
168429
  prepared.push({ candidate, windows });
168044
168430
  }
168045
168431
  if (prepared.length === 0)
168046
- return { embedded: 0, noWork };
168432
+ return { embedded: 0, noWork, failed };
168047
168433
  let embedded = 0;
168048
168434
  let i = 0;
168049
168435
  while (i < prepared.length) {
@@ -168060,35 +168446,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
168060
168446
  const texts = [];
168061
168447
  for (const item of slice)
168062
168448
  texts.push(...item.windows.map((w) => w.text));
168063
- try {
168064
- const result = await embedBatchForProject(projectIdentity, texts, signal);
168065
- if (!result)
168066
- continue;
168449
+ const persistedIds = new Set;
168450
+ for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
168067
168451
  if (signal?.aborted)
168068
168452
  break;
168069
- let offset = 0;
168070
- for (const item of slice) {
168071
- const vectors = result.vectors.slice(offset, offset + item.windows.length);
168072
- offset += item.windows.length;
168073
- if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
168074
- continue;
168453
+ let result = null;
168454
+ const attemptStart = Date.now();
168455
+ try {
168456
+ result = await embedBatchForProject(projectIdentity, texts, signal);
168457
+ } catch (error51) {
168458
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
168459
+ }
168460
+ if (signal?.aborted)
168461
+ break;
168462
+ if (result) {
168463
+ let offset = 0;
168464
+ for (const item of slice) {
168465
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
168466
+ offset += item.windows.length;
168467
+ if (persistedIds.has(item.candidate.id))
168468
+ continue;
168469
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
168470
+ continue;
168471
+ }
168472
+ const rows = item.windows.map((window, index) => ({
168473
+ compartmentId: item.candidate.id,
168474
+ sessionId: item.candidate.sessionId,
168475
+ projectPath: projectIdentity,
168476
+ window,
168477
+ modelId,
168478
+ vector: vectors[index]
168479
+ }));
168480
+ replaceCompartmentChunkEmbeddings(db, rows);
168481
+ persistedIds.add(item.candidate.id);
168075
168482
  }
168076
- const rows = item.windows.map((window, index) => ({
168077
- compartmentId: item.candidate.id,
168078
- sessionId: item.candidate.sessionId,
168079
- projectPath: projectIdentity,
168080
- window,
168081
- modelId,
168082
- vector: vectors[index]
168083
- }));
168084
- replaceCompartmentChunkEmbeddings(db, rows);
168085
- embedded += 1;
168086
168483
  }
168087
- } catch (error51) {
168088
- log("[magic-context] failed to proactively embed compartment chunks:", error51);
168484
+ if (persistedIds.size === slice.length)
168485
+ break;
168486
+ if (persistedIds.size > 0)
168487
+ break;
168488
+ if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
168489
+ break;
168490
+ if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
168491
+ await new Promise((resolve4) => setTimeout(resolve4, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
168492
+ }
168493
+ }
168494
+ embedded += persistedIds.size;
168495
+ if (!signal?.aborted) {
168496
+ for (const item of slice) {
168497
+ if (!persistedIds.has(item.candidate.id))
168498
+ failed.push(item.candidate.id);
168499
+ }
168089
168500
  }
168090
168501
  }
168091
- return { embedded, noWork };
168502
+ return { embedded, noWork, failed };
168092
168503
  }
168093
168504
  async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
168094
168505
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
@@ -168111,9 +168522,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
168111
168522
  renewal.unref?.();
168112
168523
  const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
168113
168524
  const skipIds = [];
168525
+ const failedIds = [];
168114
168526
  let embedded = 0;
168115
168527
  let aborted2 = false;
168116
- let providerStalled = false;
168528
+ let providerDown = false;
168529
+ let consecutiveFailedBatches = 0;
168117
168530
  try {
168118
168531
  options?.onProgress?.({ embedded, total });
168119
168532
  for (;; ) {
@@ -168121,15 +168534,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
168121
168534
  aborted2 = true;
168122
168535
  break;
168123
168536
  }
168124
- const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
168537
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
168125
168538
  if (candidates.length === 0)
168126
168539
  break;
168127
- const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
168540
+ const {
168541
+ embedded: n,
168542
+ noWork,
168543
+ failed
168544
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
168128
168545
  for (const id of noWork)
168129
168546
  skipIds.push(id);
168547
+ for (const id of failed)
168548
+ failedIds.push(id);
168130
168549
  if (n === 0 && noWork.length === 0) {
168131
- providerStalled = true;
168132
- break;
168550
+ consecutiveFailedBatches += 1;
168551
+ if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
168552
+ providerDown = true;
168553
+ break;
168554
+ }
168555
+ } else {
168556
+ consecutiveFailedBatches = 0;
168133
168557
  }
168134
168558
  embedded += n;
168135
168559
  options?.onProgress?.({ embedded: Math.min(embedded, total), total });
@@ -168137,16 +168561,50 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
168137
168561
  }
168138
168562
  } finally {
168139
168563
  clearInterval(renewal);
168140
- releaseGitSweepLease(db, projectIdentity, holderId);
168564
+ try {
168565
+ releaseGitSweepLease(db, projectIdentity, holderId);
168566
+ } catch (error51) {
168567
+ log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
168568
+ }
168141
168569
  }
168142
168570
  if (aborted2)
168143
- return { status: "aborted", embedded, total };
168144
- if (providerStalled) {
168571
+ return { status: "aborted", embedded, total, failed: failedIds.length };
168572
+ if (providerDown || failedIds.length > 0) {
168145
168573
  const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
168146
- if (remaining > 0)
168147
- return { status: "stalled", embedded, total, remaining };
168574
+ if (remaining > 0) {
168575
+ return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
168576
+ }
168148
168577
  }
168149
- return { status: "done", embedded, total };
168578
+ return { status: "done", embedded, total, failed: failedIds.length };
168579
+ }
168580
+ function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
168581
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
168582
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
168583
+ return {
168584
+ enabled: false,
168585
+ model: snapshot?.model ?? "off",
168586
+ provider: snapshot?.provider ?? "off",
168587
+ session: { embedded: 0, total: 0 },
168588
+ memories: { embedded: 0, total: 0 },
168589
+ commits: { embedded: 0, total: 0, gitEnabled: false }
168590
+ };
168591
+ }
168592
+ const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
168593
+ const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
168594
+ const gitEnabled = snapshot.gitCommitEnabled;
168595
+ const commits = gitEnabled ? {
168596
+ embedded: countEmbeddedCommits(db, projectIdentity),
168597
+ total: getCommitCount(db, projectIdentity),
168598
+ gitEnabled: true
168599
+ } : { embedded: 0, total: 0, gitEnabled: false };
168600
+ return {
168601
+ enabled: true,
168602
+ model: snapshot.model,
168603
+ provider: snapshot.provider,
168604
+ session,
168605
+ memories,
168606
+ commits
168607
+ };
168150
168608
  }
168151
168609
 
168152
168610
  // ../plugin/src/features/magic-context/memory/embedding.ts
@@ -168167,6 +168625,7 @@ function createProvider2(config2) {
168167
168625
  model: config2.model,
168168
168626
  apiKey: config2.api_key,
168169
168627
  inputType: config2.input_type,
168628
+ queryInputType: config2.query_input_type,
168170
168629
  truncate: config2.truncate,
168171
168630
  maxInputTokens: config2.max_input_tokens
168172
168631
  });
@@ -168195,118 +168654,6 @@ async function embedText(text, signal) {
168195
168654
  }
168196
168655
  var SWEEP_MAX_WALL_CLOCK_MS2 = 10 * 60 * 1000;
168197
168656
 
168198
- // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
168199
- init_logger();
168200
- var insertStatements = new WeakMap;
168201
- var existingShasStatements = new WeakMap;
168202
- var projectCountStatements = new WeakMap;
168203
- var evictStatements = new WeakMap;
168204
- var evictOverflowStatements = new WeakMap;
168205
- var latestCommitTimeStatements = new WeakMap;
168206
- function getInsertStatement(db) {
168207
- let stmt = insertStatements.get(db);
168208
- if (!stmt) {
168209
- stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
168210
- VALUES (?, ?, ?, ?, ?, ?, ?)
168211
- ON CONFLICT(sha) DO UPDATE SET
168212
- project_path = excluded.project_path,
168213
- short_sha = excluded.short_sha,
168214
- message = excluded.message,
168215
- author = excluded.author,
168216
- committed_at = excluded.committed_at,
168217
- indexed_at = excluded.indexed_at
168218
- WHERE git_commits.message != excluded.message`);
168219
- insertStatements.set(db, stmt);
168220
- }
168221
- return stmt;
168222
- }
168223
- function getExistingShasStatement(db) {
168224
- let stmt = existingShasStatements.get(db);
168225
- if (!stmt) {
168226
- stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
168227
- existingShasStatements.set(db, stmt);
168228
- }
168229
- return stmt;
168230
- }
168231
- function getProjectCountStatement(db) {
168232
- let stmt = projectCountStatements.get(db);
168233
- if (!stmt) {
168234
- stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
168235
- projectCountStatements.set(db, stmt);
168236
- }
168237
- return stmt;
168238
- }
168239
- function getLatestCommitTimeStatement(db) {
168240
- let stmt = latestCommitTimeStatements.get(db);
168241
- if (!stmt) {
168242
- stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
168243
- latestCommitTimeStatements.set(db, stmt);
168244
- }
168245
- return stmt;
168246
- }
168247
- function getEvictOverflowStatement(db) {
168248
- let stmt = evictOverflowStatements.get(db);
168249
- if (!stmt) {
168250
- stmt = db.prepare(`DELETE FROM git_commits
168251
- WHERE rowid IN (
168252
- SELECT rowid FROM git_commits
168253
- WHERE project_path = ?
168254
- ORDER BY committed_at DESC, sha DESC
168255
- LIMIT -1 OFFSET ?
168256
- )`);
168257
- evictOverflowStatements.set(db, stmt);
168258
- }
168259
- return stmt;
168260
- }
168261
- function upsertCommits(db, projectPath, commits) {
168262
- if (commits.length === 0)
168263
- return { inserted: 0, updated: 0 };
168264
- const existing = new Set;
168265
- for (const row of getExistingShasStatement(db).all(projectPath)) {
168266
- existing.add(row.sha);
168267
- }
168268
- let inserted = 0;
168269
- let updated = 0;
168270
- const now = Date.now();
168271
- const insertStmt = getInsertStatement(db);
168272
- db.transaction(() => {
168273
- for (const commit of commits) {
168274
- const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
168275
- if (result.changes > 0) {
168276
- if (existing.has(commit.sha)) {
168277
- updated++;
168278
- } else {
168279
- inserted++;
168280
- existing.add(commit.sha);
168281
- }
168282
- }
168283
- }
168284
- })();
168285
- return { inserted, updated };
168286
- }
168287
- function getCommitCount(db, projectPath) {
168288
- const row = getProjectCountStatement(db).get(projectPath);
168289
- return row?.count ?? 0;
168290
- }
168291
- function getLatestIndexedCommitTimeMs(db, projectPath) {
168292
- const row = getLatestCommitTimeStatement(db).get(projectPath);
168293
- return row?.latest ?? null;
168294
- }
168295
- function enforceProjectCap(db, projectPath, maxCommits) {
168296
- if (maxCommits <= 0)
168297
- return 0;
168298
- const count = getCommitCount(db, projectPath);
168299
- if (count <= maxCommits)
168300
- return 0;
168301
- getEvictOverflowStatement(db).run(projectPath, maxCommits);
168302
- const after = getCommitCount(db, projectPath);
168303
- const evicted = Math.max(0, count - after);
168304
- if (evicted > 0) {
168305
- log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
168306
- }
168307
- return evicted;
168308
- }
168309
-
168310
168657
  // ../plugin/src/features/magic-context/git-commits/indexer.ts
168311
168658
  var MS_PER_DAY = 24 * 60 * 60 * 1000;
168312
168659
  var EMBED_BATCH_SIZE = 16;
@@ -168715,7 +169062,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
168715
169062
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
168716
169063
  projectIdentity: reg.projectIdentity,
168717
169064
  sessionDirectoryOverride: reg.directory,
168718
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
169065
+ fallbackModels: resolveFallbackChain(reg.dreamerConfig.fallback_models)
168719
169066
  });
168720
169067
  } catch (error51) {
168721
169068
  log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
@@ -169591,7 +169938,7 @@ async function awaitInFlightDreamers() {
169591
169938
  function createPiDreamerClient(opts) {
169592
169939
  const runner2 = piSubagentRunnerFactory();
169593
169940
  const model = opts.config.model;
169594
- const fallbackModels = resolveFallbackChain(DREAMER_AGENT, opts.config.fallback_models);
169941
+ const fallbackModels = resolveFallbackChain(opts.config.fallback_models);
169595
169942
  const session = {
169596
169943
  create: async (args) => {
169597
169944
  const sessionId = `magic-context-pi-dream-${++sessionCounter}`;
@@ -169836,87 +170183,229 @@ function registerCtxDreamCommand(pi, deps) {
169836
170183
  });
169837
170184
  }
169838
170185
 
169839
- // src/commands/ctx-embed-history.ts
169840
- function registerCtxEmbedHistoryCommand(pi, deps) {
169841
- pi.registerCommand("ctx-embed-history", {
169842
- description: "Embed all of this session's history compartments for semantic search, in one pass",
169843
- handler: async (_args, ctx) => {
170186
+ // ../plugin/src/hooks/magic-context/embed-session-state.ts
170187
+ var embedPauseBySession = new Set;
170188
+ var embedRunStateBySession = new Map;
170189
+ var autoEmbedAttemptedBySession = new Set;
170190
+
170191
+ // ../plugin/src/hooks/magic-context/format-embed-status.ts
170192
+ function formatEmbedStatusText(coverage, drain) {
170193
+ if (!coverage.enabled) {
170194
+ return "Embedding is off (no provider configured).";
170195
+ }
170196
+ const lines = [];
170197
+ lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
170198
+ lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
170199
+ lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
170200
+ if (coverage.commits.gitEnabled) {
170201
+ lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
170202
+ } else {
170203
+ lines.push("Git commits: 0 / 0 (git indexing off)");
170204
+ }
170205
+ let drainLine = "Drain: idle";
170206
+ switch (drain.status) {
170207
+ case "running": {
170208
+ const e = drain.embedded ?? coverage.session.embedded;
170209
+ const t = drain.total ?? coverage.session.total;
170210
+ const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
170211
+ drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
170212
+ break;
170213
+ }
170214
+ case "paused": {
170215
+ const e = drain.embedded ?? coverage.session.embedded;
170216
+ const t = drain.total ?? coverage.session.total;
170217
+ drainLine = `Drain: paused ${e}/${t}`;
170218
+ break;
170219
+ }
170220
+ case "stopped":
170221
+ drainLine = "Drain: stopped (provider down)";
170222
+ break;
170223
+ default:
170224
+ drainLine = "Drain: idle";
170225
+ }
170226
+ lines.push(drainLine);
170227
+ return lines.join(`
170228
+ `);
170229
+ }
170230
+
170231
+ // src/commands/ctx-embed.ts
170232
+ function clearPiEmbedSessionState(sessionId) {
170233
+ embedPauseBySession.delete(sessionId);
170234
+ const ctrl = embedRunStateBySession.get(sessionId);
170235
+ if (ctrl) {
170236
+ ctrl.abort();
170237
+ embedRunStateBySession.delete(sessionId);
170238
+ }
170239
+ autoEmbedAttemptedBySession.delete(sessionId);
170240
+ }
170241
+ async function runEmbedDrain(db, projectIdentity, sessionId) {
170242
+ const activeCtrl = embedRunStateBySession.get(sessionId);
170243
+ if (activeCtrl && !activeCtrl.signal.aborted) {
170244
+ return {
170245
+ text: `## /ctx-embed
170246
+
170247
+ Embedding is already running for this session.`,
170248
+ level: "info"
170249
+ };
170250
+ }
170251
+ embedPauseBySession.delete(sessionId);
170252
+ const prior = embedRunStateBySession.get(sessionId);
170253
+ if (prior)
170254
+ prior.abort();
170255
+ const controller = new AbortController;
170256
+ embedRunStateBySession.set(sessionId, controller);
170257
+ let outcome;
170258
+ try {
170259
+ outcome = await embedSessionCompartmentChunks(db, projectIdentity, sessionId, {
170260
+ signal: controller.signal
170261
+ });
170262
+ } finally {
170263
+ if (embedRunStateBySession.get(sessionId) === controller) {
170264
+ embedRunStateBySession.delete(sessionId);
170265
+ }
170266
+ }
170267
+ switch (outcome.status) {
170268
+ case "nothing":
170269
+ return {
170270
+ text: `## /ctx-embed
170271
+
170272
+ All of this session's history is already embedded.`,
170273
+ level: "info"
170274
+ };
170275
+ case "disabled":
170276
+ return {
170277
+ text: `## /ctx-embed
170278
+
170279
+ No embedding provider is configured, so there is nothing to embed.`,
170280
+ level: "info"
170281
+ };
170282
+ case "busy":
170283
+ return {
170284
+ text: `## /ctx-embed
170285
+
170286
+ Embedding is already running for this project. Try again shortly.`,
170287
+ level: "info"
170288
+ };
170289
+ case "aborted": {
170290
+ const cov = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
170291
+ return {
170292
+ text: `## /ctx-embed
170293
+
170294
+ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
170295
+ level: "info"
170296
+ };
170297
+ }
170298
+ case "stalled":
170299
+ return {
170300
+ text: `## /ctx-embed
170301
+
170302
+ 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.`,
170303
+ level: "info"
170304
+ };
170305
+ default:
170306
+ return {
170307
+ text: `## /ctx-embed
170308
+
170309
+ Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`,
170310
+ level: "success"
170311
+ };
170312
+ }
170313
+ }
170314
+ function registerCtxEmbedCommand(pi, deps) {
170315
+ pi.registerCommand("ctx-embed", {
170316
+ description: "Embedding status, or start/pause history compartment embedding (start | pause)",
170317
+ handler: async (args, ctx) => {
169844
170318
  const sessionId = resolveSessionId(ctx);
169845
170319
  if (!sessionId) {
169846
170320
  sendCtxStatusMessage(pi, {
169847
- title: "/ctx-embed-history",
169848
- text: `## /ctx-embed-history
170321
+ title: "/ctx-embed",
170322
+ text: `## /ctx-embed
169849
170323
 
169850
170324
  No active Pi session is available.`,
169851
170325
  level: "error"
169852
170326
  });
169853
170327
  return;
169854
170328
  }
170329
+ const project = deps.resolveProject?.(ctx) ?? {
170330
+ projectDir: deps.projectDir,
170331
+ projectIdentity: deps.projectIdentity
170332
+ };
170333
+ const sub = args.trim().toLowerCase();
170334
+ if (sub === "pause") {
170335
+ embedPauseBySession.add(sessionId);
170336
+ const ctrl = embedRunStateBySession.get(sessionId);
170337
+ if (ctrl)
170338
+ ctrl.abort();
170339
+ const cov = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
170340
+ sendCtxStatusMessage(pi, {
170341
+ title: "/ctx-embed",
170342
+ text: `## /ctx-embed
170343
+
170344
+ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
170345
+ level: "info"
170346
+ });
170347
+ return;
170348
+ }
169855
170349
  if (deps.memoryEnabled === false) {
169856
170350
  sendCtxStatusMessage(pi, {
169857
- title: "/ctx-embed-history",
169858
- text: `## /ctx-embed-history
170351
+ title: "/ctx-embed",
170352
+ text: `## /ctx-embed
169859
170353
 
169860
170354
  Memory is disabled for this project, so there is no semantic embedding to backfill.`,
169861
170355
  level: "info"
169862
170356
  });
169863
170357
  return;
169864
170358
  }
169865
- const project = deps.resolveProject?.(ctx) ?? {
169866
- projectDir: deps.projectDir,
169867
- projectIdentity: deps.projectIdentity
169868
- };
169869
170359
  await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
169870
- const outcome = await embedSessionCompartmentChunks(deps.db, project.projectIdentity, sessionId);
169871
- const { text, level } = (() => {
169872
- switch (outcome.status) {
169873
- case "nothing":
169874
- return {
169875
- text: `## /ctx-embed-history
169876
-
169877
- All of this session's history is already embedded.`,
169878
- level: "info"
169879
- };
169880
- case "disabled":
169881
- return {
169882
- text: `## /ctx-embed-history
169883
-
169884
- No embedding provider is configured, so there is nothing to embed.`,
169885
- level: "info"
169886
- };
169887
- case "busy":
169888
- return {
169889
- text: `## /ctx-embed-history
169890
-
169891
- Embedding is already running for this project — ${outcome.total} compartment${outcome.total === 1 ? "" : "s"} still pending. Try again shortly.`,
169892
- level: "info"
169893
- };
169894
- case "stalled":
169895
- return {
169896
- text: `## /ctx-embed-history
169897
-
169898
- 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.`,
169899
- level: "info"
169900
- };
169901
- default:
169902
- return {
169903
- text: `## /ctx-embed-history
170360
+ if (sub === "start") {
170361
+ const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId);
170362
+ sendCtxStatusMessage(pi, { title: "/ctx-embed", text, level });
170363
+ return;
170364
+ }
170365
+ if (sub !== "") {
170366
+ sendCtxStatusMessage(pi, {
170367
+ title: "/ctx-embed",
170368
+ text: "## /ctx-embed\n\nUsage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.",
170369
+ level: "info"
170370
+ });
170371
+ return;
170372
+ }
170373
+ const coverage = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
170374
+ const statusText = formatEmbedStatusText(coverage, { status: "idle" });
170375
+ sendCtxStatusMessage(pi, {
170376
+ title: "/ctx-embed",
170377
+ text: `## Embedding Status
169904
170378
 
169905
- Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`,
169906
- level: "success"
169907
- };
169908
- }
169909
- })();
169910
- sendCtxStatusMessage(pi, { title: "/ctx-embed-history", text, level }, {
169911
- sessionId,
169912
- projectIdentity: project.projectIdentity,
169913
- status: outcome.status,
169914
- embedded: outcome.embedded,
169915
- total: outcome.total
170379
+ ${statusText}`,
170380
+ level: "info"
169916
170381
  });
169917
170382
  }
169918
170383
  });
169919
170384
  }
170385
+ function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity, notify) {
170386
+ if (autoEmbedAttemptedBySession.has(sessionId))
170387
+ return;
170388
+ if (embedPauseBySession.has(sessionId))
170389
+ return;
170390
+ if (deps.memoryEnabled === false)
170391
+ return;
170392
+ autoEmbedAttemptedBySession.add(sessionId);
170393
+ (async () => {
170394
+ try {
170395
+ await new Promise((resolve5) => setTimeout(resolve5, 0));
170396
+ await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
170397
+ const coverage = getEmbeddingCoverageStatus(deps.db, projectIdentity, sessionId);
170398
+ if (!coverage.enabled)
170399
+ return;
170400
+ const remaining = coverage.session.total - coverage.session.embedded;
170401
+ if (remaining <= 0)
170402
+ return;
170403
+ notify(`Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`);
170404
+ const { text } = await runEmbedDrain(deps.db, projectIdentity, sessionId);
170405
+ notify(text.replace(/^## \/ctx-embed\n\n/, ""));
170406
+ } catch {}
170407
+ })();
170408
+ }
169920
170409
 
169921
170410
  // ../plugin/src/hooks/magic-context/execute-flush.ts
169922
170411
  init_logger();
@@ -170010,10 +170499,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
170010
170499
  }
170011
170500
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
170012
170501
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
170502
+ 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";
170013
170503
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
170014
- var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
170015
170504
  var probeDataVersionStatements = new WeakMap;
170016
- var probeTotalChangesStatements = new WeakMap;
170017
170505
  function getProbeDataVersionStatement(db) {
170018
170506
  let stmt = probeDataVersionStatements.get(db);
170019
170507
  if (!stmt) {
@@ -170022,14 +170510,6 @@ function getProbeDataVersionStatement(db) {
170022
170510
  }
170023
170511
  return stmt;
170024
170512
  }
170025
- function getProbeTotalChangesStatement(db) {
170026
- let stmt = probeTotalChangesStatements.get(db);
170027
- if (!stmt) {
170028
- stmt = db.prepare(PROBE_TOTAL_CHANGES_SQL);
170029
- probeTotalChangesStatements.set(db, stmt);
170030
- }
170031
- return stmt;
170032
- }
170033
170513
  function isAssignmentRow(row) {
170034
170514
  if (row === null || typeof row !== "object") {
170035
170515
  return false;
@@ -170222,20 +170702,18 @@ function createTagger() {
170222
170702
  }
170223
170703
  function probeSignature(db) {
170224
170704
  const dvRow = getProbeDataVersionStatement(db).get();
170225
- const tcRow = getProbeTotalChangesStatement(db).get();
170226
170705
  return {
170227
- dataVersion: dvRow?.data_version ?? 0,
170228
- totalChanges: tcRow?.tc ?? 0
170706
+ dataVersion: dvRow?.data_version ?? 0
170229
170707
  };
170230
170708
  }
170231
- function initFromDb(sessionId, db) {
170709
+ function initFromDb(sessionId, db, floor = 0) {
170232
170710
  const probe = probeSignature(db);
170233
170711
  const cached2 = loadSignatures.get(sessionId);
170234
- if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.totalChanges === probe.totalChanges) {
170712
+ if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.floor === floor) {
170235
170713
  return;
170236
170714
  }
170237
170715
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
170238
- const assignmentRows = db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId).filter(isAssignmentRow);
170716
+ const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
170239
170717
  const sessionAssignments = getSessionAssignments(sessionId);
170240
170718
  sessionAssignments.clear();
170241
170719
  let maxTagNumber = 0;
@@ -170256,7 +170734,7 @@ function createTagger() {
170256
170734
  loadSignatures.set(sessionId, {
170257
170735
  db,
170258
170736
  dataVersion: probe.dataVersion,
170259
- totalChanges: probe.totalChanges
170737
+ floor
170260
170738
  });
170261
170739
  }
170262
170740
  function cleanup(sessionId) {
@@ -170280,6 +170758,162 @@ function createTagger() {
170280
170758
  };
170281
170759
  }
170282
170760
 
170761
+ // ../plugin/src/features/magic-context/transform-decision-log.ts
170762
+ var canonicalReasons = new Set([
170763
+ "system_hash",
170764
+ "model_change",
170765
+ "project_memory_epoch",
170766
+ "ttl_idle",
170767
+ "explicit_flush",
170768
+ "max_mutation_id",
170769
+ "first_render",
170770
+ "pressure_refold",
170771
+ "upgrade_state",
170772
+ "cached_m1_missing"
170773
+ ]);
170774
+ var piReasonAliases = {
170775
+ project_memory_change: "project_memory_epoch",
170776
+ pending_mutations: "max_mutation_id",
170777
+ renderer_upgrade: "upgrade_state",
170778
+ cache_invalid: "cached_m1_missing",
170779
+ drift: "pressure_refold"
170780
+ };
170781
+ var sharedReasonAliases = {
170782
+ model_key: "model_change",
170783
+ pressure: "pressure_refold"
170784
+ };
170785
+ var pendingDecisionBySession = new Map;
170786
+ var pendingPiDecisionBySession = new Map;
170787
+ var lastBoundMessageIdBySession = new Map;
170788
+ var scheduledWriteTokensBySession = new Map;
170789
+ var writerOverrideForTests = null;
170790
+ function normalizeMaterializeReason(harness, reason, rematerialized) {
170791
+ const raw = typeof reason === "string" ? reason.trim() : "";
170792
+ if (raw.length > 0) {
170793
+ const alias = sharedReasonAliases[raw] ?? (harness === "pi" ? piReasonAliases[raw] : undefined) ?? undefined;
170794
+ if (alias)
170795
+ return alias;
170796
+ if (canonicalReasons.has(raw))
170797
+ return raw;
170798
+ return null;
170799
+ }
170800
+ return rematerialized ? "pressure_refold" : null;
170801
+ }
170802
+ function recordPendingPiTransformDecision(sessionId, decision, snapshotNewestAssistantEntryId) {
170803
+ if (!decision.bustedThisPass)
170804
+ return;
170805
+ pendingPiDecisionBySession.set(sessionId, {
170806
+ ...decision,
170807
+ snapshotNewestAssistantEntryId
170808
+ });
170809
+ }
170810
+ function findNewestPiAssistantEntryId(entries) {
170811
+ if (!Array.isArray(entries))
170812
+ return null;
170813
+ for (let i = entries.length - 1;i >= 0; i--) {
170814
+ const entry = entries[i];
170815
+ if (!entry || typeof entry !== "object")
170816
+ continue;
170817
+ const row = entry;
170818
+ if (row.type !== "message" || typeof row.id !== "string" || row.id.length === 0) {
170819
+ continue;
170820
+ }
170821
+ const message = row.message;
170822
+ if (message && typeof message === "object" && message.role === "assistant") {
170823
+ return row.id;
170824
+ }
170825
+ }
170826
+ return null;
170827
+ }
170828
+ function schedulePiTransformDecisionResolve(args) {
170829
+ const pending = pendingPiDecisionBySession.get(args.sessionId);
170830
+ if (!pending)
170831
+ return false;
170832
+ const targetMessageId = findNewestPiAssistantEntryIdAfter(args.branchEntries, pending.snapshotNewestAssistantEntryId);
170833
+ if (!targetMessageId)
170834
+ return false;
170835
+ const dbPath = getDatabasePath(args.db);
170836
+ if (!dbPath)
170837
+ return false;
170838
+ pendingPiDecisionBySession.delete(args.sessionId);
170839
+ const token = addScheduledWriteToken(args.sessionId);
170840
+ setTimeout(() => {
170841
+ try {
170842
+ if (!hasScheduledWriteToken(args.sessionId, token))
170843
+ return;
170844
+ writeTransformDecisionBestEffort(dbPath, {
170845
+ ...pending,
170846
+ sessionId: args.sessionId,
170847
+ harness: "pi",
170848
+ messageId: targetMessageId
170849
+ });
170850
+ } finally {
170851
+ deleteScheduledWriteToken(args.sessionId, token);
170852
+ }
170853
+ }, 0);
170854
+ return true;
170855
+ }
170856
+ function addScheduledWriteToken(sessionId) {
170857
+ const token = Symbol(sessionId);
170858
+ let tokens = scheduledWriteTokensBySession.get(sessionId);
170859
+ if (!tokens) {
170860
+ tokens = new Set;
170861
+ scheduledWriteTokensBySession.set(sessionId, tokens);
170862
+ }
170863
+ tokens.add(token);
170864
+ return token;
170865
+ }
170866
+ function hasScheduledWriteToken(sessionId, token) {
170867
+ return scheduledWriteTokensBySession.get(sessionId)?.has(token) === true;
170868
+ }
170869
+ function deleteScheduledWriteToken(sessionId, token) {
170870
+ const tokens = scheduledWriteTokensBySession.get(sessionId);
170871
+ if (!tokens)
170872
+ return;
170873
+ tokens.delete(token);
170874
+ if (tokens.size === 0)
170875
+ scheduledWriteTokensBySession.delete(sessionId);
170876
+ }
170877
+ function findNewestPiAssistantEntryIdAfter(entries, snapshotNewestAssistantEntryId) {
170878
+ if (!Array.isArray(entries))
170879
+ return null;
170880
+ for (let i = entries.length - 1;i >= 0; i--) {
170881
+ const entry = entries[i];
170882
+ if (!entry || typeof entry !== "object")
170883
+ continue;
170884
+ const row = entry;
170885
+ if (row.type !== "message" || typeof row.id !== "string" || row.id.length === 0) {
170886
+ continue;
170887
+ }
170888
+ if (snapshotNewestAssistantEntryId !== null && row.id === snapshotNewestAssistantEntryId) {
170889
+ continue;
170890
+ }
170891
+ const message = row.message;
170892
+ if (message && typeof message === "object" && message.role === "assistant") {
170893
+ return row.id;
170894
+ }
170895
+ }
170896
+ return null;
170897
+ }
170898
+ function writeTransformDecisionBestEffort(dbPath, row) {
170899
+ try {
170900
+ const writer = writerOverrideForTests ?? writeTransformDecisionRow;
170901
+ writer(dbPath, row);
170902
+ } catch {}
170903
+ }
170904
+ function writeTransformDecisionRow(dbPath, row) {
170905
+ const db = new Database(dbPath);
170906
+ try {
170907
+ db.exec("PRAGMA busy_timeout=0");
170908
+ db.prepare(`INSERT OR REPLACE INTO transform_decisions (
170909
+ session_id, harness, message_id, ts_ms, decision, materialized,
170910
+ materialize_reason, emergency, dropped_tokens, dropped_count, input_tokens
170911
+ ) 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)));
170912
+ } finally {
170913
+ closeQuietly(db);
170914
+ }
170915
+ }
170916
+
170283
170917
  // ../plugin/src/features/magic-context/work-metrics.ts
170284
170918
  function asNumber2(value) {
170285
170919
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -170331,76 +170965,12 @@ function computePiWorkMetrics(sessionEntries) {
170331
170965
  return { newWorkTokens, totalInputTokens };
170332
170966
  }
170333
170967
 
170334
- // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
170335
- var SYSTEM_INJECTION_MARKERS = [
170336
- "<!-- OMO_INTERNAL_INITIATOR -->",
170337
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
170338
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
170339
- "[Category+Skill Reminder]",
170340
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
170341
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
170342
- "[EMERGENCY CONTEXT WINDOW WARNING]",
170343
- "Unstable background agent appears idle",
170344
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
170345
- ];
170346
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
170347
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
170348
- function stripSystemInjection(text) {
170349
- let hasInjection = false;
170350
- for (const marker of SYSTEM_INJECTION_MARKERS) {
170351
- if (text.includes(marker)) {
170352
- hasInjection = true;
170353
- break;
170354
- }
170355
- }
170356
- if (SYSTEM_REMINDER_REGEX.test(text))
170357
- hasInjection = true;
170358
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
170359
- if (!hasInjection)
170360
- return null;
170361
- let cleaned = text;
170362
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
170363
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
170364
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
170365
- for (const marker of SYSTEM_INJECTION_MARKERS) {
170366
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
170367
- continue;
170368
- const idx = cleaned.indexOf(marker);
170369
- if (idx === -1)
170370
- continue;
170371
- const blockEnd = cleaned.indexOf(`
170372
-
170373
- `, idx + marker.length);
170374
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
170375
- }
170376
- return cleaned.trim();
170377
- }
170378
-
170379
170968
  // ../plugin/src/hooks/magic-context/apply-operations.ts
170380
- var USER_DROP_PREVIEW_CHARS = 250;
170381
170969
  var RECENT_TOOL_SKELETON_WINDOW = 20;
170382
- function buildReplacementContent(tagId, target) {
170383
- const role = target.message?.info.role;
170384
- if (role !== "user") {
170385
- return `[dropped §${tagId}§]`;
170386
- }
170387
- const currentContent = target.getContent?.() ?? "";
170388
- const strippedInjection = stripSystemInjection(currentContent);
170389
- if (strippedInjection !== null && stripTagPrefix(strippedInjection).trim().length === 0) {
170390
- return `[dropped §${tagId}§]`;
170391
- }
170392
- const originalText = stripTagPrefix(currentContent);
170393
- if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
170394
- return `[truncated §${tagId}§]
170395
- ${originalText}`;
170396
- }
170397
- const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
170398
- const softCutIndex = hardCut.search(/\s\S*$/);
170399
- const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
170400
- return `[truncated §${tagId}§]
170401
- ${preview}…`;
170402
- }
170403
- function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
170970
+ function buildReplacementContent(tagId) {
170971
+ return `[dropped §${tagId}§]`;
170972
+ }
170973
+ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
170404
170974
  let didMutateMessage = false;
170405
170975
  db.transaction(() => {
170406
170976
  const tags = preloadedTags ?? getTagsBySession(db, sessionId);
@@ -170408,11 +170978,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
170408
170978
  const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
170409
170979
  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;
170410
170980
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
170981
+ const opsToApply = [
170982
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
170983
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
170984
+ ];
170411
170985
  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));
170412
- for (const pendingOp of pendingOps) {
170986
+ for (const { op: pendingOp, synthetic } of opsToApply) {
170413
170987
  const tagStatus = tagStatusById.get(pendingOp.tagId);
170414
170988
  if (tagStatus === "compacted" || tagStatus === "dropped") {
170415
- removePendingOp(db, sessionId, pendingOp.tagId);
170989
+ if (!synthetic)
170990
+ removePendingOp(db, sessionId, pendingOp.tagId);
170416
170991
  continue;
170417
170992
  }
170418
170993
  if (protectedTagIds.has(pendingOp.tagId)) {
@@ -170420,33 +170995,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
170420
170995
  }
170421
170996
  const target = targets.get(pendingOp.tagId);
170422
170997
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
170998
+ if (synthetic) {
170999
+ if (!isToolTag || target?.canDrop?.() !== true)
171000
+ continue;
171001
+ }
171002
+ let shouldPersistDrop = false;
170423
171003
  if (isToolTag) {
170424
171004
  if (skeletonWindow.has(pendingOp.tagId)) {
170425
171005
  const truncResult = target?.truncate?.() ?? "absent";
170426
- if (truncResult === "incomplete") {
171006
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
170427
171007
  continue;
170428
171008
  }
170429
171009
  if (truncResult === "truncated") {
170430
171010
  didMutateMessage = true;
170431
171011
  }
170432
171012
  updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
171013
+ shouldPersistDrop = true;
170433
171014
  } else {
170434
171015
  const dropResult = target?.drop?.() ?? "absent";
170435
- if (dropResult === "incomplete") {
171016
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
170436
171017
  continue;
170437
171018
  }
170438
171019
  if (dropResult === "removed") {
170439
171020
  didMutateMessage = true;
170440
171021
  }
170441
171022
  updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
171023
+ shouldPersistDrop = true;
170442
171024
  }
170443
171025
  } else if (target) {
170444
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId, target));
171026
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
170445
171027
  if (changed)
170446
171028
  didMutateMessage = true;
171029
+ shouldPersistDrop = true;
171030
+ } else if (!synthetic) {
171031
+ shouldPersistDrop = true;
170447
171032
  }
171033
+ if (!shouldPersistDrop)
171034
+ continue;
170448
171035
  updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
170449
- removePendingOp(db, sessionId, pendingOp.tagId);
171036
+ if (!synthetic)
171037
+ removePendingOp(db, sessionId, pendingOp.tagId);
170450
171038
  }
170451
171039
  })();
170452
171040
  return didMutateMessage;
@@ -170470,7 +171058,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
170470
171058
  }
170471
171059
  }
170472
171060
  } else if (target) {
170473
- const changed = target.setContent(buildReplacementContent(tag.tagNumber, target));
171061
+ const changed = target.setContent(buildReplacementContent(tag.tagNumber));
170474
171062
  if (changed)
170475
171063
  didMutateMessage = true;
170476
171064
  }
@@ -170758,7 +171346,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
170758
171346
  const result = {
170759
171347
  compressedToLite: 0,
170760
171348
  compressedToFull: 0,
170761
- compressedToUltra: 0
171349
+ compressedToUltra: 0,
171350
+ mutatedTextTags: 0
170762
171351
  };
170763
171352
  if (!config2.enabled)
170764
171353
  return result;
@@ -170799,7 +171388,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
170799
171388
  const target = targets.get(tag.tagNumber);
170800
171389
  if (!target)
170801
171390
  continue;
170802
- target.setContent(compressed);
171391
+ const didMutate = target.setContent(compressed);
171392
+ if (didMutate)
171393
+ result.mutatedTextTags += 1;
170803
171394
  updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
170804
171395
  if (targetDepth === DEPTH_LITE)
170805
171396
  result.compressedToLite += 1;
@@ -171177,7 +171768,7 @@ function buildToolArcs(messages) {
171177
171768
  }
171178
171769
  return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
171179
171770
  }
171180
- function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
171771
+ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
171181
171772
  let boundary = candidate;
171182
171773
  for (const arc of arcs) {
171183
171774
  if (arc.resOrdinal !== null) {
@@ -171186,6 +171777,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
171186
171777
  }
171187
171778
  continue;
171188
171779
  }
171780
+ if (arc.invOrdinal < recentOpenArcCutoff)
171781
+ continue;
171189
171782
  if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
171190
171783
  return arc.invOrdinal;
171191
171784
  }
@@ -171423,7 +172016,7 @@ function semanticSnapBoundary(args) {
171423
172016
  return snapped;
171424
172017
  }
171425
172018
  function applyHeadCap(args) {
171426
- const { index, protectedTailStart, offset, arcs, capTokens } = args;
172019
+ const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
171427
172020
  if (offset >= protectedTailStart)
171428
172021
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
171429
172022
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
@@ -171431,7 +172024,7 @@ function applyHeadCap(args) {
171431
172024
  for (const arc of arcs) {
171432
172025
  const resOrdinal = arc.resOrdinal;
171433
172026
  if (resOrdinal === null) {
171434
- if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
172027
+ if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
171435
172028
  end = Math.min(end, arc.invOrdinal);
171436
172029
  }
171437
172030
  continue;
@@ -171498,7 +172091,14 @@ function resolveProtectedTailBoundary(ctx) {
171498
172091
  }
171499
172092
  if (ctx.mode === "manual-full-recomp") {
171500
172093
  const arcs2 = buildToolArcs(messages);
171501
- const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset);
172094
+ const recompTarget = deriveProtectedTailTokenTarget({
172095
+ contextLimit: ctx.contextLimit,
172096
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
172097
+ usagePercentage: 0,
172098
+ triggerBudget: ctx.triggerBudget
172099
+ });
172100
+ const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
172101
+ const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
171502
172102
  const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
171503
172103
  const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
171504
172104
  return {
@@ -171540,13 +172140,14 @@ function resolveProtectedTailBoundary(ctx) {
171540
172140
  const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
171541
172141
  const arcs = buildToolArcs(messages);
171542
172142
  let boundary = index.findSuffixStartForTokens(scaledN);
172143
+ const recentOpenArcCutoff = boundary;
171543
172144
  let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
171544
172145
  const tokenAtBoundary = index.tokenForOrdinal(boundary);
171545
172146
  if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
171546
172147
  boundary += 1;
171547
172148
  boundaryReason = "huge-message-exception";
171548
172149
  }
171549
- boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
172150
+ boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171550
172151
  const snapped = semanticSnapBoundary({
171551
172152
  messages,
171552
172153
  index,
@@ -171556,7 +172157,7 @@ function resolveProtectedTailBoundary(ctx) {
171556
172157
  });
171557
172158
  if (snapped !== boundary)
171558
172159
  boundaryReason = "semantic-snap";
171559
- boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
172160
+ boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171560
172161
  let runtimeFloor = offset;
171561
172162
  if (ctx.migrationFloorActive)
171562
172163
  runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
@@ -171592,7 +172193,8 @@ function resolveProtectedTailBoundary(ctx) {
171592
172193
  offset,
171593
172194
  arcs,
171594
172195
  lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
171595
- capTokens: perRunCap
172196
+ capTokens: perRunCap,
172197
+ recentOpenArcCutoff
171596
172198
  });
171597
172199
  const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
171598
172200
  return {
@@ -171643,7 +172245,7 @@ function resolveBoundaryContext(args) {
171643
172245
  }
171644
172246
  let storedTokenTotals;
171645
172247
  try {
171646
- storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId).totals;
172248
+ storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId, args.taggerFloor ?? 0).totals;
171647
172249
  } catch (error51) {
171648
172250
  sessionLog(args.sessionId, "protected-tail stored-token map unavailable (live fallback):", error51);
171649
172251
  }
@@ -171760,6 +172362,38 @@ var DEFAULT_MIN_COMMIT_CLUSTERS_FOR_TRIGGER = 3;
171760
172362
  var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
171761
172363
  var FORCE_COMPARTMENT_PERCENTAGE = 80;
171762
172364
  var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
172365
+ var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
172366
+ function tagOwnerMessageId(row) {
172367
+ if (row.type === "tool")
172368
+ return row.tool_owner_message_id ?? row.message_id;
172369
+ return row.message_id.replace(CONTENT_TAG_OWNER_SUFFIX, "");
172370
+ }
172371
+ function getActiveOrDroppedTagOwnerMessageIds(db, sessionId, floor = 0) {
172372
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id
172373
+ FROM tags
172374
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id
172375
+ FROM tags
172376
+ WHERE session_id = ? AND status IN ('active', 'dropped')`).all(sessionId);
172377
+ const owners = new Set;
172378
+ for (const row of rows)
172379
+ owners.add(tagOwnerMessageId(row));
172380
+ return owners;
172381
+ }
172382
+ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor = 0) {
172383
+ const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
172384
+ const coveredOwnerMessageIds = getActiveOrDroppedTagOwnerMessageIds(db, sessionId, taggerFloor);
172385
+ let total = 0;
172386
+ for (const message of inMemoryTail.messages) {
172387
+ if (message.ordinal <= lastCompartmentEnd)
172388
+ continue;
172389
+ if (coveredOwnerMessageIds.has(message.id))
172390
+ continue;
172391
+ total += estimateTrueRawMessageTokens(message, {
172392
+ providerShapeVersion: "opencode-v1"
172393
+ }).total;
172394
+ }
172395
+ return total;
172396
+ }
171763
172397
  function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
171764
172398
  return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
171765
172399
  }
@@ -171813,7 +172447,7 @@ function resolveBoundaryContextLimit(usage, fallbackContextLimit) {
171813
172447
  }
171814
172448
  return 128000;
171815
172449
  }
171816
- function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail) {
172450
+ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor = 0) {
171817
172451
  return withRawSessionMessageCache(() => {
171818
172452
  try {
171819
172453
  const memoryPrimed = inMemoryTail ? primeInMemoryTailRawMessageCache({
@@ -171842,7 +172476,8 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
171842
172476
  contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
171843
172477
  executeThresholdPercentage,
171844
172478
  usage,
171845
- usageSource: "live"
172479
+ usageSource: "live",
172480
+ taggerFloor
171846
172481
  });
171847
172482
  const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
171848
172483
  if (!hasProtectedEligibleHead) {
@@ -171873,7 +172508,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
171873
172508
  }
171874
172509
  });
171875
172510
  }
171876
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail) {
172511
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride) {
171877
172512
  if (sessionMeta.compartmentInProgress) {
171878
172513
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
171879
172514
  return { shouldFire: false };
@@ -171887,14 +172522,17 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
171887
172522
  inMemoryTail = undefined;
171888
172523
  }
171889
172524
  }
172525
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
171890
172526
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
171891
- if (!inMemoryTail && usage.percentage < proactiveFloorForGate) {
172527
+ if (usage.percentage < proactiveFloorForGate) {
171892
172528
  try {
171893
- const { bound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId);
172529
+ const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, taggerFloor);
171894
172530
  if (nullCount === 0) {
171895
- const eligibleUpperBound = bound;
172531
+ const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
172532
+ const eligibleUpperBound = persistedBound + untaggedUpperBound;
171896
172533
  if (eligibleUpperBound < triggerBudget) {
171897
- 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`);
172534
+ const memorySuffix = inMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
172535
+ 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`);
171898
172536
  return { shouldFire: false };
171899
172537
  }
171900
172538
  }
@@ -171902,7 +172540,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
171902
172540
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
171903
172541
  }
171904
172542
  }
171905
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail);
172543
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
171906
172544
  if (!tailInfo.hasNewRawHistory) {
171907
172545
  try {
171908
172546
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -172066,6 +172704,13 @@ function computePressure(input) {
172066
172704
  function approxThousands(tokens) {
172067
172705
  return `${Math.round(tokens / 1000)}k`;
172068
172706
  }
172707
+ function formatOldestReclaimableHint(hint) {
172708
+ if (!hint || hint.length === 0)
172709
+ return "";
172710
+ const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
172711
+ return rendered.length > 0 ? `
172712
+ oldest reclaimable: ${rendered}.` : "";
172713
+ }
172069
172714
  var CHANNEL2_USABLE_FRACTION = 1 / 3;
172070
172715
  var CHANNEL2_MIN_RECLAIMABLE = 1e4;
172071
172716
  function shouldTriggerChannel2(input) {
@@ -172075,14 +172720,16 @@ function shouldTriggerChannel2(input) {
172075
172720
  return true;
172076
172721
  return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
172077
172722
  }
172078
- function buildChannel2Reminder(undroppedTokens) {
172723
+ function buildChannel2Reminder(undroppedTokens, hint) {
172079
172724
  const amount = approxThousands(undroppedTokens);
172725
+ const hintText = formatOldestReclaimableHint(hint);
172080
172726
  return `<system-reminder>
172081
- ` + `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.
172727
+ ` + `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}
172082
172728
  ` + `</system-reminder>`;
172083
172729
  }
172084
- function buildChannel1Reminder(level, undroppedTokens) {
172730
+ function buildChannel1Reminder(level, undroppedTokens, hint) {
172085
172731
  const amount = approxThousands(undroppedTokens);
172732
+ const hintText = formatOldestReclaimableHint(hint);
172086
172733
  let body;
172087
172734
  switch (level) {
172088
172735
  case "gentle":
@@ -172098,7 +172745,7 @@ function buildChannel1Reminder(level, undroppedTokens) {
172098
172745
  return `
172099
172746
 
172100
172747
  <system-reminder>
172101
- ${body}
172748
+ ${body}${hintText}
172102
172749
  </system-reminder>`;
172103
172750
  }
172104
172751
 
@@ -172646,6 +173293,41 @@ function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions
172646
173293
  `);
172647
173294
  }
172648
173295
 
173296
+ // ../plugin/src/hooks/magic-context/tool-reclaim.ts
173297
+ function buildSyntheticToolReclaimOps(input) {
173298
+ const watermark = Math.max(0, input.watermark);
173299
+ if (watermark <= 0)
173300
+ return [];
173301
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
173302
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
173303
+ const synthetic = [];
173304
+ for (const tag of tags) {
173305
+ if (tag.type !== "tool")
173306
+ continue;
173307
+ if (tag.status !== "active")
173308
+ continue;
173309
+ if (tag.tagNumber > watermark)
173310
+ continue;
173311
+ if (realPendingTagIds.has(tag.tagNumber))
173312
+ continue;
173313
+ if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
173314
+ continue;
173315
+ synthetic.push({
173316
+ id: 0,
173317
+ sessionId: input.sessionId,
173318
+ tagId: tag.tagNumber,
173319
+ operation: "drop",
173320
+ queuedAt: 0
173321
+ });
173322
+ }
173323
+ return synthetic;
173324
+ }
173325
+ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
173326
+ const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
173327
+ advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
173328
+ return maxTagNumber;
173329
+ }
173330
+
172649
173331
  // src/context-handler.ts
172650
173332
  init_logger();
172651
173333
 
@@ -173056,7 +173738,7 @@ function tagToolPart(args) {
173056
173738
  const tagged = prependTag(tagId, text);
173057
173739
  args.part.setText(tagged);
173058
173740
  }
173059
- args.targets.set(tagId, buildToolTarget(args.part, args.message));
173741
+ args.targets.set(tagId, buildToolTarget(args.part, args.message, tagId));
173060
173742
  }
173061
173743
  function setToolContentOrText(part, content) {
173062
173744
  try {
@@ -173096,7 +173778,7 @@ function buildAggregateTarget(tagId, occurrences) {
173096
173778
  return any2 ? "removed" : "absent";
173097
173779
  },
173098
173780
  truncate() {
173099
- const sentinel = "[truncated]";
173781
+ const sentinel = `[dropped §${tagId}§]`;
173100
173782
  let any2 = false;
173101
173783
  for (const occ of occurrences) {
173102
173784
  if (setToolContentOrText(occ.part, sentinel)) {
@@ -173128,7 +173810,7 @@ function buildTextTarget(part, message) {
173128
173810
  }
173129
173811
  };
173130
173812
  }
173131
- function buildToolTarget(part, message) {
173813
+ function buildToolTarget(part, message, tagId) {
173132
173814
  return {
173133
173815
  setContent(content) {
173134
173816
  return setToolContentOrText(part, content);
@@ -173137,11 +173819,11 @@ function buildToolTarget(part, message) {
173137
173819
  return part.getText() ?? null;
173138
173820
  },
173139
173821
  drop() {
173140
- const replaced = part.replaceWithSentinel(`[dropped §${part.id ?? "?"}§]`);
173822
+ const replaced = part.replaceWithSentinel(`[dropped §${tagId}§]`);
173141
173823
  return replaced ? "removed" : "absent";
173142
173824
  },
173143
173825
  truncate() {
173144
- const ok = setToolContentOrText(part, "[truncated]");
173826
+ const ok = setToolContentOrText(part, `[dropped §${tagId}§]`);
173145
173827
  return ok ? "truncated" : "absent";
173146
173828
  },
173147
173829
  message: {
@@ -174172,7 +174854,7 @@ async function runAutoSearchHintForPi(args) {
174172
174854
  embeddingEnabled,
174173
174855
  gitCommitsEnabled,
174174
174856
  embedQuery: async (text, signal) => {
174175
- const result = await embedTextForProject(options.projectPath, text, signal);
174857
+ const result = await embedTextForProject(options.projectPath, text, signal, "query");
174176
174858
  return result?.vector ?? null;
174177
174859
  },
174178
174860
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -174426,9 +175108,10 @@ function maybeChannel1ReminderForToolResult(args) {
174426
175108
  return null;
174427
175109
  return {
174428
175110
  type: "text",
174429
- text: buildChannel1Reminder(decision.level, decision.undroppedTokens)
175111
+ text: buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags)
174430
175112
  };
174431
175113
  }
175114
+ var CHANNEL2_NUDGE_CUSTOM_TYPE = "magic-context:ceiling-nudge";
174432
175115
  function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
174433
175116
  let state;
174434
175117
  try {
@@ -174456,9 +175139,12 @@ function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
174456
175139
  if (!casChannel2NudgeState(db, sessionId, "pending", "claimed"))
174457
175140
  return false;
174458
175141
  try {
174459
- pi.sendUserMessage(buildChannel2Reminder(undropped), {
174460
- deliverAs
174461
- });
175142
+ pi.sendMessage({
175143
+ customType: CHANNEL2_NUDGE_CUSTOM_TYPE,
175144
+ content: buildChannel2Reminder(undropped, baseline.oldestReclaimableToolTags),
175145
+ display: false,
175146
+ details: { kind: "channel-2-ceiling-nudge" }
175147
+ }, { deliverAs });
174462
175148
  } catch (error51) {
174463
175149
  try {
174464
175150
  casChannel2NudgeState(db, sessionId, "claimed", "pending");
@@ -174650,6 +175336,51 @@ function planEmergencyDrop(input) {
174650
175336
  };
174651
175337
  }
174652
175338
 
175339
+ // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
175340
+ var SYSTEM_INJECTION_MARKERS = [
175341
+ "<!-- OMO_INTERNAL_INITIATOR -->",
175342
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
175343
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
175344
+ "[Category+Skill Reminder]",
175345
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
175346
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
175347
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
175348
+ "Unstable background agent appears idle",
175349
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
175350
+ ];
175351
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
175352
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
175353
+ function stripSystemInjection(text) {
175354
+ let hasInjection = false;
175355
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
175356
+ if (text.includes(marker)) {
175357
+ hasInjection = true;
175358
+ break;
175359
+ }
175360
+ }
175361
+ if (SYSTEM_REMINDER_REGEX.test(text))
175362
+ hasInjection = true;
175363
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
175364
+ if (!hasInjection)
175365
+ return null;
175366
+ let cleaned = text;
175367
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
175368
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
175369
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
175370
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
175371
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
175372
+ continue;
175373
+ const idx = cleaned.indexOf(marker);
175374
+ if (idx === -1)
175375
+ continue;
175376
+ const blockEnd = cleaned.indexOf(`
175377
+
175378
+ `, idx + marker.length);
175379
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
175380
+ }
175381
+ return cleaned.trim();
175382
+ }
175383
+
174653
175384
  // src/heuristic-cleanup-pi.ts
174654
175385
  init_logger();
174655
175386
  var DEDUP_SAFE_TOOLS = new Set([
@@ -174753,6 +175484,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174753
175484
  const protectedCutoff = maxTag - config2.protectedTags;
174754
175485
  const toolAgeCutoff = protectedCutoff;
174755
175486
  let droppedTools = 0;
175487
+ let emergencyDroppedTools = 0;
174756
175488
  let deduplicatedTools = 0;
174757
175489
  let droppedInjections = 0;
174758
175490
  let droppedStaleReduceCalls = 0;
@@ -174785,6 +175517,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174785
175517
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
174786
175518
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
174787
175519
  droppedTools++;
175520
+ emergencyDroppedTools++;
174788
175521
  }
174789
175522
  }
174790
175523
  setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
@@ -174808,10 +175541,14 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174808
175541
  if (!matched)
174809
175542
  continue;
174810
175543
  const target = targets.get(tag.tagNumber);
174811
- target?.drop?.();
175544
+ const result = target?.drop?.() ?? "absent";
175545
+ if (result === "incomplete")
175546
+ continue;
174812
175547
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
174813
175548
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
174814
- droppedStaleReduceCalls++;
175549
+ if (result === "removed" || result === "truncated") {
175550
+ droppedStaleReduceCalls++;
175551
+ }
174815
175552
  }
174816
175553
  })();
174817
175554
  }
@@ -174883,7 +175620,9 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174883
175620
  continue;
174884
175621
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
174885
175622
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
174886
- deduplicatedTools++;
175623
+ if (result === "removed" || result === "truncated") {
175624
+ deduplicatedTools++;
175625
+ }
174887
175626
  }
174888
175627
  }
174889
175628
  })();
@@ -174892,6 +175631,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174892
175631
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
174893
175632
  }
174894
175633
  let compressedTextTags = 0;
175634
+ let mutatedTextTags = 0;
174895
175635
  if (config2.caveman?.enabled) {
174896
175636
  const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
174897
175637
  enabled: true,
@@ -174899,13 +175639,16 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
174899
175639
  protectedTags: config2.protectedTags
174900
175640
  });
174901
175641
  compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
175642
+ mutatedTextTags = cavemanResult.mutatedTextTags;
174902
175643
  }
174903
175644
  return {
174904
175645
  droppedTools,
174905
175646
  deduplicatedTools,
174906
175647
  droppedInjections,
174907
175648
  droppedStaleReduceCalls,
174908
- compressedTextTags
175649
+ emergencyDroppedTools,
175650
+ compressedTextTags,
175651
+ mutatedTextTags
174909
175652
  };
174910
175653
  }
174911
175654
  function buildMessageIdToMaxTagFromTargets(targets) {
@@ -176201,7 +176944,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
176201
176944
  for (const compartment of compartments) {
176202
176945
  try {
176203
176946
  const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
176204
- const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage);
176947
+ const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage) || buildCompartmentSummaryFallbackText(db, compartment.id);
176205
176948
  if (canonicalText.length === 0)
176206
176949
  continue;
176207
176950
  const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
@@ -179394,6 +180137,7 @@ async function runPiHistorian(deps) {
179394
180137
  } else {
179395
180138
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
179396
180139
  }
180140
+ clearEmergencyDrainLatch(db, sessionId);
179397
180141
  return;
179398
180142
  }
179399
180143
  const perRunCap = selectPerRunCap(boundarySnapshot);
@@ -179405,7 +180149,8 @@ async function runPiHistorian(deps) {
179405
180149
  trueRawTokens: boundarySnapshot.trueRawEligibleTokens,
179406
180150
  usagePercentage: boundarySnapshot.usagePercentage,
179407
180151
  usable,
179408
- perRunCap
180152
+ perRunCap,
180153
+ executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
179409
180154
  });
179410
180155
  if (!reserve.ok) {
179411
180156
  sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
@@ -179422,6 +180167,7 @@ async function runPiHistorian(deps) {
179422
180167
  } else {
179423
180168
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
179424
180169
  }
180170
+ clearEmergencyDrainLatch(db, sessionId);
179425
180171
  telemetry.status = "noop";
179426
180172
  telemetry.failureReason = "chunk empty after filtering";
179427
180173
  rollbackDrainReservation();
@@ -179653,6 +180399,7 @@ ${chunkText}`,
179653
180399
  }
179654
180400
  appendCompartments(db, sessionId, newCompartments);
179655
180401
  clearHistorianFailureState(db, sessionId);
180402
+ clearHistorianDrainFailure(db, sessionId);
179656
180403
  recordProtectedTailPublicationFloor(db, sessionId, lastNewEnd + 1);
179657
180404
  clearEmergencyRecovery(db, sessionId);
179658
180405
  if (firstKeptEntryId && lastNewEndMessageId) {
@@ -179749,8 +180496,12 @@ ${chunkText}`,
179749
180496
  await notify(buildHistorianFailureNotice(failCount, desc.brief));
179750
180497
  }
179751
180498
  } finally {
179752
- if (!completedSuccessfully && !retainDrainReservationForRetryThrottle) {
179753
- rollbackDrainReservation();
180499
+ if (!completedSuccessfully) {
180500
+ if (!retainDrainReservationForRetryThrottle) {
180501
+ rollbackDrainReservation();
180502
+ } else {
180503
+ recordHistorianDrainFailure(db, sessionId);
180504
+ }
179754
180505
  }
179755
180506
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
179756
180507
  try {
@@ -179989,7 +180740,7 @@ var INLINE_THINKING_PATTERNS = [
179989
180740
  /<thinking>[\s\S]*?<\/thinking>\s*/gi,
179990
180741
  /<think>[\s\S]*?<\/think>\s*/gi
179991
180742
  ];
179992
- var CLEARED = "[cleared]";
180743
+ var CLEARED = "";
179993
180744
  function stripInlineThinkingMarkup(text) {
179994
180745
  let cleaned = text;
179995
180746
  for (const pattern of INLINE_THINKING_PATTERNS) {
@@ -180038,8 +180789,11 @@ function clearOldReasoningPi(args) {
180038
180789
  for (const part of msg.content) {
180039
180790
  if (part && typeof part === "object" && part.type === "thinking") {
180040
180791
  const tp = part;
180041
- if (tp.thinking !== CLEARED) {
180792
+ if (tp.redacted)
180793
+ continue;
180794
+ if (tp.thinking !== CLEARED || tp.thinkingSignature !== undefined) {
180042
180795
  tp.thinking = CLEARED;
180796
+ tp.thinkingSignature = undefined;
180043
180797
  cleared++;
180044
180798
  }
180045
180799
  }
@@ -180117,8 +180871,11 @@ function replayClearedReasoningPi(args) {
180117
180871
  for (const part of msg.content) {
180118
180872
  if (part && typeof part === "object" && part.type === "thinking") {
180119
180873
  const tp = part;
180120
- if (tp.thinking !== CLEARED) {
180874
+ if (tp.redacted)
180875
+ continue;
180876
+ if (tp.thinking !== CLEARED || tp.thinkingSignature !== undefined) {
180121
180877
  tp.thinking = CLEARED;
180878
+ tp.thinkingSignature = undefined;
180122
180879
  cleared++;
180123
180880
  }
180124
180881
  }
@@ -180282,9 +181039,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
180282
181039
  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).`;
180283
181040
  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.`;
180284
181041
  var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
180285
- Use \`ctx_reduce\` to manage context size. It supports one operation:
180286
- - \`drop\`: Remove entirely (best for tool outputs you already acted on).
180287
- Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
181042
+ 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".
180288
181043
  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.
180289
181044
  ${CTX_NOTE_GUIDANCE}
180290
181045
  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.
@@ -180303,7 +181058,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
180303
181058
  \`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.
180304
181059
  ${TOOL_HISTORY_GUIDANCE}
180305
181060
  NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
180306
- NEVER drop user messages — they are short and will be summarized by compartmentalization automatically. Dropping them loses context the historian needs.
181061
+ 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\`.
180307
181062
  NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
180308
181063
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
180309
181064
  var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
@@ -181435,6 +182190,11 @@ function registerPiContextHandler(pi, baseOptions) {
181435
182190
  updateSessionProjectTracking(sessionId, projectIdentity, options.db);
181436
182191
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
181437
182192
  const branchEntries = readPiBranchEntriesForContext(ctx, sessionId);
182193
+ schedulePiTransformDecisionResolve({
182194
+ db: options.db,
182195
+ sessionId,
182196
+ branchEntries
182197
+ });
181438
182198
  const rawMessageProvider = {
181439
182199
  readMessages: () => branchEntries !== null ? convertEntriesToRawMessages([...branchEntries]) : readPiSessionMessages(ctx),
181440
182200
  readMessageById: (messageId) => readPiSessionMessageById(ctx, messageId)
@@ -181671,6 +182431,20 @@ function registerPiContextHandler(pi, baseOptions) {
181671
182431
  appendCompaction: resolvePiAppendCompaction(ctx),
181672
182432
  readBranchEntries: resolvePiReadBranchEntries(ctx)
181673
182433
  });
182434
+ const piDecisionSnapshotNewestAssistant = branchEntries === null ? undefined : findNewestPiAssistantEntryId(branchEntries);
182435
+ if (result.bustedThisPass && piDecisionSnapshotNewestAssistant !== undefined) {
182436
+ recordPendingPiTransformDecision(sessionId, {
182437
+ tsMs: Date.now(),
182438
+ decision: schedulerDecision,
182439
+ materialized: result.materialized,
182440
+ materializeReason: normalizeMaterializeReason("pi", result.materializeReason, result.materialized),
182441
+ emergency: result.emergency,
182442
+ droppedTokens: result.droppedTokens,
182443
+ droppedCount: result.droppedCount,
182444
+ inputTokens: usageInputTokens,
182445
+ bustedThisPass: true
182446
+ }, piDecisionSnapshotNewestAssistant);
182447
+ }
181674
182448
  if (options.historian) {
181675
182449
  maybeFireHistorian({
181676
182450
  ctx,
@@ -181780,6 +182554,7 @@ function registerPiContextHandler(pi, baseOptions) {
181780
182554
  const executeThresholdTokensPi = Math.round((usageContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
181781
182555
  const usableTokensPi = Math.max(0, executeThresholdTokensPi - usageInputTokens + liveTailTokens);
181782
182556
  resetLastNudgeCycleIfTailShrank(options.db, sessionId, tailToolTokens);
182557
+ const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(options.db, sessionId, options.protectedTags ?? 20);
181783
182558
  setPiChannel1Baseline(sessionId, {
181784
182559
  tailToolTokens,
181785
182560
  historyBudgetTokens: historyBudgetTokens ?? 0,
@@ -181788,7 +182563,8 @@ function registerPiContextHandler(pi, baseOptions) {
181788
182563
  lastInputTokens: usageInputTokens,
181789
182564
  turnToolTokens: 0,
181790
182565
  usableTokens: usableTokensPi,
181791
- reducedSinceRefresh: false
182566
+ reducedSinceRefresh: false,
182567
+ oldestReclaimableToolTags
181792
182568
  });
181793
182569
  if (usageContextLimit && usageContextLimit > 0 && resolvedExecuteThresholdPct > 0) {
181794
182570
  const channel2ShouldTrigger = shouldTriggerChannel2({
@@ -181816,6 +182592,7 @@ function registerPiContextHandler(pi, baseOptions) {
181816
182592
  logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
181817
182593
  sessionLog(sessionId, `transform completed in ${(performance.now() - transformStartTime).toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
181818
182594
  clearLastTransformErrorIfSet(options.db, sessionId);
182595
+ options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity);
181819
182596
  return { messages: outputMessages };
181820
182597
  } catch (err) {
181821
182598
  const message = err instanceof Error ? err.message : String(err);
@@ -182162,6 +182939,13 @@ async function runPipeline(args) {
182162
182939
  let historyWasConsumedThisPass = false;
182163
182940
  let materializationSatisfiedThisPass = false;
182164
182941
  let pendingOpsAppliedThisPass = false;
182942
+ let pendingOpsDidMutate = false;
182943
+ let heuristicOrReasoningDidMutate = false;
182944
+ let didMutateFromFlushedStatuses = false;
182945
+ let droppedCount = 0;
182946
+ const droppedTokens = 0;
182947
+ let emergency = false;
182948
+ let autoReclaimDidMutateThisPass = false;
182165
182949
  let suppressDeferredHistoryDrain = false;
182166
182950
  let casLost = false;
182167
182951
  const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
@@ -182247,7 +183031,10 @@ async function runPipeline(args) {
182247
183031
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
182248
183032
  try {
182249
183033
  const tApplyPending = performance.now();
182250
- applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
183034
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
183035
+ if (pendingOpsDidMutate) {
183036
+ droppedCount += pendingOps.length;
183037
+ }
182251
183038
  logTransformTiming(args.sessionId, "applyPendingOperations", tApplyPending);
182252
183039
  executedWorkThisPass = true;
182253
183040
  materializationSatisfiedThisPass = true;
@@ -182269,7 +183056,7 @@ async function runPipeline(args) {
182269
183056
  const flushedSliceTags = getTagsByNumbers(args.db, args.sessionId, targetTagNumbers);
182270
183057
  logTransformTiming(args.sessionId, "getTagsByNumbers", tGetTags, `targets=${targetTagNumbers.length} fetched=${flushedSliceTags.length}`);
182271
183058
  const tFlushed = performance.now();
182272
- applyFlushedStatuses(args.sessionId, args.db, targets, flushedSliceTags);
183059
+ didMutateFromFlushedStatuses = applyFlushedStatuses(args.sessionId, args.db, targets, flushedSliceTags);
182273
183060
  logTransformTiming(args.sessionId, "applyFlushedStatuses", tFlushed);
182274
183061
  logTransformTiming(args.sessionId, "batchFinalize:flushed", tFlushed);
182275
183062
  const messageIdToMaxTag = buildMessageIdToMaxTag(targets);
@@ -182331,6 +183118,11 @@ async function runPipeline(args) {
182331
183118
  } : undefined,
182332
183119
  caveman: args.heuristics.caveman
182333
183120
  }, activeTags, stableIdResolver);
183121
+ const heuristicMutationCount = heuristicsResult.droppedTools + heuristicsResult.deduplicatedTools + heuristicsResult.droppedInjections + heuristicsResult.droppedStaleReduceCalls + heuristicsResult.mutatedTextTags;
183122
+ droppedCount += heuristicsResult.droppedTools + heuristicsResult.deduplicatedTools + heuristicsResult.droppedInjections + heuristicsResult.droppedStaleReduceCalls + heuristicsResult.mutatedTextTags;
183123
+ emergency ||= heuristicsResult.emergencyDroppedTools > 0;
183124
+ if (heuristicMutationCount > 0)
183125
+ heuristicOrReasoningDidMutate = true;
182334
183126
  heuristicsExecuted = true;
182335
183127
  executedWorkThisPass = true;
182336
183128
  if (hasPendingMaterializeSignal) {
@@ -182339,7 +183131,7 @@ async function runPipeline(args) {
182339
183131
  if (currentTurnId !== null) {
182340
183132
  lastHeuristicsTurnIdBySession.set(args.sessionId, currentTurnId);
182341
183133
  }
182342
- logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} compressedTextTags=${heuristicsResult.compressedTextTags}`);
183134
+ logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} staleReduce=${heuristicsResult.droppedStaleReduceCalls} compressedTextTags=${heuristicsResult.compressedTextTags} mutatedTextTags=${heuristicsResult.mutatedTextTags}`);
182343
183135
  } catch (err) {
182344
183136
  sessionLog(args.sessionId, `heuristic cleanup failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182345
183137
  }
@@ -182376,6 +183168,10 @@ async function runPipeline(args) {
182376
183168
  }
182377
183169
  logTransformTiming(args.sessionId, "clearOldReasoning", tClearReasoning);
182378
183170
  logTransformTiming(args.sessionId, "watermarkCleanup", tClearReasoning);
183171
+ if (clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
183172
+ heuristicOrReasoningDidMutate = true;
183173
+ droppedCount += clearOutcome.cleared + stripOutcome.stripped;
183174
+ }
182379
183175
  if (combinedWatermark > prevWatermark || clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
182380
183176
  executedWorkThisPass = true;
182381
183177
  }
@@ -182383,7 +183179,36 @@ async function runPipeline(args) {
182383
183179
  sessionLog(args.sessionId, `reasoning clearing failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182384
183180
  }
182385
183181
  }
183182
+ const toolReclaimExecutePass = args.schedulerDecision === "execute";
183183
+ const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
183184
+ const emergencyDropEligible = args.forceMaterialization === true || args.contextUsage.percentage >= FORCE_MATERIALIZATION_PERCENTAGE;
183185
+ let autoReclaimTargetCount = 0;
183186
+ let autoReclaimDidMutate = false;
183187
+ if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
183188
+ const reclaimMeta = getOrCreateSessionMeta(args.db, args.sessionId);
183189
+ const syntheticPendingOps = buildSyntheticToolReclaimOps({
183190
+ db: args.db,
183191
+ sessionId: args.sessionId,
183192
+ targets,
183193
+ watermark: reclaimMeta.toolReclaimWatermark ?? 0,
183194
+ pendingOps
183195
+ });
183196
+ autoReclaimTargetCount = syntheticPendingOps.length;
183197
+ if (syntheticPendingOps.length > 0) {
183198
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, [], syntheticPendingOps);
183199
+ if (autoReclaimDidMutate) {
183200
+ droppedCount += syntheticPendingOps.length;
183201
+ autoReclaimDidMutateThisPass = true;
183202
+ }
183203
+ }
183204
+ }
182386
183205
  transcript.commit();
183206
+ if (toolReclaimExecutePass) {
183207
+ advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
183208
+ }
183209
+ if (autoReclaimTargetCount > 0) {
183210
+ sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
183211
+ }
182387
183212
  const postCommitStableIdByRef = new Map;
182388
183213
  const postCommitEntryIdByRef = new Map;
182389
183214
  for (let i = 0;i < args.messages.length; i++) {
@@ -182483,6 +183308,9 @@ async function runPipeline(args) {
182483
183308
  } catch (err) {
182484
183309
  sessionLog(args.sessionId, `token accounting failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
182485
183310
  }
183311
+ const materialized = injectionResult?.m0Materialized === true;
183312
+ const materializeReason = injectionResult?.m0Reason ?? null;
183313
+ const bustedThisPass = didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || materialized || historyWasConsumedThisPass;
182486
183314
  return {
182487
183315
  messages: outputMessages,
182488
183316
  heuristicsExecuted,
@@ -182491,6 +183319,12 @@ async function runPipeline(args) {
182491
183319
  syntheticLeadingCount: injectionResult?.syntheticLeadingCount ?? 0,
182492
183320
  heuristicsResult,
182493
183321
  injectionResult,
183322
+ materialized,
183323
+ materializeReason,
183324
+ droppedTokens,
183325
+ droppedCount,
183326
+ emergency,
183327
+ bustedThisPass,
182494
183328
  targetCount: targets.size,
182495
183329
  reasoningWatermark: getOrCreateSessionMeta(args.db, args.sessionId).clearedReasoningThroughTag ?? 0,
182496
183330
  activeTags,
@@ -182667,6 +183501,7 @@ function clearContextHandlerSession(sessionId) {
182667
183501
  rawMessageProviderUnregistersBySession.delete(sessionId);
182668
183502
  }
182669
183503
  clearSessionTracking(sessionId);
183504
+ clearPiEmbedSessionState(sessionId);
182670
183505
  }
182671
183506
 
182672
183507
  // src/commands/ctx-flush.ts
@@ -182916,6 +183751,12 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
182916
183751
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
182917
183752
  import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
182918
183753
  import { join as join16 } from "node:path";
183754
+
183755
+ // ../plugin/src/agents/historian.ts
183756
+ var HISTORIAN_AGENT = "historian";
183757
+ var HISTORIAN_EDITOR_AGENT = "historian-editor";
183758
+
183759
+ // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
182919
183760
  init_data_path();
182920
183761
  function historianResponseDumpDir(directory) {
182921
183762
  return getProjectMagicContextHistorianDir(directory);
@@ -185169,7 +186010,7 @@ function formatThresholdPercent(value) {
185169
186010
  // package.json
185170
186011
  var package_default = {
185171
186012
  name: "@cortexkit/pi-magic-context",
185172
- version: "0.24.1",
186013
+ version: "0.26.0",
185173
186014
  type: "module",
185174
186015
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
185175
186016
  main: "dist/index.js",
@@ -185782,9 +186623,184 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
185782
186623
 
185783
186624
  ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
185784
186625
 
185785
- 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.`;
186626
+ 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.
186627
+
186628
+ Two recovery modes for finer detail:
186629
+ - 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.
186630
+ - 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.`;
185786
186631
  var CTX_EXPAND_TOKEN_BUDGET = 15000;
185787
186632
 
186633
+ // ../plugin/src/tools/ctx-expand/render.ts
186634
+ function isRecord3(value) {
186635
+ return value !== null && typeof value === "object" && !Array.isArray(value);
186636
+ }
186637
+ function roleLabel(role) {
186638
+ if (role === "assistant")
186639
+ return "A (assistant)";
186640
+ if (role === "user")
186641
+ return "U (user)";
186642
+ return role;
186643
+ }
186644
+ function truncate2(value, max) {
186645
+ const t = value.trim();
186646
+ return t.length <= max ? t : `${t.slice(0, max)}…`;
186647
+ }
186648
+ function keyArg(input) {
186649
+ if (!input)
186650
+ return "";
186651
+ for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
186652
+ const v = input[k];
186653
+ if (typeof v === "string" && v.length > 0)
186654
+ return truncate2(v, 60);
186655
+ }
186656
+ if (typeof input.description === "string")
186657
+ return truncate2(input.description, 60);
186658
+ return "";
186659
+ }
186660
+ function asToolPart(part) {
186661
+ const type = typeof part.type === "string" ? part.type : "";
186662
+ if (type === "tool") {
186663
+ const state = isRecord3(part.state) ? part.state : null;
186664
+ const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
186665
+ const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
186666
+ const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
186667
+ return {
186668
+ name: typeof part.tool === "string" ? part.tool : "tool",
186669
+ callId: typeof part.callID === "string" ? part.callID : "",
186670
+ title,
186671
+ input: state && isRecord3(state.input) ? state.input : null,
186672
+ output
186673
+ };
186674
+ }
186675
+ if (type === "tool_use") {
186676
+ return {
186677
+ name: typeof part.name === "string" ? part.name : "tool",
186678
+ callId: typeof part.id === "string" ? part.id : "",
186679
+ title: null,
186680
+ input: isRecord3(part.input) ? part.input : null,
186681
+ output: null
186682
+ };
186683
+ }
186684
+ if (type === "tool_result") {
186685
+ const content = part.content;
186686
+ const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
186687
+ return {
186688
+ name: "tool_result",
186689
+ callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
186690
+ title: null,
186691
+ input: null,
186692
+ output
186693
+ };
186694
+ }
186695
+ return null;
186696
+ }
186697
+ function textOf(part) {
186698
+ if (part.type === "text" && typeof part.text === "string")
186699
+ return part.text;
186700
+ return null;
186701
+ }
186702
+ function reasoningOf(part) {
186703
+ if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
186704
+ return part.text;
186705
+ }
186706
+ return null;
186707
+ }
186708
+ function renderPartPreview(part) {
186709
+ if (!isRecord3(part))
186710
+ return null;
186711
+ const text = textOf(part);
186712
+ if (text !== null) {
186713
+ const t = truncate2(text, 200);
186714
+ return t.length > 0 ? ` • ${t}` : null;
186715
+ }
186716
+ const tool = asToolPart(part);
186717
+ if (tool) {
186718
+ const arg = keyArg(tool.input);
186719
+ const head = arg ? `${tool.name}(${arg})` : tool.name;
186720
+ return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
186721
+ }
186722
+ const reasoning = reasoningOf(part);
186723
+ if (reasoning !== null)
186724
+ return ` • [reasoning] ${truncate2(reasoning, 120)}`;
186725
+ const type = typeof part.type === "string" ? part.type : "part";
186726
+ if (type === "file")
186727
+ return " • [file]";
186728
+ if (type === "step-start" || type === "step-finish")
186729
+ return null;
186730
+ return ` • [${type}]`;
186731
+ }
186732
+ function renderPartFull(part) {
186733
+ if (!isRecord3(part))
186734
+ return null;
186735
+ const text = textOf(part);
186736
+ if (text !== null) {
186737
+ return text.trim().length > 0 ? ` [text]
186738
+ ${text}` : null;
186739
+ }
186740
+ const tool = asToolPart(part);
186741
+ if (tool) {
186742
+ const lines = [];
186743
+ const idSuffix = tool.callId ? ` #${tool.callId}` : "";
186744
+ lines.push(` [tool: ${tool.name}${idSuffix}]`);
186745
+ if (tool.title && tool.title.trim().length > 0) {
186746
+ lines.push(` description: ${tool.title.trim()}`);
186747
+ }
186748
+ if (tool.input)
186749
+ lines.push(` input: ${JSON.stringify(tool.input)}`);
186750
+ if (tool.output !== null)
186751
+ lines.push(` output:
186752
+ ${tool.output}`);
186753
+ return lines.join(`
186754
+ `);
186755
+ }
186756
+ const type = typeof part.type === "string" ? part.type : "part";
186757
+ if (type === "file") {
186758
+ const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
186759
+ return ` [file]${name2 ? ` ${name2}` : ""}`;
186760
+ }
186761
+ return null;
186762
+ }
186763
+ function renderMessageByOrdinal(sessionId, ordinal) {
186764
+ const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
186765
+ if (!msg) {
186766
+ 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.`;
186767
+ }
186768
+ const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
186769
+ const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
186770
+ if (rendered.length === 0) {
186771
+ lines.push(" (no recoverable content — message had only structural/reasoning parts)");
186772
+ } else {
186773
+ lines.push(...rendered);
186774
+ }
186775
+ return lines.join(`
186776
+ `);
186777
+ }
186778
+ function renderVerboseRange(sessionId, start, end, tokenBudget) {
186779
+ const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
186780
+ const out = [];
186781
+ let usedTokens = 0;
186782
+ let lastOrdinal = start - 1;
186783
+ let truncated = false;
186784
+ for (const msg of messages) {
186785
+ const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
186786
+ const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
186787
+ const block = partLines.length > 0 ? `${header}
186788
+ ${partLines.join(`
186789
+ `)}` : header;
186790
+ const blockTokens = estimateTokens(block);
186791
+ if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
186792
+ truncated = true;
186793
+ break;
186794
+ }
186795
+ out.push(block);
186796
+ usedTokens += blockTokens;
186797
+ lastOrdinal = msg.ordinal;
186798
+ }
186799
+ return { text: out.join(`
186800
+
186801
+ `), lastOrdinal, truncated };
186802
+ }
186803
+
185788
186804
  // ../../node_modules/.bun/typebox@1.1.38/node_modules/typebox/build/system/memory/memory.mjs
185789
186805
  var exports_memory = {};
185790
186806
  __export(exports_memory, {
@@ -189850,12 +190866,18 @@ __export(exports_typebox, {
189850
190866
  });
189851
190867
  // src/tools/ctx-expand.ts
189852
190868
  var ParamsSchema = exports_typebox.Object({
189853
- start: exports_typebox.Number({
190869
+ start: exports_typebox.Optional(exports_typebox.Number({
189854
190870
  description: "Start message ordinal (from compartment start attribute)"
189855
- }),
189856
- end: exports_typebox.Number({
190871
+ })),
190872
+ end: exports_typebox.Optional(exports_typebox.Number({
189857
190873
  description: "End message ordinal (from compartment end attribute)"
189858
- })
190874
+ })),
190875
+ verbose: exports_typebox.Optional(exports_typebox.Boolean({
190876
+ 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."
190877
+ })),
190878
+ message: exports_typebox.Optional(exports_typebox.Number({
190879
+ 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."
190880
+ }))
189859
190881
  });
189860
190882
  function ok(text) {
189861
190883
  return { content: [{ type: "text", text }], details: undefined };
@@ -189874,22 +190896,41 @@ function createCtxExpandTool(deps) {
189874
190896
  description: CTX_EXPAND_DESCRIPTION,
189875
190897
  parameters: ParamsSchema,
189876
190898
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
189877
- if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
189878
- return err("Error: start and end must be positive integers with start <= end.");
189879
- }
189880
190899
  const sessionId = ctx.sessionManager.getSessionId();
189881
190900
  if (!sessionId) {
189882
190901
  return err("Error: no active Pi session.");
189883
190902
  }
189884
- const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
189885
- if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
189886
- 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.`);
189887
- }
189888
- const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
189889
190903
  const unregister = setRawMessageProvider(sessionId, {
189890
190904
  readMessages: () => readPiSessionMessages(ctx)
189891
190905
  });
189892
190906
  try {
190907
+ if (typeof params.message === "number" && params.message >= 1) {
190908
+ return ok(renderMessageByOrdinal(sessionId, params.message));
190909
+ }
190910
+ if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
190911
+ return err("Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).");
190912
+ }
190913
+ const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
190914
+ if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
190915
+ 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.`);
190916
+ }
190917
+ const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
190918
+ if (params.verbose === true) {
190919
+ const v = renderVerboseRange(sessionId, params.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
190920
+ if (!v.text) {
190921
+ return ok(`No messages found in range ${params.start}-${effectiveEnd}. The range may be outside this session's history.`);
190922
+ }
190923
+ const out = [
190924
+ `Messages ${params.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
190925
+ "",
190926
+ v.text
190927
+ ];
190928
+ if (v.truncated) {
190929
+ 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.`);
190930
+ }
190931
+ return ok(out.join(`
190932
+ `));
190933
+ }
189893
190934
  const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, params.start, effectiveEnd + 1);
189894
190935
  if (!chunk.text || chunk.messageCount === 0) {
189895
190936
  return ok(`No messages found in range ${params.start}-${params.end}. The range may be outside this session's history.`);
@@ -190608,15 +191649,16 @@ function parseInteger(str) {
190608
191649
  }
190609
191650
 
190610
191651
  // ../plugin/src/tools/ctx-reduce/constants.ts
190611
- var CTX_REDUCE_DESCRIPTION = `Reduce context by dropping tagged content you no longer need.
190612
- Use §N§ identifiers visible in conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
191652
+ 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".
191653
+
191654
+ How it works:
191655
+ - 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.
191656
+ - The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
191657
+ - 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?".
190613
191658
 
190614
- CRITICAL RULES:
190615
- - NEVER blanket-drop large ranges (e.g., "1-50"). Always review what each tag contains first.
190616
- - Only drop tool outputs you have already processed and no longer need.
190617
- - Protected tags are accepted but deferred until they leave the last protected range.
190618
- - Keep recent context — only reduce OLD content that is no longer relevant to current work.
190619
- - Dropped content is gone forever.`;
191659
+ 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.
191660
+ Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
191661
+ Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
190620
191662
 
190621
191663
  // src/tools/ctx-reduce.ts
190622
191664
  var ParamsSchema4 = exports_typebox.Object({
@@ -190843,7 +191885,7 @@ function createCtxSearchTool(deps) {
190843
191885
  memoryEnabled,
190844
191886
  embeddingEnabled,
190845
191887
  embedQuery: async (text, signal) => {
190846
- const result = await embedTextForProject(projectIdentity, text, signal);
191888
+ const result = await embedTextForProject(projectIdentity, text, signal, "query");
190847
191889
  return result?.vector ?? null;
190848
191890
  },
190849
191891
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -191071,7 +192113,7 @@ function resolveSidekickFromConfig(config2) {
191071
192113
  systemPrompt: sidekick.system_prompt,
191072
192114
  timeoutMs: sidekick.timeout_ms,
191073
192115
  thinking_level: sidekick.thinking_level,
191074
- fallbackModels: resolveFallbackChain("sidekick", sidekick.fallback_models)
192116
+ fallbackModels: resolveFallbackChain(sidekick.fallback_models)
191075
192117
  };
191076
192118
  }
191077
192119
  function resolveHistorianFromConfig(config2) {
@@ -191083,7 +192125,7 @@ function resolveHistorianFromConfig(config2) {
191083
192125
  return;
191084
192126
  const historianContextLimit = resolveHistorianContextLimit(model);
191085
192127
  const historianChunkTokens = deriveHistorianChunkTokens(historianContextLimit);
191086
- const fallbackModels = resolveFallbackChain("historian", historian?.fallback_models);
192128
+ const fallbackModels = resolveFallbackChain(historian?.fallback_models);
191087
192129
  return {
191088
192130
  runner: new PiSubagentRunner,
191089
192131
  model,
@@ -191217,7 +192259,21 @@ async function src_default2(pi) {
191217
192259
  },
191218
192260
  historian: hist,
191219
192261
  autoSearch: auto,
191220
- resolveForProject: resolveContextOptionsForProject
192262
+ resolveForProject: resolveContextOptionsForProject,
192263
+ maybeAutoEmbedSession: (sessionId, dir, identity) => {
192264
+ maybeAutoEmbedPiSession({
192265
+ db: database,
192266
+ projectDir: dir,
192267
+ projectIdentity: identity,
192268
+ memoryEnabled: cfg.memory.enabled
192269
+ }, sessionId, dir, identity, (text) => {
192270
+ pi.sendMessage({
192271
+ customType: "ctx-status",
192272
+ content: text,
192273
+ display: true
192274
+ }, { triggerTurn: false });
192275
+ });
192276
+ }
191221
192277
  });
191222
192278
  function resolveContextOptionsForProject(dir) {
191223
192279
  const cached2 = contextOptionsByDir.get(dir);
@@ -191299,14 +192355,14 @@ async function src_default2(pi) {
191299
192355
  onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
191300
192356
  });
191301
192357
  info("registered /ctx-dream");
191302
- registerCtxEmbedHistoryCommand(pi, {
192358
+ registerCtxEmbedCommand(pi, {
191303
192359
  db,
191304
192360
  projectDir,
191305
192361
  projectIdentity,
191306
192362
  memoryEnabled: config2.memory.enabled,
191307
192363
  resolveProject: resolveCurrentProject
191308
192364
  });
191309
- info("registered /ctx-embed-history");
192365
+ info("registered /ctx-embed");
191310
192366
  const dreamerConfig = resolveDreamerFromConfig(config2);
191311
192367
  if (dreamerConfig) {
191312
192368
  registerPiDreamerProject({