@wolfx/pi-magic-context 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9582,6 +9582,41 @@ var MIGRATIONS = [
9582
9582
  `);
9583
9583
  }
9584
9584
  }
9585
+ },
9586
+ {
9587
+ version: 37,
9588
+ description: "emergency drain catch-up latch + historian drain failure backoff",
9589
+ up: (db) => {
9590
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
9591
+ if (!hasSessionMeta)
9592
+ return;
9593
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
9594
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
9595
+ }
9596
+ },
9597
+ {
9598
+ version: 38,
9599
+ description: "durable transform decisions for cache-event cause attribution",
9600
+ up: (db) => {
9601
+ db.exec(`
9602
+ CREATE TABLE IF NOT EXISTS transform_decisions (
9603
+ session_id TEXT NOT NULL,
9604
+ harness TEXT NOT NULL DEFAULT 'opencode',
9605
+ message_id TEXT NOT NULL,
9606
+ ts_ms INTEGER NOT NULL,
9607
+ decision TEXT NOT NULL,
9608
+ materialized INTEGER NOT NULL DEFAULT 0,
9609
+ materialize_reason TEXT,
9610
+ emergency INTEGER NOT NULL DEFAULT 0,
9611
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
9612
+ dropped_count INTEGER NOT NULL DEFAULT 0,
9613
+ input_tokens INTEGER NOT NULL DEFAULT 0,
9614
+ PRIMARY KEY (session_id, harness, message_id)
9615
+ );
9616
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
9617
+ ON transform_decisions(session_id, harness);
9618
+ `);
9619
+ }
9585
9620
  }
9586
9621
  ];
9587
9622
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -142558,8 +142593,9 @@ function backfillToolOwnersInChunks(db, result) {
142558
142593
  var databases = new Map;
142559
142594
  var persistenceByDatabase = new WeakMap;
142560
142595
  var persistenceErrorByDatabase = new WeakMap;
142596
+ var pathByDatabase = new WeakMap;
142561
142597
  var lastSchemaFenceRejection = null;
142562
- var LATEST_SUPPORTED_VERSION = 36;
142598
+ var LATEST_SUPPORTED_VERSION = 38;
142563
142599
  function resolveDatabasePath(dbPathOverride) {
142564
142600
  if (dbPathOverride) {
142565
142601
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -142572,6 +142608,9 @@ function resolveDatabasePath(dbPathOverride) {
142572
142608
  const dbDir = getMagicContextStorageDir();
142573
142609
  return { dbDir, dbPath: join4(dbDir, "context.db") };
142574
142610
  }
142611
+ function getDatabasePath(db) {
142612
+ return pathByDatabase.get(db) ?? null;
142613
+ }
142575
142614
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
142576
142615
  if (existsSync3(targetDbPath))
142577
142616
  return;
@@ -143086,6 +143125,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143086
143125
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
143087
143126
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
143088
143127
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
143128
+ emergency_drain_active INTEGER NOT NULL DEFAULT 0,
143129
+ historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
143089
143130
  cached_m0_materialized_at INTEGER,
143090
143131
  cached_m0_session_facts_version INTEGER,
143091
143132
  cached_m0_upgrade_state TEXT,
@@ -143149,6 +143190,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143149
143190
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
143150
143191
  ON historian_runs(status, created_at DESC);
143151
143192
 
143193
+ CREATE TABLE IF NOT EXISTS transform_decisions (
143194
+ session_id TEXT NOT NULL,
143195
+ harness TEXT NOT NULL DEFAULT 'opencode',
143196
+ message_id TEXT NOT NULL,
143197
+ ts_ms INTEGER NOT NULL,
143198
+ decision TEXT NOT NULL,
143199
+ materialized INTEGER NOT NULL DEFAULT 0,
143200
+ materialize_reason TEXT,
143201
+ emergency INTEGER NOT NULL DEFAULT 0,
143202
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
143203
+ dropped_count INTEGER NOT NULL DEFAULT 0,
143204
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143205
+ PRIMARY KEY (session_id, harness, message_id)
143206
+ );
143207
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
143208
+ ON transform_decisions(session_id, harness);
143209
+
143152
143210
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
143153
143211
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
143154
143212
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -143300,6 +143358,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143300
143358
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
143301
143359
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
143302
143360
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
143361
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
143362
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
143303
143363
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
143304
143364
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
143305
143365
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -143378,6 +143438,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143378
143438
  failed_at INTEGER NOT NULL,
143379
143439
  UNIQUE(table_name, row_id)
143380
143440
  );
143441
+ CREATE TABLE IF NOT EXISTS transform_decisions (
143442
+ session_id TEXT NOT NULL,
143443
+ harness TEXT NOT NULL DEFAULT 'opencode',
143444
+ message_id TEXT NOT NULL,
143445
+ ts_ms INTEGER NOT NULL,
143446
+ decision TEXT NOT NULL,
143447
+ materialized INTEGER NOT NULL DEFAULT 0,
143448
+ materialize_reason TEXT,
143449
+ emergency INTEGER NOT NULL DEFAULT 0,
143450
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
143451
+ dropped_count INTEGER NOT NULL DEFAULT 0,
143452
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143453
+ PRIMARY KEY (session_id, harness, message_id)
143454
+ );
143455
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
143456
+ ON transform_decisions(session_id, harness);
143381
143457
  `);
143382
143458
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143383
143459
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -143464,7 +143540,9 @@ function healNullIntegerColumns(db) {
143464
143540
  ["protected_tail_drain_tokens", 0],
143465
143541
  ["recovery_no_eligible_head_count", 0],
143466
143542
  ["force_emergency_bypass_window_start", 0],
143467
- ["force_emergency_bypass_used", 0]
143543
+ ["force_emergency_bypass_used", 0],
143544
+ ["emergency_drain_active", 0],
143545
+ ["historian_drain_failure_at", 0]
143468
143546
  ];
143469
143547
  for (const [column, fallback] of columns) {
143470
143548
  try {
@@ -143540,6 +143618,7 @@ function openDatabase(dbPathOrOptions) {
143540
143618
  setDatabase(db);
143541
143619
  loadToolDefinitionMeasurements(db);
143542
143620
  databases.set(dbPath, db);
143621
+ pathByDatabase.set(db, dbPath);
143543
143622
  persistenceByDatabase.set(db, true);
143544
143623
  persistenceErrorByDatabase.delete(db);
143545
143624
  return db;
@@ -158141,14 +158220,16 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
158141
158220
  }).optional();
158142
158221
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
158143
158222
  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)"),
158144
- 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")
158223
+ 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"),
158224
+ 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: [])`)
158145
158225
  }).optional();
158146
158226
  var BaseEmbeddingConfigSchema = exports_external.object({
158147
158227
  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."),
158148
158228
  model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
158149
158229
  endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
158150
158230
  api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
158151
- 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."),
158231
+ 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."),
158232
+ 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."),
158152
158233
  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."),
158153
158234
  max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
158154
158235
  }).superRefine((data, ctx) => {
@@ -158178,6 +158259,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
158178
158259
  if (data.provider === "openai-compatible") {
158179
158260
  const apiKey = data.api_key?.trim();
158180
158261
  const inputType = data.input_type?.trim();
158262
+ const queryInputType = data.query_input_type?.trim();
158181
158263
  const truncate = data.truncate?.trim();
158182
158264
  return {
158183
158265
  provider: "openai-compatible",
@@ -158185,6 +158267,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
158185
158267
  endpoint: data.endpoint?.trim() ?? "",
158186
158268
  ...apiKey ? { api_key: apiKey } : {},
158187
158269
  ...inputType ? { input_type: inputType } : {},
158270
+ ...queryInputType ? { query_input_type: queryInputType } : {},
158188
158271
  ...truncate ? { truncate } : {},
158189
158272
  ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
158190
158273
  };
@@ -158908,6 +158991,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
158908
158991
  return lines.join(`
158909
158992
  `);
158910
158993
  }
158994
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
158995
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
158996
+ if (!row)
158997
+ return "";
158998
+ const title = typeof row.title === "string" ? row.title.trim() : "";
158999
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
159000
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
159001
+ return [title, body].filter((s) => s.length > 0).join(`
159002
+ `);
159003
+ }
158911
159004
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
158912
159005
  const lines = [];
158913
159006
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -159502,7 +159595,7 @@ class LocalEmbeddingProvider {
159502
159595
  waiter();
159503
159596
  }
159504
159597
  }
159505
- async embed(text, signal) {
159598
+ async embed(text, signal, _purpose) {
159506
159599
  if (signal?.aborted)
159507
159600
  return null;
159508
159601
  if (this.disposing)
@@ -159528,7 +159621,7 @@ class LocalEmbeddingProvider {
159528
159621
  this.finishInFlight();
159529
159622
  }
159530
159623
  }
159531
- async embedBatch(texts, signal) {
159624
+ async embedBatch(texts, signal, _purpose) {
159532
159625
  if (texts.length === 0) {
159533
159626
  return [];
159534
159627
  }
@@ -159667,6 +159760,7 @@ class OpenAICompatibleEmbeddingProvider {
159667
159760
  model;
159668
159761
  apiKey;
159669
159762
  inputType;
159763
+ queryInputType;
159670
159764
  truncate;
159671
159765
  initialized = false;
159672
159766
  failureTimes = [];
@@ -159679,6 +159773,7 @@ class OpenAICompatibleEmbeddingProvider {
159679
159773
  this.model = options.model?.trim() ?? "";
159680
159774
  this.apiKey = options.apiKey?.trim() ?? "";
159681
159775
  this.inputType = options.inputType?.trim() ?? "";
159776
+ this.queryInputType = options.queryInputType?.trim() ?? "";
159682
159777
  this.truncate = options.truncate?.trim() ?? "";
159683
159778
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
159684
159779
  this.modelId = getEmbeddingProviderIdentity({
@@ -159706,11 +159801,17 @@ class OpenAICompatibleEmbeddingProvider {
159706
159801
  this.initialized = true;
159707
159802
  return true;
159708
159803
  }
159709
- async embed(text, signal) {
159710
- const [embedding] = await this.embedBatch([text], signal);
159804
+ resolveInputTypeForPurpose(purpose = "passage") {
159805
+ if (purpose === "query") {
159806
+ return this.queryInputType || this.inputType;
159807
+ }
159808
+ return this.inputType;
159809
+ }
159810
+ async embed(text, signal, purpose) {
159811
+ const [embedding] = await this.embedBatch([text], signal, purpose);
159711
159812
  return embedding ?? null;
159712
159813
  }
159713
- async embedBatch(texts, signal) {
159814
+ async embedBatch(texts, signal, purpose) {
159714
159815
  if (texts.length === 0) {
159715
159816
  return [];
159716
159817
  }
@@ -159736,6 +159837,7 @@ class OpenAICompatibleEmbeddingProvider {
159736
159837
  if (signal) {
159737
159838
  signal.addEventListener("abort", onOuterAbort, { once: true });
159738
159839
  }
159840
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
159739
159841
  const response = await fetch(`${this.endpoint}/embeddings`, {
159740
159842
  method: "POST",
159741
159843
  headers: {
@@ -159745,7 +159847,7 @@ class OpenAICompatibleEmbeddingProvider {
159745
159847
  body: JSON.stringify({
159746
159848
  model: this.model,
159747
159849
  input: texts,
159748
- ...this.inputType ? { input_type: this.inputType } : {},
159850
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
159749
159851
  ...this.truncate ? { truncate: this.truncate } : {}
159750
159852
  }),
159751
159853
  redirect: "error",
@@ -160449,6 +160551,7 @@ function resolveEmbeddingConfig(config2) {
160449
160551
  if (config2.provider === "openai-compatible") {
160450
160552
  const apiKey = config2.api_key?.trim();
160451
160553
  const inputType = config2.input_type?.trim();
160554
+ const queryInputType = config2.query_input_type?.trim();
160452
160555
  const truncate = config2.truncate?.trim();
160453
160556
  return {
160454
160557
  provider: "openai-compatible",
@@ -160456,6 +160559,7 @@ function resolveEmbeddingConfig(config2) {
160456
160559
  endpoint: config2.endpoint.trim(),
160457
160560
  ...apiKey ? { api_key: apiKey } : {},
160458
160561
  ...inputType ? { input_type: inputType } : {},
160562
+ ...queryInputType ? { query_input_type: queryInputType } : {},
160459
160563
  ...truncate ? { truncate } : {},
160460
160564
  ...config2.max_input_tokens ? {
160461
160565
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -160477,6 +160581,7 @@ function createProvider(config2) {
160477
160581
  model: config2.model,
160478
160582
  apiKey: config2.api_key,
160479
160583
  inputType: config2.input_type,
160584
+ queryInputType: config2.query_input_type,
160480
160585
  truncate: config2.truncate,
160481
160586
  maxInputTokens: config2.max_input_tokens
160482
160587
  });
@@ -160659,7 +160764,7 @@ function getOrCreateProjectProvider(registration) {
160659
160764
  registration.provider = provider;
160660
160765
  return provider;
160661
160766
  }
160662
- async function embedTextForProject(projectIdentity, text, signal) {
160767
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
160663
160768
  const registration = projectRegistrations.get(projectIdentity);
160664
160769
  if (!registration)
160665
160770
  return null;
@@ -160668,7 +160773,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
160668
160773
  const provider = getOrCreateProjectProvider(registration);
160669
160774
  if (!provider)
160670
160775
  return null;
160671
- const vector = await provider.embed(text, signal);
160776
+ const vector = await provider.embed(text, signal, purpose);
160672
160777
  if (!vector)
160673
160778
  return null;
160674
160779
  const current = projectRegistrations.get(projectIdentity);
@@ -160677,7 +160782,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
160677
160782
  }
160678
160783
  return { vector, modelId, generation };
160679
160784
  }
160680
- async function embedBatchForProject(projectIdentity, texts, signal) {
160785
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
160681
160786
  if (texts.length === 0) {
160682
160787
  const registration2 = projectRegistrations.get(projectIdentity);
160683
160788
  if (!registration2 || registration2.observationMode)
@@ -160693,7 +160798,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
160693
160798
  const provider = getOrCreateProjectProvider(registration);
160694
160799
  if (!provider)
160695
160800
  return null;
160696
- const vectors = await provider.embedBatch(texts, signal);
160801
+ const vectors = await provider.embedBatch(texts, signal, purpose);
160697
160802
  const current = projectRegistrations.get(projectIdentity);
160698
160803
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
160699
160804
  return null;
@@ -160750,7 +160855,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
160750
160855
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
160751
160856
  const prepared = [];
160752
160857
  for (const candidate of candidates) {
160753
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
160858
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
160754
160859
  if (canonicalText.length === 0) {
160755
160860
  noWork.push(candidate.id);
160756
160861
  continue;
@@ -160959,6 +161064,7 @@ function createProvider2(config2) {
160959
161064
  model: config2.model,
160960
161065
  apiKey: config2.api_key,
160961
161066
  inputType: config2.input_type,
161067
+ queryInputType: config2.query_input_type,
160962
161068
  truncate: config2.truncate,
160963
161069
  maxInputTokens: config2.max_input_tokens
160964
161070
  });
@@ -161194,6 +161300,8 @@ var SESSION_META_SELECT_COLUMNS = [
161194
161300
  "recovery_no_eligible_head_count",
161195
161301
  "force_emergency_bypass_window_start",
161196
161302
  "force_emergency_bypass_used",
161303
+ "emergency_drain_active",
161304
+ "historian_drain_failure_at",
161197
161305
  "upgrade_reminded_at",
161198
161306
  "pi_stable_id_scheme"
161199
161307
  ];
@@ -161243,6 +161351,8 @@ var META_COLUMNS = {
161243
161351
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
161244
161352
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
161245
161353
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
161354
+ emergencyDrainActive: "emergency_drain_active",
161355
+ historianDrainFailureAt: "historian_drain_failure_at",
161246
161356
  upgradeRemindedAt: "upgrade_reminded_at",
161247
161357
  piStableIdScheme: "pi_stable_id_scheme"
161248
161358
  };
@@ -161944,6 +162054,24 @@ class ToolMutationBatch {
161944
162054
  }
161945
162055
 
161946
162056
  // ../plugin/src/hooks/magic-context/read-session-chunk.ts
162057
+ var BLOCK_TOKEN_MEMO_MAX = 2048;
162058
+ var blockTokenMemo = new Map;
162059
+ function estimateBlockTokens(blockText) {
162060
+ const cached2 = blockTokenMemo.get(blockText);
162061
+ if (cached2 !== undefined) {
162062
+ blockTokenMemo.delete(blockText);
162063
+ blockTokenMemo.set(blockText, cached2);
162064
+ return cached2;
162065
+ }
162066
+ const count = estimateTokens(blockText);
162067
+ if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
162068
+ const oldest = blockTokenMemo.keys().next().value;
162069
+ if (oldest !== undefined)
162070
+ blockTokenMemo.delete(oldest);
162071
+ }
162072
+ blockTokenMemo.set(blockText, count);
162073
+ return count;
162074
+ }
161947
162075
  var activeRawMessageCache = null;
161948
162076
  var activeAbsoluteCountCache = null;
161949
162077
  var sessionProviders = new Map;
@@ -162126,7 +162254,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
162126
162254
  if (!currentBlock)
162127
162255
  return true;
162128
162256
  const blockText = formatBlock(currentBlock);
162129
- const blockTokens = estimateTokens(blockText);
162257
+ const blockTokens = estimateBlockTokens(blockText);
162130
162258
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
162131
162259
  return false;
162132
162260
  }
@@ -168076,7 +168204,9 @@ var DEFAULT_PROTECTED_TAIL_META = {
168076
168204
  protectedTailDrainTokens: 0,
168077
168205
  recoveryNoEligibleHeadCount: 0,
168078
168206
  forceEmergencyBypassWindowStart: 0,
168079
- forceEmergencyBypassUsed: 0
168207
+ forceEmergencyBypassUsed: 0,
168208
+ emergencyDrainActive: 0,
168209
+ historianDrainFailureAt: 0
168080
168210
  };
168081
168211
  function toProtectedTailMeta(row) {
168082
168212
  if (row === null || typeof row !== "object")
@@ -168090,7 +168220,9 @@ function toProtectedTailMeta(row) {
168090
168220
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
168091
168221
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
168092
168222
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
168093
- forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
168223
+ forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
168224
+ emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
168225
+ historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
168094
168226
  };
168095
168227
  }
168096
168228
  function loadProtectedTailMeta(db, sessionId) {
@@ -168098,7 +168230,7 @@ function loadProtectedTailMeta(db, sessionId) {
168098
168230
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
168099
168231
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
168100
168232
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
168101
- force_emergency_bypass_used
168233
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
168102
168234
  FROM session_meta WHERE session_id = ?`).get(sessionId);
168103
168235
  return toProtectedTailMeta(row);
168104
168236
  }
@@ -168142,6 +168274,17 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
168142
168274
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
168143
168275
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
168144
168276
  }
168277
+ var EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95;
168278
+ var EMERGENCY_DRAIN_EXIT_MARGIN = 10;
168279
+ var EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55;
168280
+ var EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000;
168281
+ var EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
168282
+ function emergencyDrainExitThreshold(executeThresholdPercentage) {
168283
+ if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
168284
+ return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
168285
+ }
168286
+ return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
168287
+ }
168145
168288
  function reserveProtectedTailDrainTokens(args) {
168146
168289
  const now = args.now ?? Date.now();
168147
168290
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -168160,18 +168303,30 @@ function reserveProtectedTailDrainTokens(args) {
168160
168303
  let meta3 = loadProtectedTailMeta(args.db, args.sessionId);
168161
168304
  if (now - meta3.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
168162
168305
  args.db.prepare(`UPDATE session_meta
168163
- SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0,
168164
- force_emergency_bypass_window_start = ?, force_emergency_bypass_used = 0
168165
- WHERE session_id = ?`).run(now, now, args.sessionId);
168306
+ SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
168307
+ WHERE session_id = ?`).run(now, args.sessionId);
168166
168308
  meta3 = loadProtectedTailMeta(args.db, args.sessionId);
168167
168309
  }
168310
+ const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
168311
+ let latchActiveSince = meta3.emergencyDrainActive;
168312
+ if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
168313
+ if (latchActiveSince <= 0)
168314
+ latchActiveSince = now;
168315
+ } else if (latchActiveSince > 0) {
168316
+ const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
168317
+ if (args.usagePercentage < exitThreshold || expired)
168318
+ latchActiveSince = 0;
168319
+ }
168320
+ if (latchActiveSince !== meta3.emergencyDrainActive) {
168321
+ args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
168322
+ }
168323
+ const latchActive = latchActiveSince > 0;
168168
168324
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
168169
168325
  const remaining = Math.max(0, budget - meta3.protectedTailDrainTokens);
168170
168326
  let reserved = Math.min(requested, args.perRunCap, remaining);
168171
168327
  let bypass = false;
168172
- const bypassWindowExpired = now - meta3.forceEmergencyBypassWindowStart > DRAIN_WINDOW_MS;
168173
- const bypassUsed = bypassWindowExpired ? 0 : meta3.forceEmergencyBypassUsed;
168174
- if (reserved <= 0 && args.usagePercentage >= 95 && bypassUsed === 0) {
168328
+ const inFailureBackoff = meta3.historianDrainFailureAt > 0 && now - meta3.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
168329
+ if (reserved <= 0 && latchActive && !inFailureBackoff) {
168175
168330
  reserved = Math.min(requested, args.perRunCap);
168176
168331
  bypass = true;
168177
168332
  }
@@ -168179,10 +168334,8 @@ function reserveProtectedTailDrainTokens(args) {
168179
168334
  return;
168180
168335
  args.db.prepare(`UPDATE session_meta
168181
168336
  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,
168182
- protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?,
168183
- force_emergency_bypass_window_start = CASE WHEN ? THEN ? ELSE force_emergency_bypass_window_start END,
168184
- force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
168185
- WHERE session_id = ?`).run(now, reserved, bypass ? 1 : 0, now, bypass ? 1 : 0, args.sessionId);
168337
+ protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
168338
+ WHERE session_id = ?`).run(now, reserved, args.sessionId);
168186
168339
  result = {
168187
168340
  ok: true,
168188
168341
  reservedTokens: reserved,
@@ -168192,6 +168345,25 @@ function reserveProtectedTailDrainTokens(args) {
168192
168345
  })();
168193
168346
  return result;
168194
168347
  }
168348
+ function clearEmergencyDrainLatch(db, sessionId) {
168349
+ db.transaction(() => {
168350
+ ensureSessionMetaRow(db, sessionId);
168351
+ db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
168352
+ })();
168353
+ }
168354
+ function recordHistorianDrainFailure(db, sessionId, now) {
168355
+ const ts = now ?? Date.now();
168356
+ db.transaction(() => {
168357
+ ensureSessionMetaRow(db, sessionId);
168358
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
168359
+ })();
168360
+ }
168361
+ function clearHistorianDrainFailure(db, sessionId) {
168362
+ db.transaction(() => {
168363
+ ensureSessionMetaRow(db, sessionId);
168364
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
168365
+ })();
168366
+ }
168195
168367
  function rollbackProtectedTailDrainReservation(db, reservation) {
168196
168368
  if (!reservation || reservation.tokens <= 0)
168197
168369
  return;
@@ -169213,12 +169385,17 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
169213
169385
  toolName: typeof row.tool_name === "string" ? row.tool_name : null
169214
169386
  }));
169215
169387
  }
169216
- function getTriggerTagTokenUpperBound(db, sessionId) {
169217
- const row = db.prepare(`SELECT
169388
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
169389
+ const sql = floor > 0 ? `SELECT
169218
169390
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
169219
169391
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
169220
169392
  FROM tags
169221
- WHERE session_id = ? AND status IN ('active', 'dropped')`).get(sessionId);
169393
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
169394
+ COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
169395
+ COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
169396
+ FROM tags
169397
+ WHERE session_id = ? AND status IN ('active', 'dropped')`;
169398
+ const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
169222
169399
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
169223
169400
  }
169224
169401
  function updateTagInputByteSize(db, sessionId, tagNumber, newInputByteSize) {
@@ -169245,10 +169422,12 @@ function getUpdateTagInputTokenCountStatement(db) {
169245
169422
  function updateTagTokenCount(db, sessionId, tagNumber, newTokenCount) {
169246
169423
  getUpdateTagTokenCountStatement(db).run(newTokenCount, sessionId, tagNumber);
169247
169424
  }
169248
- function getAllStatusTagTokenTotalsFlat(db, sessionId) {
169249
- const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
169250
- FROM tags
169251
- WHERE session_id = ?`).all(sessionId);
169425
+ function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
169426
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
169427
+ FROM tags
169428
+ 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
169429
+ FROM tags
169430
+ WHERE session_id = ?`).all(sessionId);
169252
169431
  const totals = new Map;
169253
169432
  const nullMessageIds = new Set;
169254
169433
  for (const row of rows) {
@@ -169367,6 +169546,52 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
169367
169546
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
169368
169547
  return isTagNumberRow(row) ? row.tag_number : null;
169369
169548
  }
169549
+ var getMinMessageTagNumberForRawIdStatements = new WeakMap;
169550
+ function isMinTagNumberRow(row) {
169551
+ return row !== null && typeof row === "object" && "m" in row;
169552
+ }
169553
+ function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
169554
+ if (rawId.includes(":"))
169555
+ return null;
169556
+ let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
169557
+ if (!stmt) {
169558
+ stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
169559
+ getMinMessageTagNumberForRawIdStatements.set(db, stmt);
169560
+ }
169561
+ const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
169562
+ return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
169563
+ }
169564
+ var TAGGER_FLOOR_SCAN_MESSAGES = 8;
169565
+ var TAGGER_FLOOR_MAX_PROBES = 64;
169566
+ var TAGGER_FLOOR_SAFETY_MARGIN = 256;
169567
+ var TAGGER_FLOOR_PER_SKIP_MARGIN = 64;
169568
+ function deriveTagLoadFloor(db, sessionId, rawIds) {
169569
+ let min = Number.POSITIVE_INFINITY;
169570
+ let probes = 0;
169571
+ let hits = 0;
169572
+ let skippedBeforeFirstHit = 0;
169573
+ for (const rawId of rawIds) {
169574
+ if (typeof rawId !== "string" || rawId.length === 0)
169575
+ continue;
169576
+ if (probes >= TAGGER_FLOOR_MAX_PROBES)
169577
+ break;
169578
+ probes++;
169579
+ const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
169580
+ if (m === null) {
169581
+ if (hits === 0)
169582
+ skippedBeforeFirstHit++;
169583
+ continue;
169584
+ }
169585
+ if (m < min)
169586
+ min = m;
169587
+ if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
169588
+ break;
169589
+ }
169590
+ if (!Number.isFinite(min))
169591
+ return 0;
169592
+ const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
169593
+ return Math.max(0, min - margin);
169594
+ }
169370
169595
  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";
169371
169596
  function getTagsBySession(db, sessionId) {
169372
169597
  const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
@@ -172105,7 +172330,7 @@ function createCtxSearchTool(deps) {
172105
172330
  memoryEnabled,
172106
172331
  embeddingEnabled,
172107
172332
  embedQuery: async (text, signal) => {
172108
- const result = await embedTextForProject(projectIdentity, text, signal);
172333
+ const result = await embedTextForProject(projectIdentity, text, signal, "query");
172109
172334
  return result?.vector ?? null;
172110
172335
  },
172111
172336
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfx/pi-magic-context",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "description": "Pi coding agent extension for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "README.md"
27
27
  ],
28
28
  "scripts": {
29
- "build": "bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external better-sqlite3",
29
+ "build": "bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external node:sqlite",
30
30
  "typecheck": "tsc --noEmit",
31
31
  "test": "bun test",
32
32
  "lint": "biome check src",
@@ -49,7 +49,6 @@
49
49
  "@earendil-works/pi-tui": "^0.74.0",
50
50
  "@types/better-sqlite3": "^7.6.13",
51
51
  "@types/bun": "^1.3.10",
52
- "better-sqlite3": "^12.9.0",
53
52
  "@types/node": "^22.0.0",
54
53
  "typescript": "^5.8.0"
55
54
  },