@wolfx/pi-magic-context 0.24.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -146668,15 +146668,22 @@ function ownerMessageIdForTagRow(row) {
146668
146668
  }
146669
146669
  return row.message_id.replace(CONTENT_ID_SUFFIX, "");
146670
146670
  }
146671
- function getActiveTagTokenAggregate(db, sessionId) {
146672
- const row = db.prepare(`SELECT
146671
+ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
146672
+ const toolOutputExpr = protectedTags > 0 ? `COALESCE(SUM(CASE WHEN type = 'tool' AND tag_number < (
146673
+ SELECT tag_number FROM tags
146674
+ WHERE session_id = ? AND status = 'active'
146675
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
146676
+ ) THEN COALESCE(token_count, 0) ELSE 0 END), 0)` : `COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)`;
146677
+ const sql = `SELECT
146673
146678
  COALESCE(SUM(CASE WHEN type != 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)
146674
146679
  + COALESCE(SUM(COALESCE(reasoning_token_count, 0)), 0) AS conversation,
146675
146680
  COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) + COALESCE(input_token_count, 0) ELSE 0 END), 0) AS tool_call,
146676
- COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0) AS tool_output,
146681
+ ${toolOutputExpr} AS tool_output,
146677
146682
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
146678
146683
  FROM tags
146679
- WHERE session_id = ? AND status = 'active'`).get(sessionId);
146684
+ WHERE session_id = ? AND status = 'active'`;
146685
+ const params = protectedTags > 0 ? [sessionId, protectedTags - 1, sessionId] : [sessionId];
146686
+ const row = db.prepare(sql).get(...params);
146680
146687
  return {
146681
146688
  conversation: row?.conversation ?? 0,
146682
146689
  toolCall: row?.tool_call ?? 0,
@@ -166296,6 +166303,7 @@ init_logger();
166296
166303
  // ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
166297
166304
  import { createHash as createHash8 } from "node:crypto";
166298
166305
  var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512;
166306
+ var CHUNK_WINDOW_SAFETY_RATIO = 0.9;
166299
166307
  var loadFtsRowsStatements = new WeakMap;
166300
166308
  var existingHashStatements = new WeakMap;
166301
166309
  var existingHashByProjectStatements = new WeakMap;
@@ -166531,9 +166539,10 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
166531
166539
  if (lines.length === 0 || endOrdinal < startOrdinal)
166532
166540
  return [];
166533
166541
  const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
166542
+ const effectiveMax = Math.max(1, Math.floor(normalizedMax * CHUNK_WINDOW_SAFETY_RATIO));
166534
166543
  const fullText = lines.join(`
166535
166544
  `);
166536
- if (estimateTokens(fullText) <= normalizedMax) {
166545
+ if (estimateTokens(fullText) <= effectiveMax) {
166537
166546
  return [
166538
166547
  {
166539
166548
  windowIndex: 0,
@@ -166571,7 +166580,7 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
166571
166580
  const lineStart = range?.start ?? startOrdinal;
166572
166581
  const lineEnd = range?.end ?? lineStart;
166573
166582
  const lineTokens = estimateTokens(line);
166574
- if (currentLines.length > 0 && currentTokens + lineTokens > normalizedMax) {
166583
+ if (currentLines.length > 0 && currentTokens + lineTokens > effectiveMax) {
166575
166584
  flush2();
166576
166585
  }
166577
166586
  if (currentLines.length === 0) {
@@ -167171,6 +167180,13 @@ function blockedEmbeddingEndpointReason(endpoint) {
167171
167180
  function normalizeEndpoint2(endpoint) {
167172
167181
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
167173
167182
  }
167183
+ function embeddingModelsMatch(served, requested) {
167184
+ const a = served.trim().toLowerCase();
167185
+ const b = requested.trim().toLowerCase();
167186
+ if (a.length === 0 || b.length === 0)
167187
+ return true;
167188
+ return a === b || a.includes(b) || b.includes(a);
167189
+ }
167174
167190
  var FAILURE_THRESHOLD = 3;
167175
167191
  var FAILURE_WINDOW_MS = 60000;
167176
167192
  var OPEN_DURATION_MS = 5 * 60000;
@@ -167188,6 +167204,7 @@ class OpenAICompatibleEmbeddingProvider {
167188
167204
  failureTimes = [];
167189
167205
  circuitOpenUntil = 0;
167190
167206
  openLogged = false;
167207
+ modelMismatchLogged = false;
167191
167208
  halfOpenProbeInFlight = false;
167192
167209
  constructor(options) {
167193
167210
  this.endpoint = normalizeEndpoint2(options.endpoint);
@@ -167286,6 +167303,15 @@ class OpenAICompatibleEmbeddingProvider {
167286
167303
  this.recordFailure(isProbe);
167287
167304
  return Array.from({ length: texts.length }, () => null);
167288
167305
  }
167306
+ const servedModel = typeof body.model === "string" ? body.model : "";
167307
+ if (this.model && servedModel && !embeddingModelsMatch(servedModel, this.model)) {
167308
+ if (!this.modelMismatchLogged) {
167309
+ log(`[magic-context] embedding endpoint served a DIFFERENT model than requested — refusing the substituted vectors (they have the wrong dimensions/space). requested="${this.model}" served="${servedModel}". The endpoint likely substituted a loaded model; load/select "${this.model}" on the endpoint, or set embedding.model to the served model.`);
167310
+ this.modelMismatchLogged = true;
167311
+ }
167312
+ this.recordFailure(isProbe);
167313
+ return Array.from({ length: texts.length }, () => null);
167314
+ }
167289
167315
  const items = Array.isArray(body.data) ? body.data : [];
167290
167316
  const results = Array.from({ length: texts.length }, (_, index) => {
167291
167317
  const embedding = items[index]?.embedding;
@@ -167768,7 +167794,7 @@ function getChunkEmbeddingModelId(config2, providerIdentity) {
167768
167794
  }
167769
167795
  const chunkIdentity = {
167770
167796
  providerIdentity,
167771
- chunkerVersion: 1,
167797
+ chunkerVersion: 2,
167772
167798
  maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
167773
167799
  truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
167774
167800
  };
@@ -170312,6 +170338,51 @@ function computePiWorkMetrics(sessionEntries) {
170312
170338
  return { newWorkTokens, totalInputTokens };
170313
170339
  }
170314
170340
 
170341
+ // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
170342
+ var SYSTEM_INJECTION_MARKERS = [
170343
+ "<!-- OMO_INTERNAL_INITIATOR -->",
170344
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
170345
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
170346
+ "[Category+Skill Reminder]",
170347
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
170348
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
170349
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
170350
+ "Unstable background agent appears idle",
170351
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
170352
+ ];
170353
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
170354
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
170355
+ function stripSystemInjection(text) {
170356
+ let hasInjection = false;
170357
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
170358
+ if (text.includes(marker)) {
170359
+ hasInjection = true;
170360
+ break;
170361
+ }
170362
+ }
170363
+ if (SYSTEM_REMINDER_REGEX.test(text))
170364
+ hasInjection = true;
170365
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
170366
+ if (!hasInjection)
170367
+ return null;
170368
+ let cleaned = text;
170369
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
170370
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
170371
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
170372
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
170373
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
170374
+ continue;
170375
+ const idx = cleaned.indexOf(marker);
170376
+ if (idx === -1)
170377
+ continue;
170378
+ const blockEnd = cleaned.indexOf(`
170379
+
170380
+ `, idx + marker.length);
170381
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
170382
+ }
170383
+ return cleaned.trim();
170384
+ }
170385
+
170315
170386
  // ../plugin/src/hooks/magic-context/apply-operations.ts
170316
170387
  var USER_DROP_PREVIEW_CHARS = 250;
170317
170388
  var RECENT_TOOL_SKELETON_WINDOW = 20;
@@ -170321,6 +170392,10 @@ function buildReplacementContent(tagId, target) {
170321
170392
  return `[dropped §${tagId}§]`;
170322
170393
  }
170323
170394
  const currentContent = target.getContent?.() ?? "";
170395
+ const strippedInjection = stripSystemInjection(currentContent);
170396
+ if (strippedInjection !== null && stripTagPrefix(strippedInjection).trim().length === 0) {
170397
+ return `[dropped §${tagId}§]`;
170398
+ }
170324
170399
  const originalText = stripTagPrefix(currentContent);
170325
170400
  if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
170326
170401
  return `[truncated §${tagId}§]
@@ -171929,8 +172004,8 @@ var CHANNEL1_SENTINEL = "<system-reminder>";
171929
172004
  var TOKENS_PER_BYTE = 0.25;
171930
172005
  var CHANNEL1_FLOOR_TOKENS = 1e4;
171931
172006
  var CHANNEL1_REFIRE_FLOOR_TOKENS = 1e4;
171932
- function channel1RefireTokens(historyBudgetTokens) {
171933
- const scaled = Math.round(0.05 * Math.max(0, historyBudgetTokens));
172007
+ function channel1RefireTokens(workingWindowTokens) {
172008
+ const scaled = Math.round(0.05 * Math.max(0, workingWindowTokens));
171934
172009
  return Math.max(CHANNEL1_REFIRE_FLOOR_TOKENS, scaled);
171935
172010
  }
171936
172011
  var S_GENTLE = 0.2;
@@ -171946,7 +172021,7 @@ function toolOutputTokens(output) {
171946
172021
  return Math.round(byteSize(output) * TOKENS_PER_BYTE);
171947
172022
  }
171948
172023
  function decideChannel1(input) {
171949
- const { undroppedTokens, pressure, historyBudgetTokens, hasRecentReduce } = input;
172024
+ const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
171950
172025
  const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
171951
172026
  const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
171952
172027
  const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
@@ -171961,7 +172036,7 @@ function decideChannel1(input) {
171961
172036
  return quiet();
171962
172037
  if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
171963
172038
  return quiet();
171964
- const budget = historyBudgetTokens > 0 ? historyBudgetTokens : undroppedTokens || 1;
172039
+ const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
171965
172040
  const severity = undroppedTokens / budget * pressure;
171966
172041
  if (severity < S_GENTLE)
171967
172042
  return quiet();
@@ -171973,7 +172048,7 @@ function decideChannel1(input) {
171973
172048
  else
171974
172049
  level = "gentle";
171975
172050
  if (lastLevel === "") {
171976
- if (undroppedTokens < lastNudge + channel1RefireTokens(historyBudgetTokens)) {
172051
+ if (undroppedTokens < lastNudge + channel1RefireTokens(workingWindowTokens)) {
171977
172052
  return quiet();
171978
172053
  }
171979
172054
  } else if (LEVEL_RANK[level] <= LEVEL_RANK[lastLevel]) {
@@ -172018,13 +172093,13 @@ function buildChannel1Reminder(level, undroppedTokens) {
172018
172093
  let body;
172019
172094
  switch (level) {
172020
172095
  case "gentle":
172021
- body = `You have ~${amount} tokens of tool output you have not reduced. ` + `Once you are done with earlier outputs, drop them with ctx_reduce to keep context lean.`;
172096
+ body = `You have ~${amount} tokens of tool output you have not reduced. ` + `When you are done with earlier outputs, dropping them with ctx_reduce keeps context lean.`;
172022
172097
  break;
172023
172098
  case "firm":
172024
- body = `~${amount} tokens of unreduced tool output is accumulating. ` + `Drop what you have already processed with ctx_reduce before continuing.`;
172099
+ body = `~${amount} tokens of unreduced tool output has built up. ` + `At your next natural stopping point, consider dropping what you have already processed with ctx_reduce.`;
172025
172100
  break;
172026
172101
  case "urgent":
172027
- body = `~${amount} tokens of unreduced tool output remain. ` + `A large span of this session will be comparted soon; drop spent outputs with ctx_reduce first so the archived span is the part that matters.`;
172102
+ body = `~${amount} tokens of unreduced tool output remain, and a large span of this session will be comparted before long. ` + `Consider dropping spent outputs with ctx_reduce so the archived span is the part that matters.`;
172028
172103
  break;
172029
172104
  }
172030
172105
  return `
@@ -174343,10 +174418,11 @@ function maybeChannel1ReminderForToolResult(args) {
174343
174418
  contextLimit: state.contextLimit,
174344
174419
  executeThresholdPercentage: state.executeThresholdPercentage
174345
174420
  });
174421
+ const workingWindowTokens = Math.round(state.contextLimit * state.executeThresholdPercentage / 100);
174346
174422
  const decision = decideChannel1({
174347
174423
  undroppedTokens,
174348
174424
  pressure,
174349
- historyBudgetTokens: state.historyBudgetTokens,
174425
+ workingWindowTokens,
174350
174426
  lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
174351
174427
  lastNudgeLevel: getLastNudgeLevel(db, sessionId),
174352
174428
  hasRecentReduce: false
@@ -174581,51 +174657,6 @@ function planEmergencyDrop(input) {
174581
174657
  };
174582
174658
  }
174583
174659
 
174584
- // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
174585
- var SYSTEM_INJECTION_MARKERS = [
174586
- "<!-- OMO_INTERNAL_INITIATOR -->",
174587
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
174588
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
174589
- "[Category+Skill Reminder]",
174590
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
174591
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
174592
- "[EMERGENCY CONTEXT WINDOW WARNING]",
174593
- "Unstable background agent appears idle",
174594
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
174595
- ];
174596
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
174597
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
174598
- function stripSystemInjection(text) {
174599
- let hasInjection = false;
174600
- for (const marker of SYSTEM_INJECTION_MARKERS) {
174601
- if (text.includes(marker)) {
174602
- hasInjection = true;
174603
- break;
174604
- }
174605
- }
174606
- if (SYSTEM_REMINDER_REGEX.test(text))
174607
- hasInjection = true;
174608
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
174609
- if (!hasInjection)
174610
- return null;
174611
- let cleaned = text;
174612
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
174613
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
174614
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
174615
- for (const marker of SYSTEM_INJECTION_MARKERS) {
174616
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
174617
- continue;
174618
- const idx = cleaned.indexOf(marker);
174619
- if (idx === -1)
174620
- continue;
174621
- const blockEnd = cleaned.indexOf(`
174622
-
174623
- `, idx + marker.length);
174624
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
174625
- }
174626
- return cleaned.trim();
174627
- }
174628
-
174629
174660
  // src/heuristic-cleanup-pi.ts
174630
174661
  init_logger();
174631
174662
  var DEDUP_SAFE_TOOLS = new Set([
@@ -181745,7 +181776,7 @@ function registerPiContextHandler(pi, baseOptions) {
181745
181776
  let tailToolTokens;
181746
181777
  let liveTailTokens;
181747
181778
  try {
181748
- const agg = getActiveTagTokenAggregate(options.db, sessionId);
181779
+ const agg = getActiveTagTokenAggregate(options.db, sessionId, options.protectedTags ?? 20);
181749
181780
  tailToolTokens = agg.toolOutput;
181750
181781
  liveTailTokens = agg.conversation + agg.toolCall;
181751
181782
  } catch {
@@ -182098,6 +182129,15 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
182098
182129
  const trigger = checkCompartmentTrigger(db, sessionId, sessionMeta, usage, 0, triggerInputs.executeThresholdPercentage, triggerInputs.triggerBudget, triggerInputs.clearReasoningAge, triggerInputs.commitClusterTrigger, args.activeTags, boundaryContextLimit);
182099
182130
  if (!trigger.shouldFire) {
182100
182131
  sessionLog(sessionId, `historian trigger eval: shouldFire=false (no trigger condition met)`);
182132
+ try {
182133
+ const overflowState = getOverflowState(db, sessionId);
182134
+ if (overflowState.needsEmergencyRecovery && usage.percentage < FORCE_MATERIALIZATION_PERCENTAGE && !inFlightHistorian.has(sessionId) && !hasRunnableCompartmentWindow(boundarySnapshot)) {
182135
+ clearEmergencyRecovery(db, sessionId);
182136
+ sessionLog(sessionId, `historian: disarming stale emergency recovery — real pressure ${usage.percentage.toFixed(1)}% with no runnable compartment window (would otherwise bump to 95% every pass)`);
182137
+ }
182138
+ } catch (err) {
182139
+ sessionLog(sessionId, `historian: emergency-recovery disarm check failed: ${err instanceof Error ? err.message : String(err)}`);
182140
+ }
182101
182141
  return;
182102
182142
  }
182103
182143
  triggered = true;
@@ -184478,6 +184518,11 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
184478
184518
  fallbackModelId: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined
184479
184519
  }, parsed.kind === "partial" ? { range: parsed.range } : {});
184480
184520
  if (result.published) {
184521
+ try {
184522
+ clearEmergencyRecovery(deps.db, sessionId);
184523
+ } catch (recoveryError) {
184524
+ sessionLog(sessionId, `/ctx-recomp: clearEmergencyRecovery failed (continuing): ${describeError(recoveryError).brief}`);
184525
+ }
184481
184526
  try {
184482
184527
  stagePiRecompMarker({ db: deps.db, sessionId, ctx });
184483
184528
  } catch (markerError) {
@@ -185131,7 +185176,7 @@ function formatThresholdPercent(value) {
185131
185176
  // package.json
185132
185177
  var package_default = {
185133
185178
  name: "@wolfx/pi-magic-context",
185134
- version: "0.24.0",
185179
+ version: "0.24.1",
185135
185180
  type: "module",
185136
185181
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
185137
185182
  main: "dist/index.js",
@@ -158709,6 +158709,7 @@ init_logger();
158709
158709
  // ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
158710
158710
  import { createHash as createHash4 } from "node:crypto";
158711
158711
  var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512;
158712
+ var CHUNK_WINDOW_SAFETY_RATIO = 0.9;
158712
158713
  var loadFtsRowsStatements = new WeakMap;
158713
158714
  var existingHashStatements = new WeakMap;
158714
158715
  var existingHashByProjectStatements = new WeakMap;
@@ -158944,9 +158945,10 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
158944
158945
  if (lines.length === 0 || endOrdinal < startOrdinal)
158945
158946
  return [];
158946
158947
  const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
158948
+ const effectiveMax = Math.max(1, Math.floor(normalizedMax * CHUNK_WINDOW_SAFETY_RATIO));
158947
158949
  const fullText = lines.join(`
158948
158950
  `);
158949
- if (estimateTokens(fullText) <= normalizedMax) {
158951
+ if (estimateTokens(fullText) <= effectiveMax) {
158950
158952
  return [
158951
158953
  {
158952
158954
  windowIndex: 0,
@@ -158984,7 +158986,7 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
158984
158986
  const lineStart = range?.start ?? startOrdinal;
158985
158987
  const lineEnd = range?.end ?? lineStart;
158986
158988
  const lineTokens = estimateTokens(line);
158987
- if (currentLines.length > 0 && currentTokens + lineTokens > normalizedMax) {
158989
+ if (currentLines.length > 0 && currentTokens + lineTokens > effectiveMax) {
158988
158990
  flush2();
158989
158991
  }
158990
158992
  if (currentLines.length === 0) {
@@ -159594,6 +159596,13 @@ function blockedEmbeddingEndpointReason(endpoint) {
159594
159596
  function normalizeEndpoint3(endpoint) {
159595
159597
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
159596
159598
  }
159599
+ function embeddingModelsMatch(served, requested) {
159600
+ const a = served.trim().toLowerCase();
159601
+ const b = requested.trim().toLowerCase();
159602
+ if (a.length === 0 || b.length === 0)
159603
+ return true;
159604
+ return a === b || a.includes(b) || b.includes(a);
159605
+ }
159597
159606
  var FAILURE_THRESHOLD = 3;
159598
159607
  var FAILURE_WINDOW_MS = 60000;
159599
159608
  var OPEN_DURATION_MS = 5 * 60000;
@@ -159611,6 +159620,7 @@ class OpenAICompatibleEmbeddingProvider {
159611
159620
  failureTimes = [];
159612
159621
  circuitOpenUntil = 0;
159613
159622
  openLogged = false;
159623
+ modelMismatchLogged = false;
159614
159624
  halfOpenProbeInFlight = false;
159615
159625
  constructor(options) {
159616
159626
  this.endpoint = normalizeEndpoint3(options.endpoint);
@@ -159709,6 +159719,15 @@ class OpenAICompatibleEmbeddingProvider {
159709
159719
  this.recordFailure(isProbe);
159710
159720
  return Array.from({ length: texts.length }, () => null);
159711
159721
  }
159722
+ const servedModel = typeof body.model === "string" ? body.model : "";
159723
+ if (this.model && servedModel && !embeddingModelsMatch(servedModel, this.model)) {
159724
+ if (!this.modelMismatchLogged) {
159725
+ log(`[magic-context] embedding endpoint served a DIFFERENT model than requested — refusing the substituted vectors (they have the wrong dimensions/space). requested="${this.model}" served="${servedModel}". The endpoint likely substituted a loaded model; load/select "${this.model}" on the endpoint, or set embedding.model to the served model.`);
159726
+ this.modelMismatchLogged = true;
159727
+ }
159728
+ this.recordFailure(isProbe);
159729
+ return Array.from({ length: texts.length }, () => null);
159730
+ }
159712
159731
  const items = Array.isArray(body.data) ? body.data : [];
159713
159732
  const results = Array.from({ length: texts.length }, (_, index) => {
159714
159733
  const embedding = items[index]?.embedding;
@@ -160307,7 +160326,7 @@ function getChunkEmbeddingModelId(config2, providerIdentity) {
160307
160326
  }
160308
160327
  const chunkIdentity = {
160309
160328
  providerIdentity,
160310
- chunkerVersion: 1,
160329
+ chunkerVersion: 2,
160311
160330
  maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
160312
160331
  truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
160313
160332
  };
@@ -168674,15 +168693,22 @@ function ownerMessageIdForTagRow(row) {
168674
168693
  }
168675
168694
  return row.message_id.replace(CONTENT_ID_SUFFIX, "");
168676
168695
  }
168677
- function getActiveTagTokenAggregate(db, sessionId) {
168678
- const row = db.prepare(`SELECT
168696
+ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
168697
+ const toolOutputExpr = protectedTags > 0 ? `COALESCE(SUM(CASE WHEN type = 'tool' AND tag_number < (
168698
+ SELECT tag_number FROM tags
168699
+ WHERE session_id = ? AND status = 'active'
168700
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
168701
+ ) THEN COALESCE(token_count, 0) ELSE 0 END), 0)` : `COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)`;
168702
+ const sql = `SELECT
168679
168703
  COALESCE(SUM(CASE WHEN type != 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)
168680
168704
  + COALESCE(SUM(COALESCE(reasoning_token_count, 0)), 0) AS conversation,
168681
168705
  COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) + COALESCE(input_token_count, 0) ELSE 0 END), 0) AS tool_call,
168682
- COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0) AS tool_output,
168706
+ ${toolOutputExpr} AS tool_output,
168683
168707
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
168684
168708
  FROM tags
168685
- WHERE session_id = ? AND status = 'active'`).get(sessionId);
168709
+ WHERE session_id = ? AND status = 'active'`;
168710
+ const params = protectedTags > 0 ? [sessionId, protectedTags - 1, sessionId] : [sessionId];
168711
+ const row = db.prepare(sql).get(...params);
168686
168712
  return {
168687
168713
  conversation: row?.conversation ?? 0,
168688
168714
  toolCall: row?.tool_call ?? 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfx/pi-magic-context",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
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",