@retaw106/mmc-pi 2.3.0 → 2.4.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
@@ -50,11 +50,15 @@ import {
50
50
  stripTagPrefix,
51
51
  peelLeadingMcTagNotation,
52
52
  prependTag,
53
+ rememberCurrentToolOutputCount,
53
54
  isRecord,
54
55
  TEXT_TAG_IDENTITY_MARKER,
55
56
  PI_FALLBACK_PREFIXES_JSON,
56
57
  contentTagOwnerMessageId,
58
+ TailHygieneJsonMemo,
59
+ piJsonHash,
57
60
  estimateImageTokensFromDataUrl,
61
+ providerVisibleMessage,
58
62
  TODO_STATUSES,
59
63
  TODO_PRIORITIES,
60
64
  TITLE_DONE_STATUSES,
@@ -105,6 +109,7 @@ import {
105
109
  backfillTagTokenCounts,
106
110
  insertTag,
107
111
  updateTagStatus,
112
+ markTagDropped,
108
113
  getInertWhitespaceAssistantTagsByNumbers,
109
114
  updateTagDropMode,
110
115
  updateCavemanDepth,
@@ -139,7 +144,6 @@ import {
139
144
  withReadOnlySessionDb,
140
145
  expandToolPart,
141
146
  retreatPastHostUnservedRows,
142
- providerVisibleMessage,
143
147
  completedToolArcCrossesBoundary,
144
148
  estimateTrueRawMessageTokens,
145
149
  buildToolArcs,
@@ -156,6 +160,7 @@ import {
156
160
  applyEditMarkerToInput,
157
161
  setDatabase,
158
162
  loadToolDefinitionMeasurements,
163
+ estimatePartImageTokens,
159
164
  modelAcceptsEmptyContent,
160
165
  isAnthropicFamilyRoute,
161
166
  variantChangeBustsProviderCache,
@@ -179,8 +184,9 @@ import {
179
184
  getRawSessionTagKeysThrough2,
180
185
  getLegacyProtectedTailStartOrdinal2,
181
186
  readSessionChunk2
182
- } from "./index-f7z4jc1f.js";
187
+ } from "./index-zmx631ey.js";
183
188
  import {
189
+ inRootAsyncContext,
184
190
  registerSlowWriteReporter,
185
191
  registerSqliteDiagnosticSink,
186
192
  detectSqliteRuntime,
@@ -195,7 +201,7 @@ import {
195
201
  withPrivilegedWriter,
196
202
  indexCoordinationInput,
197
203
  withIndexChunk
198
- } from "./index-3qawqvws.js";
204
+ } from "./index-cr4hzd76.js";
199
205
  import {
200
206
  pushNotification2
201
207
  } from "./index-b3eqj1g6.js";
@@ -6750,6 +6756,15 @@ var HistorianModelEntryObjectSchema = object({
6750
6756
  thinking_level: PiThinkingLevelSchema.describe("Pi thinking level for this entry.")
6751
6757
  }).strict();
6752
6758
  var HistorianModelEntrySchema = union([string2(), HistorianModelEntryObjectSchema]);
6759
+ var MaintenanceModelEntrySchema = HistorianModelEntrySchema.superRefine((entry, ctx) => {
6760
+ const model = typeof entry === "string" ? entry : entry.model;
6761
+ if (typeof model !== "string" || model.trim().length === 0) {
6762
+ ctx.addIssue({
6763
+ code: "custom",
6764
+ message: "maintenance model entries must be non-empty: a bare provider/model string or { model, variant?, thinking_level? }"
6765
+ });
6766
+ }
6767
+ });
6753
6768
  var ProfileHistorianSchema = object({
6754
6769
  model: HistorianModelEntrySchema.optional().describe("Primary historian model entry."),
6755
6770
  fallback_models: array(HistorianModelEntrySchema).optional().describe("Ordered fallback historian model entries."),
@@ -6876,6 +6891,8 @@ var MagicContextConfigSchema = object({
6876
6891
  }).describe("Cross-session memory configuration"),
6877
6892
  maintenance: object({
6878
6893
  enabled: boolean2().default(true).describe("Enable the automatic memory-maintenance task (default: true). USER-LEVEL ONLY — the whole maintenance group is stripped from project-tier config."),
6894
+ model: MaintenanceModelEntrySchema.optional().describe("Primary maintenance model entry: a bare provider/model string or { model, variant? (OpenCode), thinking_level? (Pi) }. USER-LEVEL ONLY. When absent, inherit historian model configuration. If neither has a primary, automatic passes skip and manual runs refuse. The main-session model is never inherited."),
6895
+ fallback_models: array(MaintenanceModelEntrySchema).optional().describe("Ordered fallback maintenance model entries, tried after CALL failures only. When absent and the primary is inherited, inherit historian fallbacks; explicit [] disables fallbacks. Illegal manifests, permission refusals, and exhausted budgets never switch models."),
6879
6896
  budgets: object({
6880
6897
  inputTokens: number2().int().min(1).default(400000).describe("Per-run input-token budget (input + cacheRead + cacheWrite pricing dimensions). Default 400000."),
6881
6898
  outputTokens: number2().int().min(1).default(32000).describe("Per-run output-token budget. Default 32000."),
@@ -6917,7 +6934,7 @@ var MagicContextConfigSchema = object({
6917
6934
  maxRetries: 3
6918
6935
  },
6919
6936
  anomalyThreshold: { absoluteRows: 10, poolFraction: 0.25 }
6920
- }).describe("Automatic memory-maintenance run controls (dreamer replacement). USER-LEVEL ONLY: the whole group is stripped from project-tier config so a cloned repository cannot raise budgets, relax cooldowns, or loosen the anomaly guard.")
6937
+ }).describe("Automatic memory-maintenance run controls (dreamer replacement). USER-LEVEL ONLY: project-tier config cannot pick maintenance models or change run controls. An absent model inherits historian model configuration, never the session model.")
6921
6938
  });
6922
6939
  var LIVE_RELOAD_CONFIG_PATHS = [
6923
6940
  "cache_ttl",
@@ -8654,6 +8671,130 @@ function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
8654
8671
  return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
8655
8672
  }
8656
8673
 
8674
+ // ../plugin/src/shared/model-resolution.ts
8675
+ function asRecord(value) {
8676
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
8677
+ }
8678
+ function readString(value) {
8679
+ if (typeof value !== "string")
8680
+ return;
8681
+ const trimmed = value.trim();
8682
+ return trimmed.length > 0 ? trimmed : undefined;
8683
+ }
8684
+ function normalizeModelEntry(entry, harness) {
8685
+ if (typeof entry === "string") {
8686
+ const model = readString(entry);
8687
+ return model ? { model } : undefined;
8688
+ }
8689
+ const objectEntry = asRecord(entry);
8690
+ if (!objectEntry)
8691
+ return;
8692
+ const model = readString(objectEntry.model);
8693
+ if (!model)
8694
+ return;
8695
+ const qualifier = readString(objectEntry[harness === "opencode" ? "variant" : "thinking_level"]);
8696
+ return qualifier ? { model, qualifier } : { model };
8697
+ }
8698
+ function isValidModelReference(entry) {
8699
+ const slash = entry.model.indexOf("/");
8700
+ return slash > 0 && slash < entry.model.length - 1;
8701
+ }
8702
+ function sameAttempt(a, b) {
8703
+ return a.model === b.model && a.qualifier === b.qualifier;
8704
+ }
8705
+ function resolveFallbackEntries(value, harness) {
8706
+ const values = Array.isArray(value) ? value : value === undefined ? [] : [value];
8707
+ const resolved = [];
8708
+ for (const value of values) {
8709
+ const entry = normalizeModelEntry(value, harness);
8710
+ if (!entry || !isValidModelReference(entry))
8711
+ continue;
8712
+ if (resolved.some((candidate) => sameAttempt(candidate, entry)))
8713
+ continue;
8714
+ resolved.push(entry);
8715
+ }
8716
+ return resolved;
8717
+ }
8718
+ function resolvePrimaryEntry(args) {
8719
+ const entry = normalizeModelEntry(args.entry, args.harness);
8720
+ if (!entry)
8721
+ return;
8722
+ const qualifier = entry.qualifier ?? readString(args.defaultQualifier);
8723
+ return qualifier ? { ...entry, qualifier } : entry;
8724
+ }
8725
+ function resolveHistorianModel(config, harness) {
8726
+ const historian = asRecord(asRecord(config)?.historian);
8727
+ if (!historian)
8728
+ return { fallbacks: [] };
8729
+ return {
8730
+ primary: resolvePrimaryEntry({
8731
+ entry: historian.model,
8732
+ defaultQualifier: historian[harness === "opencode" ? "variant" : "thinking_level"],
8733
+ harness
8734
+ }),
8735
+ fallbacks: resolveFallbackEntries(historian.fallback_models, harness)
8736
+ };
8737
+ }
8738
+
8739
+ // ../plugin/src/shared/resolve-fallbacks.ts
8740
+ function parseProviderModel(spec) {
8741
+ const slash = spec.indexOf("/");
8742
+ if (slash < 1 || slash >= spec.length - 1)
8743
+ return null;
8744
+ return {
8745
+ providerID: spec.slice(0, slash).trim(),
8746
+ modelID: spec.slice(slash + 1).trim()
8747
+ };
8748
+ }
8749
+ function modelBodyField(spec) {
8750
+ const entry = toModelEntry(spec);
8751
+ if (!entry)
8752
+ return {};
8753
+ const parsed = parseProviderModel(entry.model);
8754
+ return parsed ? { model: parsed, ...entry.qualifier ? { variant: entry.qualifier } : {} } : {};
8755
+ }
8756
+ function toModelEntry(spec) {
8757
+ if (typeof spec === "string") {
8758
+ const model = spec.trim();
8759
+ return model ? { model } : undefined;
8760
+ }
8761
+ return spec;
8762
+ }
8763
+
8764
+ // ../plugin/src/features/magic-context/memory-maintainer/model-chain.ts
8765
+ var MAINTENANCE_MODEL_UNCONFIGURED_MESSAGE = "memory maintenance has no primary model configured — set maintenance.model or historian.model in the user-level mmc config (fallback_models alone does not enable maintenance; the session model is never inherited)";
8766
+ function inheritMaintenanceModelConfig(config) {
8767
+ const { maintenance, historian } = config;
8768
+ if (maintenance.model !== undefined || historian?.model === undefined)
8769
+ return maintenance;
8770
+ const entry = typeof historian.model === "string" ? { model: historian.model } : historian.model;
8771
+ return {
8772
+ ...maintenance,
8773
+ model: {
8774
+ ...entry,
8775
+ variant: entry.variant ?? historian.variant,
8776
+ thinking_level: entry.thinking_level ?? historian.thinking_level
8777
+ },
8778
+ fallback_models: maintenance.fallback_models ?? historian.fallback_models
8779
+ };
8780
+ }
8781
+ function resolveMaintenanceModelChain(config, harness) {
8782
+ const primary = normalizeModelEntry(config.model, harness);
8783
+ if (!primary)
8784
+ return;
8785
+ const fallbacks = [];
8786
+ for (const entry of config.fallback_models ?? []) {
8787
+ const resolved = normalizeModelEntry(entry, harness);
8788
+ if (resolved)
8789
+ fallbacks.push(resolved);
8790
+ }
8791
+ return { primary, fallbacks };
8792
+ }
8793
+ function hasMaintenancePrimary(config) {
8794
+ const primary = toModelEntry(config.model);
8795
+ return primary !== undefined && primary.model.trim().length > 0;
8796
+ }
8797
+
8657
8798
  // ../plugin/src/shared/model-cache-ttl.ts
8658
8799
  var MODEL_CACHE_LIFETIMES = [
8659
8800
  {
@@ -14735,6 +14876,9 @@ function requestPendingMaterialization(sessions, sessionId, origin) {
14735
14876
  function pendingMaterializationOrigin(sessions, sessionId) {
14736
14877
  return sessions.has(sessionId) ? origins.get(sessions)?.get(sessionId) ?? "pending_materialization" : "pending_materialization";
14737
14878
  }
14879
+ function hasPendingMaterializationOrigin(sessions, sessionId, origin) {
14880
+ return sessions.has(sessionId) && causes.get(sessions)?.get(sessionId)?.has(origin) === true;
14881
+ }
14738
14882
  function clearPendingMaterialization(sessions, sessionId) {
14739
14883
  sessions.delete(sessionId);
14740
14884
  origins.get(sessions)?.delete(sessionId);
@@ -16746,7 +16890,7 @@ function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
16746
16890
  function getErrorMessage(error) {
16747
16891
  return error instanceof Error ? error.message : String(error);
16748
16892
  }
16749
- function readString(value) {
16893
+ function readString2(value) {
16750
16894
  if (typeof value === "string" && value.length > 0)
16751
16895
  return value;
16752
16896
  if (typeof value === "number")
@@ -16769,19 +16913,19 @@ function describeError(error) {
16769
16913
  };
16770
16914
  }
16771
16915
  const obj = error;
16772
- const nameFromField = readString(obj.name);
16916
+ const nameFromField = readString2(obj.name);
16773
16917
  const nameFromCtor = error?.constructor?.name;
16774
16918
  const name = nameFromField ?? nameFromCtor ?? "Error";
16775
- const message = readString(obj.message) ?? "";
16776
- const status = readString(obj.status) ?? readString(obj.statusCode);
16777
- const code = readString(obj.code);
16919
+ const message = readString2(obj.message) ?? "";
16920
+ const status = readString2(obj.status) ?? readString2(obj.statusCode);
16921
+ const code = readString2(obj.code);
16778
16922
  let causeName;
16779
16923
  const cause = obj.cause;
16780
16924
  if (cause && typeof cause === "object") {
16781
16925
  const causeRecord = cause;
16782
- causeName = readString(causeRecord.name) ?? cause.constructor?.name;
16926
+ causeName = readString2(causeRecord.name) ?? cause.constructor?.name;
16783
16927
  }
16784
- const stack = readString(obj.stack);
16928
+ const stack = readString2(obj.stack);
16785
16929
  const stackHead = stack ? stack.split(`
16786
16930
  `).slice(0, 4).map((l) => l.trim()).filter((l) => l.length > 0).join(" | ") : undefined;
16787
16931
  const briefParts = [];
@@ -17503,8 +17647,8 @@ function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion)
17503
17647
  startMessageFtsRowidMapBackfill(db).then(async () => {
17504
17648
  if (!readsOpenCodeStore)
17505
17649
  return;
17506
- const { readRawSessionMessagePage, readRawSessionMessages } = await import("./read-session-chunk-vhejzv1k.js");
17507
- const { startMessageTimeBackfill } = await import("./message-time-backfill-mpks152d.js");
17650
+ const { readRawSessionMessagePage, readRawSessionMessages } = await import("./read-session-chunk-g164kwxz.js");
17651
+ const { startMessageTimeBackfill } = await import("./message-time-backfill-fvkrfyp4.js");
17508
17652
  await startMessageTimeBackfill(db, Object.assign(readRawSessionMessages, {
17509
17653
  readPage: readRawSessionMessagePage
17510
17654
  }));
@@ -20593,71 +20737,6 @@ function shouldKeepSubagents() {
20593
20737
  return keepSubagents;
20594
20738
  }
20595
20739
 
20596
- // ../plugin/src/shared/model-resolution.ts
20597
- function asRecord(value) {
20598
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
20599
- }
20600
- function readString2(value) {
20601
- if (typeof value !== "string")
20602
- return;
20603
- const trimmed = value.trim();
20604
- return trimmed.length > 0 ? trimmed : undefined;
20605
- }
20606
- function normalizeModelEntry(entry, harness) {
20607
- if (typeof entry === "string") {
20608
- const model = readString2(entry);
20609
- return model ? { model } : undefined;
20610
- }
20611
- const objectEntry = asRecord(entry);
20612
- if (!objectEntry)
20613
- return;
20614
- const model = readString2(objectEntry.model);
20615
- if (!model)
20616
- return;
20617
- const qualifier = readString2(objectEntry[harness === "opencode" ? "variant" : "thinking_level"]);
20618
- return qualifier ? { model, qualifier } : { model };
20619
- }
20620
- function isValidModelReference(entry) {
20621
- const slash = entry.model.indexOf("/");
20622
- return slash > 0 && slash < entry.model.length - 1;
20623
- }
20624
- function sameAttempt(a, b) {
20625
- return a.model === b.model && a.qualifier === b.qualifier;
20626
- }
20627
- function resolveFallbackEntries(value, harness) {
20628
- const values = Array.isArray(value) ? value : value === undefined ? [] : [value];
20629
- const resolved = [];
20630
- for (const value of values) {
20631
- const entry = normalizeModelEntry(value, harness);
20632
- if (!entry || !isValidModelReference(entry))
20633
- continue;
20634
- if (resolved.some((candidate) => sameAttempt(candidate, entry)))
20635
- continue;
20636
- resolved.push(entry);
20637
- }
20638
- return resolved;
20639
- }
20640
- function resolvePrimaryEntry(args) {
20641
- const entry = normalizeModelEntry(args.entry, args.harness);
20642
- if (!entry)
20643
- return;
20644
- const qualifier = entry.qualifier ?? readString2(args.defaultQualifier);
20645
- return qualifier ? { ...entry, qualifier } : entry;
20646
- }
20647
- function resolveHistorianModel(config, harness) {
20648
- const historian = asRecord(asRecord(config)?.historian);
20649
- if (!historian)
20650
- return { fallbacks: [] };
20651
- return {
20652
- primary: resolvePrimaryEntry({
20653
- entry: historian.model,
20654
- defaultQualifier: historian[harness === "opencode" ? "variant" : "thinking_level"],
20655
- harness
20656
- }),
20657
- fallbacks: resolveFallbackEntries(historian.fallback_models, harness)
20658
- };
20659
- }
20660
-
20661
20740
  // ../plugin/src/shared/prompt-surface-runtime.ts
20662
20741
  import { readFileSync as readFileSync12, statSync as statSync5 } from "node:fs";
20663
20742
  import { dirname as dirname5, isAbsolute as isAbsolute2, resolve as resolve5 } from "node:path";
@@ -20812,7 +20891,7 @@ function registerStandalonePluginMarker(version) {
20812
20891
  // package.json
20813
20892
  var package_default = {
20814
20893
  name: "@retaw106/mmc-pi",
20815
- version: "2.3.0",
20894
+ version: "2.4.0",
20816
20895
  type: "module",
20817
20896
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
20818
20897
  main: "dist/index.js",
@@ -22035,10 +22114,16 @@ class MaintenanceLauncher {
22035
22114
  constructor(deps) {
22036
22115
  this.deps = deps;
22037
22116
  }
22117
+ modelConfigured() {
22118
+ return hasMaintenancePrimary(this.deps.getConfig().maintenance);
22119
+ }
22038
22120
  maybeLaunch(signals) {
22039
22121
  if (!this.deps.getConfig().maintenance.enabled) {
22040
22122
  return { launched: false, reason: "maintenance-disabled" };
22041
22123
  }
22124
+ if (!this.modelConfigured()) {
22125
+ return { launched: false, reason: "maintenance-model-unconfigured" };
22126
+ }
22042
22127
  if (this.runInFlight) {
22043
22128
  return { launched: false, reason: "run-in-flight-local" };
22044
22129
  }
@@ -22068,6 +22153,9 @@ class MaintenanceLauncher {
22068
22153
  if (!this.deps.getConfig().maintenance.enabled) {
22069
22154
  return { launched: false, reason: "maintenance-disabled" };
22070
22155
  }
22156
+ if (!this.modelConfigured()) {
22157
+ return { launched: false, reason: "maintenance-model-unconfigured" };
22158
+ }
22071
22159
  if (this.runInFlight) {
22072
22160
  return { launched: false, reason: "run-in-flight-local" };
22073
22161
  }
@@ -22961,6 +23049,11 @@ var pendingDecisionBySession = new Map;
22961
23049
  var pendingPiDecisionBySession = new Map;
22962
23050
  var lastBoundMessageIdBySession = new Map;
22963
23051
  var scheduledWriteTokensBySession = new Map;
23052
+ var systemHashChangedSessions = new Set;
23053
+ var piSystemHashChangedSessions = new Set;
23054
+ var unbustedPassBySession = new Map;
23055
+ var newestInputAssistantBySession = new Map;
23056
+ var stagedV2DecisionBySession = new Map;
22964
23057
  var writerOverrideForTests = null;
22965
23058
  var retentionOverrideForTests = null;
22966
23059
  function normalizeMaterializeReason(harness, reason, rematerialized) {
@@ -22975,12 +23068,28 @@ function normalizeMaterializeReason(harness, reason, rematerialized) {
22975
23068
  }
22976
23069
  return rematerialized ? "pressure_refold" : null;
22977
23070
  }
23071
+ function notePiSystemPromptHashChange(sessionId) {
23072
+ piSystemHashChangedSessions.add(sessionId);
23073
+ }
23074
+ function hasPendingPiSystemPromptHashChange(sessionId) {
23075
+ return piSystemHashChangedSessions.has(sessionId);
23076
+ }
23077
+ var IDLE_EXPIRY_REASONS = new Set([
23078
+ "ttl_idle",
23079
+ "ttl_expiry"
23080
+ ]);
23081
+ function withSystemHashReason(decision) {
23082
+ if (IDLE_EXPIRY_REASONS.has(decision.materializeReason))
23083
+ return decision;
23084
+ return { ...decision, materializeReason: "system_hash" };
23085
+ }
22978
23086
  function recordPendingPiTransformDecision(sessionId, decision, snapshotNewestAssistantEntryId, publicationGuard) {
22979
23087
  publicationGuard?.();
22980
- if (!decision.bustedThisPass)
23088
+ const systemHashChanged = piSystemHashChangedSessions.delete(sessionId);
23089
+ if (!decision.bustedThisPass && !systemHashChanged)
22981
23090
  return;
22982
23091
  pendingPiDecisionBySession.set(sessionId, {
22983
- ...decision,
23092
+ ...systemHashChanged ? withSystemHashReason(decision) : decision,
22984
23093
  snapshotNewestAssistantEntryId,
22985
23094
  publicationGuard
22986
23095
  });
@@ -23023,7 +23132,7 @@ function schedulePiTransformDecisionResolve(args) {
23023
23132
  return false;
23024
23133
  pendingPiDecisionBySession.delete(args.sessionId);
23025
23134
  const token = addScheduledWriteToken(args.sessionId);
23026
- setTimeout(() => {
23135
+ inRootAsyncContext(() => setTimeout(() => {
23027
23136
  try {
23028
23137
  if (!hasScheduledWriteToken(args.sessionId, token))
23029
23138
  return;
@@ -23043,7 +23152,7 @@ function schedulePiTransformDecisionResolve(args) {
23043
23152
  } finally {
23044
23153
  deleteScheduledWriteToken(args.sessionId, token);
23045
23154
  }
23046
- }, 0);
23155
+ }, 0));
23047
23156
  return true;
23048
23157
  }
23049
23158
  function addScheduledWriteToken(sessionId) {
@@ -23192,6 +23301,31 @@ function computePiWorkMetrics(sessionEntries) {
23192
23301
  return { newWorkTokens, totalInputTokens };
23193
23302
  }
23194
23303
 
23304
+ // ../plugin/src/hooks/magic-context/pending-drop-eligibility.ts
23305
+ function pendingDropVeto(tagNumber, target, protectedTagIds) {
23306
+ if (protectedTagIds.has(tagNumber))
23307
+ return "protected";
23308
+ if (target?.thinkingDropProtected)
23309
+ return "active_thinking";
23310
+ return null;
23311
+ }
23312
+ function capturePendingDropEligibility(targets, protectedTagIds) {
23313
+ const eligible = new Set;
23314
+ for (const [number, target] of targets) {
23315
+ if (pendingDropVeto(number, target, protectedTagIds))
23316
+ continue;
23317
+ if (target.drop) {
23318
+ if (target.canDrop?.() === true)
23319
+ eligible.add(number);
23320
+ } else {
23321
+ const content = target.getContent?.();
23322
+ if (typeof content === "string" && content.length > `[dropped §${number}§]`.length)
23323
+ eligible.add(number);
23324
+ }
23325
+ }
23326
+ return eligible;
23327
+ }
23328
+
23195
23329
  // ../plugin/src/hooks/magic-context/apply-operations.ts
23196
23330
  var RECENT_TOOL_SKELETON_WINDOW = 20;
23197
23331
  function buildReplacementContent(tagId) {
@@ -23295,7 +23429,7 @@ function prepareLegacyToolSkeletonConversions(db, sessionId, targets, options =
23295
23429
  }
23296
23430
  };
23297
23431
  }
23298
- function applyPendingOperations(sessionId, db, targets, protectedTagIds, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set, onTagReduced, onBatchComplete) {
23432
+ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set, onTagReduced, onBatchComplete, onEligibilitySnapshot) {
23299
23433
  let didMutateMessage = false;
23300
23434
  let diagnostics;
23301
23435
  const reject = (reason) => {
@@ -23310,6 +23444,7 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23310
23444
  }).immediate();
23311
23445
  {
23312
23446
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
23447
+ onEligibilitySnapshot?.(capturePendingDropEligibility(targets, protectedTagIds));
23313
23448
  const neededTagNumbers = new Set(pendingOps.map((op) => op.tagId));
23314
23449
  for (const op of syntheticPendingOps)
23315
23450
  neededTagNumbers.add(op.tagId);
@@ -23346,13 +23481,10 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23346
23481
  removePendingOp(db, sessionId, pendingOp.tagId);
23347
23482
  continue;
23348
23483
  }
23349
- if (protectedTagIds.has(pendingOp.tagId)) {
23350
- reject("protected");
23351
- continue;
23352
- }
23353
23484
  const target = targets.get(pendingOp.tagId);
23354
- if (target?.thinkingDropProtected) {
23355
- reject("active_thinking");
23485
+ const veto = pendingDropVeto(pendingOp.tagId, target, protectedTagIds);
23486
+ if (veto) {
23487
+ reject(veto);
23356
23488
  continue;
23357
23489
  }
23358
23490
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
@@ -23372,9 +23504,7 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23372
23504
  try {
23373
23505
  db.transaction(() => {
23374
23506
  startedAt = performance.now();
23375
- if (mode)
23376
- updateTagDropMode(db, sessionId, pendingOp.tagId, mode);
23377
- updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
23507
+ markTagDropped(db, sessionId, pendingOp.tagId, mode);
23378
23508
  if (!synthetic)
23379
23509
  removePendingOp(db, sessionId, pendingOp.tagId);
23380
23510
  }).immediate();
@@ -23883,6 +24013,7 @@ var AGENT_STEP_CAPS = {
23883
24013
  [MEMORY_MAINTAINER_A_AGENT]: MEMORY_MAINTAINER_A_STEP_CAP,
23884
24014
  [HIDDEN_HISTORIAN_AGENT]: HISTORIAN_STEP_CAP
23885
24015
  };
24016
+ var DISPOSED_CHILDREN_KEY = Symbol.for("magic-context.v2.disposed-hidden-children");
23886
24017
  class HiddenAgentStepLimit extends Error {
23887
24018
  agent;
23888
24019
  cap;
@@ -23965,31 +24096,6 @@ function hasLengthCappedOutput(value) {
23965
24096
  import { AsyncLocalStorage } from "node:async_hooks";
23966
24097
  var primaryQuotas = new AsyncLocalStorage;
23967
24098
 
23968
- // ../plugin/src/shared/resolve-fallbacks.ts
23969
- function parseProviderModel(spec) {
23970
- const slash = spec.indexOf("/");
23971
- if (slash < 1 || slash >= spec.length - 1)
23972
- return null;
23973
- return {
23974
- providerID: spec.slice(0, slash).trim(),
23975
- modelID: spec.slice(slash + 1).trim()
23976
- };
23977
- }
23978
- function modelBodyField(spec) {
23979
- const entry = toModelEntry(spec);
23980
- if (!entry)
23981
- return {};
23982
- const parsed = parseProviderModel(entry.model);
23983
- return parsed ? { model: parsed, ...entry.qualifier ? { variant: entry.qualifier } : {} } : {};
23984
- }
23985
- function toModelEntry(spec) {
23986
- if (typeof spec === "string") {
23987
- const model = spec.trim();
23988
- return model ? { model } : undefined;
23989
- }
23990
- return spec;
23991
- }
23992
-
23993
24099
  // ../plugin/src/shared/model-suggestion-retry.ts
23994
24100
  var ABORT_CALL_TIMEOUT_MS = 3000;
23995
24101
  function copyPromptArgs(args, body) {
@@ -25372,12 +25478,12 @@ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, tag
25372
25478
  function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
25373
25479
  return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
25374
25480
  }
25375
- function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, keepReasoningTokens, clearedReasoningThroughTag, canClearReasoning, liveBudgetCutoff) {
25481
+ function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, keepReasoningTokens, clearedReasoningThroughTag, canClearReasoning, liveBudgetCutoff, eligiblePendingDropTagIds) {
25376
25482
  const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
25377
25483
  if (totalActiveBytes === 0)
25378
25484
  return null;
25379
25485
  let droppableBytes = 0;
25380
- const pendingDrops = getPendingOps(db, sessionId).filter((op) => op.operation === "drop");
25486
+ const pendingDrops = getPendingOps(db, sessionId).filter((op) => op.operation === "drop" && eligiblePendingDropTagIds?.has(op.tagId) === true);
25381
25487
  const pendingDropTagIds = new Set(pendingDrops.map((op) => op.tagId));
25382
25488
  if (pendingDrops.length > 0) {
25383
25489
  droppableBytes += activeTags.filter((tag) => pendingDropTagIds.has(tag.tagNumber)).reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
@@ -25505,7 +25611,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
25505
25611
  function formatProjectedPostDropPercentage(value) {
25506
25612
  return value === null ? "none" : `${value.toFixed(1)}%`;
25507
25613
  }
25508
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, keepReasoningTokens, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection, reclaimRide, storedTotalsExcludeToolTags) {
25614
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, keepReasoningTokens, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection, reclaimRide, eligiblePendingDropTagIds, storedTotalsExcludeToolTags) {
25509
25615
  if (sessionMeta.compartmentInProgress) {
25510
25616
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
25511
25617
  return { shouldFire: false };
@@ -25569,7 +25675,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
25569
25675
  return { shouldFire: false };
25570
25676
  }
25571
25677
  const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
25572
- const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff);
25678
+ const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff, eligiblePendingDropTagIds);
25573
25679
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
25574
25680
  const rideLabel = reclaimRide ? reclaimRideLabel(reclaimRide) : "ride=none";
25575
25681
  const dropsCanLand = reclaimRide !== undefined && hasReclaimRide(reclaimRide);
@@ -25672,8 +25778,8 @@ var USER_FACING_FAILURES = {
25672
25778
  },
25673
25779
  history_boundary_unresolved: {
25674
25780
  code: "MC-H04",
25675
- sentence: "This request was not sent: the message that marks where this session's history summary ends is missing from the OpenCode store, and without it the request is larger than the model's context window.",
25676
- action: "Run /mmc-recomp to rebuild the history summary."
25781
+ sentence: "This request was not sent: Magic Context could not safely place the history summary boundary in the loaded conversation, and the untrimmed request is larger than the model's context window.",
25782
+ action: "Reopen this session in its current project directory, then run /mmc-recomp to rebuild the history summary. A model with a larger context window can also help."
25677
25783
  },
25678
25784
  history_over_window_unmanaged: {
25679
25785
  code: "MC-H06",
@@ -25791,6 +25897,20 @@ function userFacingFailureCode(key) {
25791
25897
  }
25792
25898
 
25793
25899
  // ../plugin/src/hooks/magic-context/degraded-pass-refusal.ts
25900
+ function describeDegradedPassFailure(site, cause) {
25901
+ const details = [site];
25902
+ const seen = new Set;
25903
+ while (cause !== undefined && cause !== null && !seen.has(cause)) {
25904
+ seen.add(cause);
25905
+ const current = cause;
25906
+ const detail = typeof current.site === "string" ? current.site : typeof current.message === "string" ? current.message : String(cause);
25907
+ if (!details.includes(detail))
25908
+ details.push(detail);
25909
+ cause = current.cause;
25910
+ }
25911
+ return `Magic Context could not safely prepare this turn [${userFacingFailureCode("transform_pass_degraded")}] (${details.join(": ")}). This request was not sent.`;
25912
+ }
25913
+
25794
25914
  class DegradedPassRefusalError extends Error {
25795
25915
  site;
25796
25916
  code = "DEGRADED_PASS_REFUSAL";
@@ -25798,7 +25918,7 @@ class DegradedPassRefusalError extends Error {
25798
25918
  estimatedTokens;
25799
25919
  contextLimitTokens;
25800
25920
  constructor(site, options) {
25801
- super(renderUserFacingFailure("transform_pass_degraded", "plain"), {
25921
+ super(describeDegradedPassFailure(site, options?.cause), {
25802
25922
  cause: options?.cause
25803
25923
  });
25804
25924
  this.site = site;
@@ -27899,7 +28019,7 @@ function buildEditSupersessionReclaim(input) {
27899
28019
  seenFile.add(filePath);
27900
28020
  continue;
27901
28021
  }
27902
- if (input.recentMessageIds && (tag.toolOwnerMessageId === null || input.recentMessageIds.has(tag.toolOwnerMessageId))) {
28022
+ if (tag.tagNumber > input.watermark || input.recentMessageIds && (tag.toolOwnerMessageId === null || input.recentMessageIds.has(tag.toolOwnerMessageId))) {
27903
28023
  continue;
27904
28024
  }
27905
28025
  if (realPendingTagIds.has(tag.tagNumber))
@@ -27932,6 +28052,51 @@ function readFilePath(target) {
27932
28052
  return null;
27933
28053
  }
27934
28054
 
28055
+ // ../plugin/src/shared/reclaim-distance.ts
28056
+ var RECLAIM_DISTANCE_USER_TURNS = 3;
28057
+ var RECLAIM_DISTANCE_WINDOW_FRACTION = 0.25;
28058
+ function isReclaimUserTurn(args) {
28059
+ return args.role === "user" && !args.syntheticHead && !args.synthetic && !args.ignored && args.parts.some((part) => {
28060
+ if (part.synthetic || part.ignored)
28061
+ return false;
28062
+ if (part.kind !== "text" && part.kind !== "image" && part.kind !== "file")
28063
+ return false;
28064
+ if (part.kind !== "text")
28065
+ return true;
28066
+ return stripReclaimMcText(part.text ?? "").trim().length > 0;
28067
+ });
28068
+ }
28069
+ function stripReclaimMcText(text) {
28070
+ return text.replace(/^§\d+§\s*/, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, (reminder) => /^(?:Your next step: call ctx_reduce on the outputs you've already used|Still unstamped:|Housekeeping: .* are reclaimable\. Stamp each output with ctx_reduce|.* are reclaimable\. Make a ctx_reduce pass now|.* are still unstamped\. Call ctx_reduce now)/.test(reminder.slice("<system-reminder>".length).trimStart()) ? "" : reminder).replace(/<instruction name="deferred_notes">[\s\S]*?<\/instruction>/g, "");
28071
+ }
28072
+ function farBehindToolBookmark(entries, usableWindow) {
28073
+ let newerTurns = 0;
28074
+ let newerTokens = 0;
28075
+ const farTags = [];
28076
+ let firstYoungTag = Infinity;
28077
+ const seenOutputs = new Set;
28078
+ const tokenDistance = usableWindow > 0 && Number.isFinite(usableWindow) ? usableWindow * RECLAIM_DISTANCE_WINDOW_FRACTION : Infinity;
28079
+ for (let index = entries.length - 1;index >= 0; index--) {
28080
+ const entry = entries[index];
28081
+ if (!entry)
28082
+ continue;
28083
+ const tag = entry.toolOutputTag;
28084
+ if (tag !== undefined && !seenOutputs.has(tag)) {
28085
+ seenOutputs.add(tag);
28086
+ if (newerTurns >= RECLAIM_DISTANCE_USER_TURNS || newerTokens >= tokenDistance) {
28087
+ farTags.push(tag);
28088
+ } else {
28089
+ firstYoungTag = Math.min(firstYoungTag, tag);
28090
+ }
28091
+ }
28092
+ if (entry.userTurn)
28093
+ newerTurns++;
28094
+ if (Number.isFinite(entry.tokens) && entry.tokens > 0)
28095
+ newerTokens += entry.tokens;
28096
+ }
28097
+ return farTags.reduce((bookmark, tag) => tag < firstYoungTag ? Math.max(bookmark, tag) : bookmark, 0);
28098
+ }
28099
+
27935
28100
  // ../plugin/src/hooks/magic-context/tool-reclaim.ts
27936
28101
  function buildSyntheticToolReclaimOps(input) {
27937
28102
  const watermark = Math.max(0, input.watermark);
@@ -27963,10 +28128,74 @@ function buildSyntheticToolReclaimOps(input) {
27963
28128
  }
27964
28129
  return synthetic;
27965
28130
  }
27966
- function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
27967
- const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
27968
- advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
27969
- return maxTagNumber;
28131
+ function advanceToolReclaimWatermarkByDistance(db, sessionId, entries, usableWindow) {
28132
+ const bookmark = farBehindToolBookmark(entries, usableWindow);
28133
+ advanceToolReclaimWatermark(db, sessionId, bookmark);
28134
+ return bookmark;
28135
+ }
28136
+ function toolReclaimDistanceEntries(messages, targets) {
28137
+ const outputTags = new Map;
28138
+ for (const [tag, target] of targets) {
28139
+ for (const location of target.mutationParts ?? []) {
28140
+ const part = distancePart(location.part);
28141
+ if (part.kind === "tool_result" || part.kind === "tool")
28142
+ outputTags.set(location.part, tag);
28143
+ }
28144
+ }
28145
+ const entries = [];
28146
+ for (const message of messages) {
28147
+ const parts = message.parts.map(distancePart);
28148
+ let userTurn = isReclaimUserTurn({ ...message.info, parts });
28149
+ for (const [index, part] of parts.entries()) {
28150
+ const authored = isReclaimUserTurn({ role: "user", parts: [part] });
28151
+ entries.push({
28152
+ tokens: message.info.syntheticHead ? 0 : estimateTokens(stripReclaimMcText(part.text ?? "")) + part.imageTokens,
28153
+ userTurn: userTurn && authored,
28154
+ toolOutputTag: outputTags.get(message.parts[index])
28155
+ });
28156
+ if (authored)
28157
+ userTurn = false;
28158
+ }
28159
+ }
28160
+ return entries;
28161
+ }
28162
+ function distancePart(raw) {
28163
+ if (!raw || typeof raw !== "object")
28164
+ return { kind: "structural", imageTokens: 0 };
28165
+ const part = raw;
28166
+ const provenance = { synthetic: part.synthetic === true, ignored: part.ignored === true };
28167
+ if (typeof part.getText === "function") {
28168
+ const text = part.getText();
28169
+ return {
28170
+ ...provenance,
28171
+ kind: String(part.kind),
28172
+ text: typeof text === "string" ? text : undefined,
28173
+ imageTokens: typeof part.imageTokens === "function" ? Number(part.imageTokens()) : 0
28174
+ };
28175
+ }
28176
+ const metadata = { ...provenance, imageTokens: estimatePartImageTokens(raw) };
28177
+ if (part.type === "tool") {
28178
+ const state = part.state;
28179
+ return {
28180
+ ...metadata,
28181
+ kind: "tool",
28182
+ text: `${JSON.stringify(state?.input ?? {})}
28183
+ ${typeof state?.output === "string" ? state.output : JSON.stringify(state?.output ?? "")}`
28184
+ };
28185
+ }
28186
+ if (part.type === "tool_use")
28187
+ return { ...metadata, kind: "tool_use", text: JSON.stringify(part.input ?? {}) };
28188
+ if (part.type === "tool_result")
28189
+ return {
28190
+ ...metadata,
28191
+ kind: "tool_result",
28192
+ text: typeof part.content === "string" ? part.content : JSON.stringify(part.content ?? "")
28193
+ };
28194
+ return {
28195
+ ...metadata,
28196
+ kind: String(part.type),
28197
+ text: typeof part.text === "string" ? part.text : typeof part.thinking === "string" ? part.thinking : undefined
28198
+ };
27970
28199
  }
27971
28200
 
27972
28201
  // ../plugin/src/shared/recovery-reasoning-prefix.ts
@@ -36048,6 +36277,59 @@ function recordPiTransformTiming(sample) {
36048
36277
  // src/ctx-reduce-nudge-pi.ts
36049
36278
  import { randomUUID as randomUUID6 } from "node:crypto";
36050
36279
 
36280
+ // src/provider-visible-parts-pi.ts
36281
+ function isRecord4(value) {
36282
+ return value !== null && typeof value === "object" && !Array.isArray(value);
36283
+ }
36284
+ var PART_FIELDS = {
36285
+ text: ["type", "text", "textSignature"],
36286
+ thinking: ["type", "thinking", "thinkingSignature", "redacted"],
36287
+ image: ["type", "data", "mimeType"],
36288
+ toolCall: [
36289
+ "type",
36290
+ "id",
36291
+ "name",
36292
+ "arguments",
36293
+ "thoughtSignature",
36294
+ "namespace"
36295
+ ]
36296
+ };
36297
+ var MESSAGE_FIELDS = [
36298
+ "role",
36299
+ "provider",
36300
+ "api",
36301
+ "model",
36302
+ "stopReason",
36303
+ "toolCallId",
36304
+ "toolName",
36305
+ "isError"
36306
+ ];
36307
+ function pick2(value, fields) {
36308
+ const view = {};
36309
+ for (const field of fields) {
36310
+ if (Object.hasOwn(value, field))
36311
+ view[field] = value[field];
36312
+ }
36313
+ return view;
36314
+ }
36315
+ function providerVisiblePiPart(value, providerApi) {
36316
+ if (providerApi === "pi-messages")
36317
+ return value;
36318
+ if (Array.isArray(value))
36319
+ return value.map((part) => providerVisiblePiPart(part, providerApi));
36320
+ if (!isRecord4(value))
36321
+ return value;
36322
+ if (typeof value.role === "string") {
36323
+ const view = pick2(value, MESSAGE_FIELDS);
36324
+ if (Object.hasOwn(value, "content")) {
36325
+ view.content = Array.isArray(value.content) ? value.content.map((part) => providerVisiblePiPart(part, providerApi)) : typeof value.content === "string" ? value.content : undefined;
36326
+ }
36327
+ return view;
36328
+ }
36329
+ const type = typeof value.type === "string" ? value.type : "";
36330
+ return pick2(value, Object.hasOwn(PART_FIELDS, type) ? PART_FIELDS[type] : ["type"]);
36331
+ }
36332
+
36051
36333
  // src/tail-hygiene-walk-pi.ts
36052
36334
  var TAG_PREFIX = /^§(\d+)§\s*/;
36053
36335
  var SYNTHETIC_TODO_PREFIX = "mc_synthetic_todo_";
@@ -36061,7 +36343,7 @@ function clearPiTailHygieneContentMemo() {
36061
36343
  }
36062
36344
  var FNV1A_32_OFFSET2 = 2166136261;
36063
36345
  var FNV1A_32_PRIME2 = 16777619;
36064
- function isRecord4(value) {
36346
+ function isRecord5(value) {
36065
36347
  return value !== null && typeof value === "object";
36066
36348
  }
36067
36349
  function fnv1a32(value) {
@@ -36076,7 +36358,7 @@ function safeStableStringify(value) {
36076
36358
  const seen = new Set;
36077
36359
  try {
36078
36360
  return JSON.stringify(value, (_key, current) => {
36079
- if (!isRecord4(current) || Array.isArray(current))
36361
+ if (!isRecord5(current) || Array.isArray(current))
36080
36362
  return current;
36081
36363
  if (seen.has(current))
36082
36364
  return "[Circular]";
@@ -36135,7 +36417,7 @@ function messageIdentity(message, index, stableId) {
36135
36417
  const resolved = stableId?.(message, index);
36136
36418
  if (resolved)
36137
36419
  return resolved;
36138
- if (isRecord4(message)) {
36420
+ if (isRecord5(message)) {
36139
36421
  if (typeof message.responseId === "string" && message.responseId.length > 0) {
36140
36422
  return `response:${message.responseId}`;
36141
36423
  }
@@ -36195,13 +36477,13 @@ function collectToolArcs(input) {
36195
36477
  const messageIds = input.messages.map((message, index) => messageIdentity(message, index, input.stableId));
36196
36478
  for (let messageIndex = 0;messageIndex < input.messages.length; messageIndex += 1) {
36197
36479
  const raw = input.messages[messageIndex];
36198
- if (!isRecord4(raw))
36480
+ if (!isRecord5(raw))
36199
36481
  continue;
36200
36482
  const role = raw.role;
36201
36483
  if (role === "assistant" && Array.isArray(raw.content)) {
36202
36484
  for (let partIndex = 0;partIndex < raw.content.length; partIndex += 1) {
36203
36485
  const part = raw.content[partIndex];
36204
- if (!isRecord4(part) || part.type !== "toolCall")
36486
+ if (!isRecord5(part) || part.type !== "toolCall")
36205
36487
  continue;
36206
36488
  if (typeof part.id !== "string" || part.id.length === 0)
36207
36489
  continue;
@@ -36212,7 +36494,7 @@ function collectToolArcs(input) {
36212
36494
  ownerId,
36213
36495
  ownerIndex: messageIndex,
36214
36496
  toolName: typeof part.name === "string" ? part.name : null,
36215
- sentinel: isRecord4(part.arguments) && typeof part.arguments.__magic_context_dropped__ === "string",
36497
+ sentinel: isRecord5(part.arguments) && typeof part.arguments.__magic_context_dropped__ === "string",
36216
36498
  parts: [part]
36217
36499
  };
36218
36500
  arcs.push(arc);
@@ -36244,7 +36526,7 @@ function collectToolArcs(input) {
36244
36526
  if (!Array.isArray(raw.content))
36245
36527
  continue;
36246
36528
  for (const part of raw.content) {
36247
- if (!isRecord4(part))
36529
+ if (!isRecord5(part))
36248
36530
  continue;
36249
36531
  arc.parts.push(part);
36250
36532
  arcByPart.set(part, arc);
@@ -36445,11 +36727,20 @@ function attributeTags(input, arcs, messageIds) {
36445
36727
  }
36446
36728
  return { messageTags, tagsByNumber };
36447
36729
  }
36448
- function excludedDraft(key, value) {
36730
+ var partJsonMemo = new TailHygieneJsonMemo;
36731
+ var excludedViews = new WeakMap;
36732
+ function excludedDraftForApi(key, value, providerApi) {
36733
+ let view = value !== null && typeof value === "object" ? excludedViews.get(value) : undefined;
36734
+ if (view === undefined) {
36735
+ view = providerVisiblePiPart(value, providerApi);
36736
+ if (value !== null && typeof value === "object")
36737
+ excludedViews.set(value, view);
36738
+ }
36449
36739
  return {
36450
36740
  key,
36451
36741
  kind: "excluded",
36452
- content: typeof value === "string" ? value : safeStableStringify(value),
36742
+ content: "",
36743
+ contentHash: partJsonMemo.get(key, view, () => typeof view === "string" ? partHash("excluded", view) : piJsonHash(view, () => safeStableStringify(view)), true),
36453
36744
  tokens: 0
36454
36745
  };
36455
36746
  }
@@ -36474,7 +36765,7 @@ function finalizeParts(drafts, pendingDropTagNumbers, protectedTagNumbers, newes
36474
36765
  u += uTokens;
36475
36766
  return {
36476
36767
  key: draft.key,
36477
- contentHash: partHash(draft.kind, draft.content),
36768
+ contentHash: draft.contentHash ?? partHash(draft.kind, draft.content),
36478
36769
  kind: draft.kind,
36479
36770
  tokens,
36480
36771
  uTokens,
@@ -36494,6 +36785,19 @@ function finalizeParts(drafts, pendingDropTagNumbers, protectedTagNumbers, newes
36494
36785
  };
36495
36786
  }
36496
36787
  function measurePiTailHygiene(input) {
36788
+ partJsonMemo.beginPass();
36789
+ excludedViews = new WeakMap;
36790
+ let providerApi = input.providerApi;
36791
+ if (providerApi === undefined) {
36792
+ for (let index = input.messages.length - 1;index >= 0; index--) {
36793
+ const message = input.messages[index];
36794
+ if (isRecord5(message) && message.role === "assistant" && typeof message.api === "string") {
36795
+ providerApi = message.api;
36796
+ break;
36797
+ }
36798
+ }
36799
+ }
36800
+ const excludedDraft = (key, value) => excludedDraftForApi(key, value, providerApi);
36497
36801
  const { arcs, arcByPart, messageIds } = collectToolArcs(input);
36498
36802
  const { messageTags, tagsByNumber } = attributeTags(input, arcs, messageIds);
36499
36803
  const drafts = [];
@@ -36504,7 +36808,7 @@ function measurePiTailHygiene(input) {
36504
36808
  if (messageIndex === input.messages.length - 1)
36505
36809
  newestMessagePartStart = drafts.length;
36506
36810
  const messageKey = messageIds[messageIndex] ?? `pi-message:${messageIndex}`;
36507
- if (!isRecord4(raw)) {
36811
+ if (!isRecord5(raw)) {
36508
36812
  drafts.push(excludedDraft(`${messageKey}\x00excluded`, raw));
36509
36813
  continue;
36510
36814
  }
@@ -36543,7 +36847,7 @@ function measurePiTailHygiene(input) {
36543
36847
  for (let partIndex = 0;partIndex < raw.content.length; partIndex += 1) {
36544
36848
  const part = raw.content[partIndex];
36545
36849
  const key = `${messageKey}\x00${partIndex}`;
36546
- if (!isRecord4(part)) {
36850
+ if (!isRecord5(part)) {
36547
36851
  drafts.push(excludedDraft(`${key}\x00excluded`, part));
36548
36852
  continue;
36549
36853
  }
@@ -36613,7 +36917,7 @@ function measurePiTailHygiene(input) {
36613
36917
  drafts.push(excludedDraft(`${key}\x00toolInput`, part));
36614
36918
  continue;
36615
36919
  }
36616
- const content = safeStableStringify(part.arguments);
36920
+ const content = partJsonMemo.get(`${key}\x00input`, part.arguments, () => safeStableStringify(part.arguments));
36617
36921
  if (!content) {
36618
36922
  drafts.push(excludedDraft(`${key}\x00toolInput`, part));
36619
36923
  } else {
@@ -36663,7 +36967,7 @@ function refreshPiTailHygieneBaseline(input) {
36663
36967
  })
36664
36968
  };
36665
36969
  const now = input.now ?? Date.now();
36666
- const refrozen = (mismatch) => ({
36970
+ const refrozen = (mismatch) => rememberCurrentToolOutputCount({
36667
36971
  ...freezeTailHygieneMeasurement(measured),
36668
36972
  protectedToolsPolicy,
36669
36973
  hygieneUnitsVersion: frozenCalibration.hygieneUnitsVersion,
@@ -36677,7 +36981,7 @@ function refreshPiTailHygieneBaseline(input) {
36677
36981
  channel1PostReduceGrace: input.previous?.channel1PostReduceGrace,
36678
36982
  lastPrefixMismatch: mismatch,
36679
36983
  realUserTurnCount: rawMeasured.realUserTurnCount
36680
- });
36984
+ }, measured);
36681
36985
  if (input.cacheBusting || !input.previous)
36682
36986
  return refrozen();
36683
36987
  const prefix = compareMeasuredTailPrefix(input.previous.baselineParts, measured.parts);
@@ -36691,7 +36995,7 @@ function refreshPiTailHygieneBaseline(input) {
36691
36995
  if (part.kind !== "toolOutput" || !part.protected)
36692
36996
  turnDeltaU += part.uTokens;
36693
36997
  }
36694
- return {
36998
+ return rememberCurrentToolOutputCount({
36695
36999
  ...input.previous,
36696
37000
  protectedToolsPolicy,
36697
37001
  turnDeltaU,
@@ -36701,7 +37005,7 @@ function refreshPiTailHygieneBaseline(input) {
36701
37005
  contentSignature: measured.contentSignature,
36702
37006
  lastPrefixMismatch: undefined,
36703
37007
  realUserTurnCount: rawMeasured.realUserTurnCount
36704
- };
37008
+ }, measured);
36705
37009
  }
36706
37010
  function effectivePiTailHygiene(baseline) {
36707
37011
  const t = Math.ceil(Math.max(0, baseline.baselineT + baseline.turnDeltaT));
@@ -36711,7 +37015,7 @@ function effectivePiTailHygiene(baseline) {
36711
37015
  function measurePiToolResultDelta(content) {
36712
37016
  let tokens = 0;
36713
37017
  for (const part of content) {
36714
- if (!isRecord4(part))
37018
+ if (!isRecord5(part))
36715
37019
  continue;
36716
37020
  if (part.type === "text" && typeof part.text === "string") {
36717
37021
  const text = stripChannel1ReminderSpans(part.text);
@@ -39160,7 +39464,6 @@ ${renderUserFacingFailure("recomp_unavailable")}`;
39160
39464
  }
39161
39465
  }
39162
39466
  }
39163
-
39164
39467
  // ../plugin/src/hooks/magic-context/compartment-runner.ts
39165
39468
  var activeRuns = new Map;
39166
39469
  function getActiveCompartmentRun(sessionId) {
@@ -39181,7 +39484,7 @@ function withPublishedCallback(deps) {
39181
39484
  };
39182
39485
  }
39183
39486
  function startLeaseRenewal(deps, holderId) {
39184
- return setInterval(() => {
39487
+ return inRootAsyncContext(() => setInterval(() => {
39185
39488
  try {
39186
39489
  if (!renewCompartmentLease(deps.db, deps.sessionId, holderId)) {
39187
39490
  sessionLog(deps.sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
@@ -39189,7 +39492,7 @@ function startLeaseRenewal(deps, holderId) {
39189
39492
  } catch (err) {
39190
39493
  sessionLog(deps.sessionId, `compartment lease renewal threw; publish will be skipped if holder is stale (${err instanceof Error ? err.message : String(err)})`);
39191
39494
  }
39192
- }, COMPARTMENT_LEASE_RENEWAL_MS);
39495
+ }, COMPARTMENT_LEASE_RENEWAL_MS));
39193
39496
  }
39194
39497
  async function executeContextRecompWithResult(deps, options = {}) {
39195
39498
  const { sessionId } = deps;
@@ -41126,9 +41429,9 @@ class PiContextBudget {
41126
41429
  elapsed() {
41127
41430
  return this.now() - this.startedAt;
41128
41431
  }
41129
- remainingWork() {
41432
+ remainingWork = () => {
41130
41433
  return Math.max(0, PI_CONTEXT_BUDGET.workMs - this.elapsed());
41131
- }
41434
+ };
41132
41435
  remainingOutcome = () => Math.max(0, PI_CONTEXT_BUDGET.outcomeMs - this.elapsed());
41133
41436
  writerAllowance() {
41134
41437
  return Math.max(0, Math.min(PI_CONTEXT_BUDGET.writerMs, this.remainingWork() - PI_CONTEXT_BUDGET.completionReserveMs));
@@ -42513,6 +42816,7 @@ async function executeGuardedRun(opts, holderId, leaseKey, acquisition) {
42513
42816
  pending,
42514
42817
  fullPool: roundState.snapshot,
42515
42818
  shouldContinue: () => meter.shouldContinue(),
42819
+ remainingWallMs: () => Math.max(0, config.budgets.wallMs - meter.getState().spent.wallMs),
42516
42820
  recordUsage: (delta) => meter.recordUsage(delta),
42517
42821
  recordToolStep: () => meter.recordToolStep(),
42518
42822
  refuseToolStep: () => meter.refuseToolStep()
@@ -43012,6 +43316,14 @@ function createPiMaintenanceSeams(deps) {
43012
43316
  function runPiMaintenanceFactory(deps) {
43013
43317
  const nowMs = deps.nowMs ?? Date.now;
43014
43318
  const startedAtMs = nowMs();
43319
+ if (!deps.options.model || deps.options.model.trim().length === 0) {
43320
+ return Promise.resolve({
43321
+ startedAtMs,
43322
+ finishedAtMs: nowMs(),
43323
+ ok: false,
43324
+ error: MAINTENANCE_MODEL_UNCONFIGURED_MESSAGE
43325
+ });
43326
+ }
43015
43327
  return new Promise((resolve, reject) => {
43016
43328
  queueMicrotask(() => {
43017
43329
  (async () => {
@@ -44624,11 +44936,22 @@ class PiDegradedPassError extends Error {
44624
44936
  code = "PI_DEGRADED_PASS";
44625
44937
  recoverable = true;
44626
44938
  constructor(site, options) {
44627
- super("Magic Context could not finish preparing this turn; send your message again", options);
44939
+ super(describeDegradedPassFailure(site, options?.cause), options);
44628
44940
  this.site = site;
44629
44941
  this.name = "PiDegradedPassError";
44630
44942
  }
44631
44943
  }
44944
+ function piFallbackRefusal(site, cause) {
44945
+ const seen = new Set;
44946
+ let current = cause;
44947
+ while (current && typeof current === "object" && !seen.has(current)) {
44948
+ seen.add(current);
44949
+ if (current instanceof PiStorageBusyError || isTransientSqliteError(current) || isTransientPiStorageError(current))
44950
+ return new PiStorageBusyError({ cause });
44951
+ current = current.cause;
44952
+ }
44953
+ return cause instanceof PiDegradedPassError ? cause : new PiDegradedPassError(site, { cause });
44954
+ }
44632
44955
  function estimatePiOutgoingInputTokens(messages, observed, measuredPrefix, contextLimit) {
44633
44956
  if (!observed || !Number.isFinite(observed.systemTokens) || observed.systemTokens <= 0 || !Number.isFinite(observed.toolDefinitionTokens) || observed.toolDefinitionTokens < 0 || !hasTokenizerForFit())
44634
44957
  return { tokens: 0, trusted: false };
@@ -44761,7 +45084,7 @@ function estimatePiOutgoingInputTokens(messages, observed, measuredPrefix, conte
44761
45084
  function assertPiRawFallbackFits(messages, contextLimit, log, cause, observed, measuredPrefix) {
44762
45085
  if (!contextLimit || !Number.isFinite(contextLimit) || !observed || !Number.isFinite(observed.systemTokens) || observed.systemTokens <= 0 || !Number.isFinite(observed.toolDefinitionTokens) || observed.toolDefinitionTokens < 0 || observed.envelopeBytes !== undefined && (!Number.isFinite(observed.envelopeBytes) || observed.envelopeBytes < 0)) {
44763
45086
  log("raw_fallback_refused completeness=partial");
44764
- throw new PiStorageBusyError({ cause });
45087
+ throw piFallbackRefusal("fallback-metadata-incomplete", cause);
44765
45088
  }
44766
45089
  const measured = measuredPrefix && measuredPrefix.modelKey === observed.modelKey && observed.envelopeSignature && measuredPrefix.envelopeSignature === observed.envelopeSignature && Number.isSafeInteger(measuredPrefix.inputTokens) && measuredPrefix.inputTokens > 0 ? measuredPrefix : undefined;
44767
45090
  const baseInputTokens = measured?.inputTokens ?? 0;
@@ -44781,7 +45104,7 @@ function assertPiRawFallbackFits(messages, contextLimit, log, cause, observed, m
44781
45104
  const proxyTokens = baseInputTokens + Math.ceil(bytes / 4);
44782
45105
  if (serializationFailed || proxyTokens > contextLimit) {
44783
45106
  log(`raw_fallback_over_context_limit proxy_bytes=${bytes} proxy_tokens=${proxyTokens} limit=${contextLimit} early_abort=true serialization_failed=${serializationFailed}`);
44784
- throw new PiStorageBusyError({ cause });
45107
+ throw piFallbackRefusal(serializationFailed ? "fallback-serialization-failed" : "fallback-over-context-limit", cause);
44785
45108
  }
44786
45109
  const complete = pricedMessages.every((message) => {
44787
45110
  if (!message || typeof message !== "object")
@@ -44792,7 +45115,7 @@ function assertPiRawFallbackFits(messages, contextLimit, log, cause, observed, m
44792
45115
  const raw = tokenizePiMessages([...pricedMessages]);
44793
45116
  if (!hasTokenizerForFit()) {
44794
45117
  log("raw_fallback_refused completeness=partial tokenizer=unavailable");
44795
- throw new PiStorageBusyError({ cause });
45118
+ throw piFallbackRefusal("fallback-tokenizer-unavailable", cause);
44796
45119
  }
44797
45120
  const tokens = baseInputTokens + providerMass({
44798
45121
  system: measured ? 0 : observed.systemTokens,
@@ -44801,7 +45124,7 @@ function assertPiRawFallbackFits(messages, contextLimit, log, cause, observed, m
44801
45124
  }, observed.calibration ?? calibrationForModelKey(observed.modelKey), true);
44802
45125
  if (!complete || !Number.isFinite(tokens) || tokens <= 0 || tokens > contextLimit || serializationFailed || proxyTokens > contextLimit) {
44803
45126
  log(`raw_fallback_over_context_limit proxy_bytes=${bytes} proxy_tokens=${proxyTokens} limit=${contextLimit} early_abort=true serialization_failed=${serializationFailed}`);
44804
- throw new PiStorageBusyError({ cause });
45127
+ throw piFallbackRefusal(complete ? "fallback-over-context-limit" : "fallback-messages-incomplete", cause);
44805
45128
  }
44806
45129
  }
44807
45130
  function classifyPiOutgoingFit(estimate, limit) {
@@ -44840,9 +45163,12 @@ function parseNumbers(value) {
44840
45163
  }
44841
45164
  return value;
44842
45165
  }
44843
- function readPiCommittedServedNumbers(db, sessionId) {
45166
+ function readPiCommittedServedNumbers(db, sessionId, requireEnvelope = false) {
44844
45167
  try {
44845
- return parseNumbers(readReplayEnvelope(db, sessionId).piServedTagNumbers);
45168
+ const numbers = readReplayEnvelope(db, sessionId).piServedTagNumbers;
45169
+ if (requireEnvelope && numbers === undefined)
45170
+ throw new PiServedEnvelopeUnavailable("No committed Pi served-number envelope for torn legacy tail");
45171
+ return parseNumbers(numbers);
44846
45172
  } catch (error) {
44847
45173
  if (error instanceof ReplayDocumentError) {
44848
45174
  throw new PiServedEnvelopeUnavailable("Pi served-number envelope is unreadable", { cause: error });
@@ -45735,23 +46061,33 @@ function stripThinkingParts(message) {
45735
46061
  assistant.content = strippedContent;
45736
46062
  return before - strippedContent.length;
45737
46063
  }
46064
+
46065
+ class PiThinkingBindingRecoveryRefusal extends EmergencyFailClosedError {
46066
+ constructor() {
46067
+ super("Pi thinking-binding recovery has no valid new strip shape; this turn was not sent. Send a new message or start a new session.", { code: "pi_thinking_binding_no_valid_shape" });
46068
+ this.name = "PiThinkingBindingRecoveryRefusal";
46069
+ }
46070
+ }
45738
46071
  function applyPiThinkingBindingRecovery(args) {
45739
46072
  const protectedMessages = latestAssistantTurnMessages(args.messages);
45740
46073
  const frozenEntryIds = frozenBindingEntryIds(args.db, args.sessionId);
45741
- const flagTargetCandidate = isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
46074
+ const flagTargetCandidate = !args.replayOnly && isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
45742
46075
  const flagTarget = flagTargetCandidate?.startsWith(LATEST_THINKING_RESTORE) ? null : flagTargetCandidate;
45743
46076
  let applied = null;
45744
46077
  if (flagTarget) {
45745
46078
  const entryIds = new Set;
45746
46079
  for (let index = 0;index < args.messages.length; index += 1) {
45747
- if (protectedMessages.has(args.messages[index]))
46080
+ if (!hasThinkingPart(args.messages[index]))
45748
46081
  continue;
45749
46082
  const entryId = args.entryIds[index];
45750
- if (entryId && hasThinkingPart(args.messages[index]))
45751
- entryIds.add(entryId);
46083
+ if (!entryId)
46084
+ throw new PiThinkingBindingRecoveryRefusal;
46085
+ entryIds.add(entryId);
45752
46086
  }
45753
46087
  const newEntryIds = [...entryIds].filter((id) => !frozenEntryIds.has(id));
45754
- if (newEntryIds.length === 0 || addMergedReasoningStrippedIds(args.db, args.sessionId, [
46088
+ if (newEntryIds.length === 0)
46089
+ throw new PiThinkingBindingRecoveryRefusal;
46090
+ if (addMergedReasoningStrippedIds(args.db, args.sessionId, [
45755
46091
  ...newEntryIds.map(thinkingBindingRecoveryFrozenId),
45756
46092
  ...args.endOfPassOrder ? [THINKING_BINDING_STRIP_ORDER_END_MARKER] : []
45757
46093
  ])) {
@@ -45759,7 +46095,8 @@ function applyPiThinkingBindingRecovery(args) {
45759
46095
  frozenEntryIds.add(id);
45760
46096
  applied = { flagTarget, entryIds: [...entryIds] };
45761
46097
  reportBindingRecovery(args.sessionId, args.report, `thinking-binding recovery consumed on context pass target=${flagTarget} entries=${applied.entryIds.length} [${applied.entryIds.join(",")}]`);
45762
- }
46098
+ } else
46099
+ throw new PiThinkingBindingRecoveryRefusal;
45763
46100
  }
45764
46101
  const restoringActiveTurn = latestThinkingRecoveryCoversActiveTurn({
45765
46102
  db: args.db,
@@ -45776,6 +46113,12 @@ function applyPiThinkingBindingRecovery(args) {
45776
46113
  }
45777
46114
  return applied;
45778
46115
  }
46116
+ function hasUnfrozenPiThinking(args) {
46117
+ return isPrefixBoundThinkingModel(args.provider, args.model) && args.messages.some((message, index) => {
46118
+ const entryId = args.entryIds[index];
46119
+ return hasThinkingPart(message) && (!entryId || !args.frozen.has(entryId));
46120
+ });
46121
+ }
45779
46122
  function applyPiProactiveThinkingStrip(args) {
45780
46123
  if (!args.cacheBustingPass)
45781
46124
  return null;
@@ -47764,6 +48107,15 @@ var afterFallbackAdoptionForTests;
47764
48107
  var pendingDecisionLogObserverForTests;
47765
48108
  var mutationGateObserverForTests;
47766
48109
  var lkgRecoveryLogObserverForTests;
48110
+ var pipelineResultObserverForTests;
48111
+ function observePipelineResultForTests(sessionId, result) {
48112
+ pipelineResultObserverForTests?.(sessionId, {
48113
+ prefixBustPermitted: result.prefixBustPermitted,
48114
+ bustedThisPass: result.bustedThisPass,
48115
+ identityRebuilt: result.identityRebuilt,
48116
+ signedPrefixRewritten: result.signedPrefixRewritten
48117
+ });
48118
+ }
47767
48119
  var variantChangeLogObserverForTests;
47768
48120
  var beforePipelineForTests;
47769
48121
  function logPiLkgRecovery(sessionId, message) {
@@ -47784,6 +48136,7 @@ function signalPiSystemHashChange(sessionId, hash) {
47784
48136
  signalPiSystemPromptRefresh(sessionId);
47785
48137
  if (!pendingMaterializationSessions.has(sessionId))
47786
48138
  signalPiPendingMaterialization(sessionId, "system_hash");
48139
+ notePiSystemPromptHashChange(sessionId);
47787
48140
  }
47788
48141
  function deferPiDeliveredSystemRefresh(sessionId, ownsHistory) {
47789
48142
  deferDeliveredSystemRefresh({
@@ -47794,6 +48147,7 @@ function deferPiDeliveredSystemRefresh(sessionId, ownsHistory) {
47794
48147
  }, sessionId, ownsHistory);
47795
48148
  }
47796
48149
  var sessionsByProject = new Map;
48150
+ var lastServedSystemHashBySession = new Map;
47797
48151
  var lastSeenProjectIdentityBySession = new Map;
47798
48152
  var persistedProjectIdentityBySession = new Map;
47799
48153
  var sessionProjectBindingFailureLogAtBySession = new Map;
@@ -49430,7 +49784,7 @@ function registerPiContextHandler(pi, baseOptions) {
49430
49784
  const passGuard = { assert: assertCurrentPass, wait: guardAwait };
49431
49785
  guardSqliteTransformPass({
49432
49786
  assert: assertCurrentPass,
49433
- remainingMs: () => budget.remainingWork()
49787
+ remainingMs: budget.remainingWork
49434
49788
  });
49435
49789
  budget.stage = "schema";
49436
49790
  const toolWireSchema = await guardAwait(loadPiToolWireSchema());
@@ -49438,6 +49792,15 @@ function registerPiContextHandler(pi, baseOptions) {
49438
49792
  let rawMessageCount = 0;
49439
49793
  let rawFallbackLimit;
49440
49794
  let sessionIdForError;
49795
+ let systemHashForPass;
49796
+ const recordServedSystemHash = () => {
49797
+ if (budget.sideTurn || !sessionIdForError)
49798
+ return;
49799
+ if (systemHashForPass === undefined)
49800
+ lastServedSystemHashBySession.delete(sessionIdForError);
49801
+ else
49802
+ lastServedSystemHashBySession.set(sessionIdForError, systemHashForPass);
49803
+ };
49441
49804
  let lkgPassSnapshot;
49442
49805
  let lkgCompactionOff = baseOptions.compactionOff === true;
49443
49806
  let lkgOptions = baseOptions;
@@ -49459,6 +49822,11 @@ function registerPiContextHandler(pi, baseOptions) {
49459
49822
  return;
49460
49823
  }
49461
49824
  sessionIdForError = sessionId;
49825
+ try {
49826
+ const effectiveSystem = readHostSystemPrompt(ctx);
49827
+ if (effectiveSystem !== undefined)
49828
+ systemHashForPass = crypto3.createHash("sha256").update(effectiveSystem).digest("hex");
49829
+ } catch {}
49462
49830
  let piUsage;
49463
49831
  try {
49464
49832
  piUsage = ctx.getContextUsage?.();
@@ -49964,6 +50332,9 @@ function registerPiContextHandler(pi, baseOptions) {
49964
50332
  messages: event.messages,
49965
50333
  lkgEntryIds: event.messages.map((message) => lkgInputIdByRef.get(message)),
49966
50334
  smartDrops: options.smartDrops === true,
50335
+ effectiveSystemHash: systemHashForPass,
50336
+ userFlushOrigin: hasPendingMaterializationOrigin(pendingMaterializationSessions, sessionId, "user_flush"),
50337
+ reclaimUsableWindow: usageContextLimit ?? 0,
49967
50338
  protectedTools: options.protectedTools,
49968
50339
  protectedTags: options.protectedTags ?? 20,
49969
50340
  protectedTokens: options.protectedTokens,
@@ -50040,6 +50411,7 @@ function registerPiContextHandler(pi, baseOptions) {
50040
50411
  }
50041
50412
  };
50042
50413
  let result = await budget.waitMandatory(runPipeline(pipelineArgs));
50414
+ observePipelineResultForTests(sessionId, result);
50043
50415
  logTransformTiming(sessionId, "runPipeline", tRunPipeline);
50044
50416
  if (result.identityRebuilt || result.lkgFrozenReleaseReason)
50045
50417
  schedulerDecision = "execute";
@@ -50049,7 +50421,7 @@ function registerPiContextHandler(pi, baseOptions) {
50049
50421
  assertCurrentPass();
50050
50422
  recordSuccessfulTaggedMessageIds(sessionId, taggedInputEntryIds ?? strictEntryIds, branchEntries);
50051
50423
  }
50052
- const piDecisionSnapshotNewestAssistant = result.bustedThisPass ? findNewestPiAssistantEntryId(branchEntries) : undefined;
50424
+ const piDecisionSnapshotNewestAssistant = result.bustedThisPass || hasPendingPiSystemPromptHashChange(sessionId) ? findNewestPiAssistantEntryId(branchEntries) : undefined;
50053
50425
  const publishTransformDecision = !budget.sideTurn && piDecisionSnapshotNewestAssistant !== undefined ? () => {
50054
50426
  recordPendingPiTransformDecision(sessionId, {
50055
50427
  tsMs: Date.now(),
@@ -50075,7 +50447,7 @@ function registerPiContextHandler(pi, baseOptions) {
50075
50447
  if (historian && !options.compactionOff && budget.remainingWork() > 0) {
50076
50448
  guardSqliteTransformPass({
50077
50449
  assert: budget.assert,
50078
- remainingMs: () => budget.remainingWork()
50450
+ remainingMs: budget.remainingWork
50079
50451
  });
50080
50452
  try {
50081
50453
  budget.assert();
@@ -50093,6 +50465,7 @@ function registerPiContextHandler(pi, baseOptions) {
50093
50465
  historian,
50094
50466
  isFirstContextPassForSession,
50095
50467
  activeTags: result.activeTags,
50468
+ eligiblePendingDropTagIds: result.eligiblePendingDropTagIds,
50096
50469
  rawMessageProvider,
50097
50470
  taggerFloor,
50098
50471
  sessionMeta,
@@ -50150,7 +50523,7 @@ function registerPiContextHandler(pi, baseOptions) {
50150
50523
  if (options.autoSearch?.enabled && !options.compactionOff && result.prefixMutationAllowed && budget.remainingWork() > PI_CONTEXT_BUDGET.searchMs) {
50151
50524
  guardSqliteTransformPass({
50152
50525
  assert: budget.assert,
50153
- remainingMs: () => budget.remainingWork()
50526
+ remainingMs: budget.remainingWork
50154
50527
  });
50155
50528
  try {
50156
50529
  outputMessages = await guardAwait(runAutoSearchHintForPi({
@@ -50209,14 +50582,15 @@ function registerPiContextHandler(pi, baseOptions) {
50209
50582
  hostEditBeforeNewestThinking = stickyBefore === null || [...stickyAfter].some((key) => !stickyBefore.has(key)) || JSON.stringify(getPersistedTodoSyntheticAnchor(options.db, sessionId)) !== todoAnchorBefore;
50210
50583
  } catch {}
50211
50584
  const outputEntryIds = resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message) ?? bindingEntryIdByRef.get(message)).map((entryId) => entryId ?? undefined);
50212
- const thinkingBindingRecoveryApplied = bindingStripOrder === "end" && !result.signedPrefixRewritten ? applyPiThinkingBindingRecovery({
50585
+ const thinkingBindingRecoveryApplied = bindingStripOrder === "end" ? applyPiThinkingBindingRecovery({
50213
50586
  db: options.db,
50214
50587
  sessionId,
50215
50588
  messages: outputMessages,
50216
50589
  entryIds: outputEntryIds,
50217
50590
  provider: lkgProviderKey ?? undefined,
50218
50591
  model: ctx.model?.id,
50219
- endOfPassOrder: true
50592
+ endOfPassOrder: true,
50593
+ replayOnly: result.signedPrefixRewritten && result.prefixBustPermitted
50220
50594
  }) : startOfPassBindingRecovery;
50221
50595
  if (shouldRunPiProactiveThinkingStrip({
50222
50596
  compactionOff: options.compactionOff === true,
@@ -50231,7 +50605,7 @@ function registerPiContextHandler(pi, baseOptions) {
50231
50605
  entryIds: outputEntryIds,
50232
50606
  provider: lkgProviderKey ?? undefined,
50233
50607
  model: ctx.model?.id,
50234
- cacheBustingPass: !!recovery.rebuildReason || result.signedPrefixRewritten || result.prefixBustPermitted && !result.strictThinkingKept && (isCacheBusting || result.prefixRebuildAlreadyPriced || result.executedWorkThisPass && (result.prefixEditBesidesReasoningTrim || hostEditBeforeNewestThinking)),
50608
+ cacheBustingPass: result.prefixBustPermitted && !result.strictThinkingKept && (result.signedPrefixRewritten || isCacheBusting || result.prefixRebuildAlreadyPriced || result.executedWorkThisPass && (result.prefixEditBesidesReasoningTrim || hostEditBeforeNewestThinking)),
50235
50609
  stripActiveTurn: result.signedPrefixRewritten,
50236
50610
  requirePersistence: !!recovery.rebuildReason,
50237
50611
  report: (line) => sessionLog(sessionId, line)
@@ -50280,6 +50654,7 @@ function registerPiContextHandler(pi, baseOptions) {
50280
50654
  protectedTools: options.protectedTools,
50281
50655
  pendingDropTagNumbers,
50282
50656
  stableId,
50657
+ providerApi: ctx.model?.api,
50283
50658
  protectedToolTags: protectedToolTagNumbers(result.channelBaselineSnapshot.activeTags, options.protectedTools),
50284
50659
  syntheticLeadingCount: result.syntheticLeadingCount,
50285
50660
  cacheBusting: result.bustedThisPass,
@@ -50439,7 +50814,7 @@ function registerPiContextHandler(pi, baseOptions) {
50439
50814
  entryIds: retryIds.map((id) => id ?? undefined),
50440
50815
  provider: ctx.model?.provider,
50441
50816
  model: ctx.model?.id,
50442
- cacheBustingPass: true,
50817
+ cacheBustingPass: result.prefixBustPermitted,
50443
50818
  stripActiveTurn: true
50444
50819
  });
50445
50820
  estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, undefined, rawFallbackLimit);
@@ -50497,6 +50872,7 @@ function registerPiContextHandler(pi, baseOptions) {
50497
50872
  startGuard: budget.assert,
50498
50873
  isFirstContextPassForSession: false,
50499
50874
  activeTags: result.activeTags,
50875
+ eligiblePendingDropTagIds: result.eligiblePendingDropTagIds,
50500
50876
  rawMessageProvider,
50501
50877
  ordinalAlignment: ordinalAlignment(),
50502
50878
  taggerFloor,
@@ -50551,7 +50927,7 @@ function registerPiContextHandler(pi, baseOptions) {
50551
50927
  entryIds: resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message)).map((id) => id ?? undefined),
50552
50928
  provider: ctx.model?.provider,
50553
50929
  model: ctx.model?.id,
50554
- cacheBustingPass: result.bustedThisPass,
50930
+ cacheBustingPass: result.prefixBustPermitted,
50555
50931
  stripActiveTurn: result.signedPrefixRewritten
50556
50932
  });
50557
50933
  estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, undefined, outgoingFitLimit);
@@ -50574,7 +50950,7 @@ function registerPiContextHandler(pi, baseOptions) {
50574
50950
  budget.assertOutcome();
50575
50951
  guardSqliteTransformPass({
50576
50952
  assert: budget.assertOutcome,
50577
- remainingMs: () => Math.max(0, PI_CONTEXT_BUDGET.outcomeMs - budget.elapsed())
50953
+ remainingMs: budget.remainingOutcome
50578
50954
  });
50579
50955
  if (!budget.sideTurn && !lkgCompactionOff && lkgPassSnapshot && (rawMessageCount > 0 || result.servedTagNumbers?.size)) {
50580
50956
  let hostEnvelopeSignature;
@@ -50586,7 +50962,7 @@ function registerPiContextHandler(pi, baseOptions) {
50586
50962
  readPiCommittedServedNumbers(options.db, sessionId);
50587
50963
  serializedOutput = lkgCoordinator.captureAppliedPass({
50588
50964
  publishServedNumbers: () => publishPiServedNumbers(options.db, sessionId, [
50589
- ...getPiServedTagNumbers(sessionId),
50965
+ ...getPiServedTagNumbers(sessionId, undefined, options.db),
50590
50966
  ...result.servedTagNumbers ?? []
50591
50967
  ]),
50592
50968
  assertCurrentPass: budget.assertOutcome,
@@ -50614,7 +50990,7 @@ function registerPiContextHandler(pi, baseOptions) {
50614
50990
  options.db.transaction(() => {
50615
50991
  budget.assertOutcome();
50616
50992
  publishPiServedNumbers(options.db, sessionId, [
50617
- ...getPiServedTagNumbers(sessionId),
50993
+ ...getPiServedTagNumbers(sessionId, undefined, options.db),
50618
50994
  ...result.servedTagNumbers ?? []
50619
50995
  ]);
50620
50996
  }).immediate();
@@ -50654,6 +51030,7 @@ function registerPiContextHandler(pi, baseOptions) {
50654
51030
  publishTransformDecision?.();
50655
51031
  if (!budget.sideTurn && result.strictThinkingKept && !result.signedPrefixRewritten && !result.materialized)
50656
51032
  recordPiHeldSystemHead(sessionId, (ownsHistory) => deferPiDeliveredSystemRefresh(sessionId, ownsHistory));
51033
+ recordServedSystemHash();
50657
51034
  return { messages: outputMessages };
50658
51035
  } catch (err) {
50659
51036
  if (budget.isCancelled(err))
@@ -50663,7 +51040,7 @@ function registerPiContextHandler(pi, baseOptions) {
50663
51040
  if (err instanceof PiServedIdentityError) {
50664
51041
  if (sessionIdForError)
50665
51042
  clearPiLkgSessionState(sessionIdForError);
50666
- throw new PiStorageBusyError({ cause: err });
51043
+ throw piFallbackRefusal("served-identity-unavailable", err);
50667
51044
  }
50668
51045
  if (err instanceof PiLkgAdmissionReplay && sessionIdForError) {
50669
51046
  budget.assertOutcome();
@@ -50676,6 +51053,7 @@ function registerPiContextHandler(pi, baseOptions) {
50676
51053
  });
50677
51054
  budget.assertOutcome();
50678
51055
  budget.recovery = err.recovery;
51056
+ recordServedSystemHash();
50679
51057
  return { messages: err.messages };
50680
51058
  }
50681
51059
  if ((isFailClosedBlockingError(err) || err instanceof EmergencyFailClosedError) && !baseOptions.compactionOff)
@@ -50742,6 +51120,7 @@ function registerPiContextHandler(pi, baseOptions) {
50742
51120
  });
50743
51121
  budget.assertOutcome();
50744
51122
  budget.recovery = "fitting LKG replay";
51123
+ recordServedSystemHash();
50745
51124
  return { messages: replay.messages };
50746
51125
  }
50747
51126
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG unavailable (${replay.reason}); refusing unreduced ${rawMessageCount}-message input`);
@@ -50762,11 +51141,7 @@ function registerPiContextHandler(pi, baseOptions) {
50762
51141
  if (err instanceof PiDegradedPassError && err.site === "outgoing-over-window")
50763
51142
  throw contextRefusalError("The request exceeds the model's context window after reclaim, and no fitting saved request is available; this turn was not sent.");
50764
51143
  if (!lkgCompactionOff && sessionIdForError) {
50765
- throw new PiStorageBusyError({
50766
- cause: err instanceof PiDegradedPassError ? err : new PiDegradedPassError("context-handler-failed", {
50767
- cause: err
50768
- })
50769
- });
51144
+ throw piFallbackRefusal("context-handler-failed", err);
50770
51145
  }
50771
51146
  log(`[magic-context][pi] context handler failed (continuing without mutation): ${message}`, stack);
50772
51147
  return;
@@ -50843,7 +51218,7 @@ function resolveHistoryBudgetTokensForPi(args) {
50843
51218
  }) / 100) * historyBudgetPercentage);
50844
51219
  }
50845
51220
  function startPiCompartmentLeaseRenewal(db, sessionId, holderId) {
50846
- return setInterval(() => {
51221
+ return inRootAsyncContext(() => setInterval(() => {
50847
51222
  try {
50848
51223
  if (!renewCompartmentLease(db, sessionId, holderId)) {
50849
51224
  sessionLog(sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
@@ -50851,7 +51226,7 @@ function startPiCompartmentLeaseRenewal(db, sessionId, holderId) {
50851
51226
  } catch (err) {
50852
51227
  sessionLog(sessionId, `compartment lease renewal threw; publish will be skipped if holder is stale (${err instanceof Error ? err.message : String(err)})`);
50853
51228
  }
50854
- }, COMPARTMENT_LEASE_RENEWAL_MS);
51229
+ }, COMPARTMENT_LEASE_RENEWAL_MS));
50855
51230
  }
50856
51231
  function ensureRunnablePiBoundaryForTests(snapshot) {
50857
51232
  if (true) {
@@ -50940,15 +51315,21 @@ function maybeLaunchPiMaintenance(args) {
50940
51315
  }
50941
51316
  function resolvePiMaintenanceLauncher(db, projectIdentity, commandRuntime) {
50942
51317
  return getMaintenanceLauncher(db, projectIdentity, {
50943
- getConfig: () => ({
50944
- maintenance: {
50945
- enabled: maintenanceRuntime.current?.maintenance.enabled ?? commandRuntime?.()?.options?.enabled ?? false
50946
- }
50947
- }),
51318
+ getConfig: () => {
51319
+ const command = commandRuntime?.();
51320
+ const slot = maintenanceRuntime.current;
51321
+ const slotIsOurs = slot?.projectIdentity === projectIdentity;
51322
+ const enabled = command?.options?.enabled ?? (slotIsOurs ? slot?.maintenance.enabled : undefined) ?? command?.maintenanceConfig?.enabled ?? false;
51323
+ const model = command?.options?.model ?? (slotIsOurs ? slot?.maintenance.model : undefined) ?? command?.maintenanceConfig?.model;
51324
+ return { maintenance: { enabled, model } };
51325
+ },
50948
51326
  nowMs: Date.now,
50949
51327
  runFactory: (signals) => {
50950
- const options = maintenanceRuntime.current?.maintenance ?? commandRuntime?.()?.options;
50951
- const projectPath = maintenanceRuntime.current?.projectIdentity ?? commandRuntime?.()?.projectPath ?? projectIdentity;
51328
+ const command = commandRuntime?.();
51329
+ const slot = maintenanceRuntime.current;
51330
+ const slotIsOurs = slot?.projectIdentity === projectIdentity;
51331
+ const options = (slotIsOurs ? slot?.maintenance : undefined) ?? command?.options;
51332
+ const projectPath = (slotIsOurs ? slot?.projectIdentity : undefined) ?? command?.projectPath ?? projectIdentity;
50952
51333
  if (!options) {
50953
51334
  return Promise.resolve({
50954
51335
  startedAtMs: Date.now(),
@@ -50957,6 +51338,14 @@ function resolvePiMaintenanceLauncher(db, projectIdentity, commandRuntime) {
50957
51338
  error: "maintenance runtime slot cleared"
50958
51339
  });
50959
51340
  }
51341
+ if (!options.model || options.model.trim().length === 0) {
51342
+ return Promise.resolve({
51343
+ startedAtMs: Date.now(),
51344
+ finishedAtMs: Date.now(),
51345
+ ok: false,
51346
+ error: MAINTENANCE_MODEL_UNCONFIGURED_MESSAGE
51347
+ });
51348
+ }
50960
51349
  return runPiMaintenanceFactory({
50961
51350
  db,
50962
51351
  projectPath,
@@ -50985,7 +51374,7 @@ function spawnPiHistorianRun(args) {
50985
51374
  const holderId = crypto3.randomUUID();
50986
51375
  const controller = new AbortController;
50987
51376
  historianAbortControllers.set(sessionId, controller);
50988
- const runPromise = withoutSqliteTransformPass(() => (async () => {
51377
+ const runPromise = inRootAsyncContext(() => (async () => {
50989
51378
  await new Promise((resolve) => setTimeout(resolve, 0));
50990
51379
  if (controller.signal.aborted)
50991
51380
  return;
@@ -51323,7 +51712,7 @@ Historian previously failed ${failureCount} time(s), so Magic Context is retryin
51323
51712
  force: usage.percentage >= historianForceMaterializationPercentage && (usage.percentage >= 95 || getEmergencyInputSample(db, sessionId) === 0),
51324
51713
  explicitFlush: hasPendingMaterialization(sessionId),
51325
51714
  publishedHistory: args.publishedHistoryRide
51326
- }, true);
51715
+ }, args.eligiblePendingDropTagIds, true);
51327
51716
  if (!trigger.shouldFire) {
51328
51717
  sessionLog(sessionId, `historian trigger eval: shouldFire=false (no trigger condition met)`);
51329
51718
  try {
@@ -51485,29 +51874,37 @@ async function runPipeline(args) {
51485
51874
  let activeThinkingTurn = hasActiveAnthropicThinkingTurn(args.messages, args.resolvedProviderID, args.resolvedModelID);
51486
51875
  let protectedSignedPrefix = activeThinkingTurn && args.reasoningClearing?.prefixBound === true;
51487
51876
  let rewritingSignedPrefix = false;
51877
+ let isCacheBustingPass = args.recoveryRebuildReason === "lkg_recovery_input_rebuild" || (!!args.recoveryRebuildReason || args.forceSignedRebuildForFit === true) && hasUnfrozenPiThinking({
51878
+ messages: args.messages,
51879
+ entryIds: args.messages.map((message, index) => args.lkgEntryIds?.[index] ?? resolvePiRealStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
51880
+ frozen: frozenBindingEntryIds(args.db, args.sessionId),
51881
+ provider: args.resolvedProviderID,
51882
+ model: args.resolvedModelID
51883
+ });
51488
51884
  let workingForRewrite;
51489
- const stripForPrefixRewrite = () => {
51885
+ const stripForPrefixRewrite = (permitRewrite = true) => {
51886
+ isCacheBustingPass ||= permitRewrite;
51490
51887
  const messages = workingForRewrite ?? args.messages;
51491
51888
  const result = applyPiProactiveThinkingStrip({
51492
51889
  db: args.db,
51493
51890
  sessionId: args.sessionId,
51494
51891
  messages,
51495
- entryIds: messages.map((message, index) => resolvePiStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
51892
+ entryIds: messages.map((message, index) => args.lkgEntryIds?.[index] ?? resolvePiRealStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
51496
51893
  provider: args.resolvedProviderID,
51497
51894
  model: args.resolvedModelID,
51498
- cacheBustingPass: true,
51895
+ cacheBustingPass: isCacheBustingPass,
51499
51896
  stripActiveTurn: true,
51500
51897
  persistOnly: true,
51501
51898
  requirePersistence: !!args.recoveryRebuildReason
51502
51899
  });
51503
- if (activeThinkingTurn && !result)
51900
+ if (activeThinkingTurn && isCacheBustingPass && !result)
51504
51901
  throw new Error("Cannot persist signed thinking omission before prefix rewrite");
51505
51902
  rewritingSignedPrefix = true;
51506
51903
  activeThinkingTurn = false;
51507
51904
  protectedSignedPrefix = false;
51508
51905
  };
51509
51906
  if (args.recoveryRebuildReason || args.forceSignedRebuildForFit && protectedSignedPrefix)
51510
- stripForPrefixRewrite();
51907
+ stripForPrefixRewrite(isCacheBustingPass);
51511
51908
  const realStableId = (msg, index) => resolvePiRealStableId(msg, index, args.entryIds, args.entryIdByRef ?? undefined);
51512
51909
  const passFallbackIds = settlePiFallbackOccurrences(args.db, args.sessionId, piContentFallbackIds(args.messages, realStableId));
51513
51910
  const stableIdResolver = (msg, index) => realStableId(msg, index) ?? passFallbackIds[index] ?? resolvePiStableId(msg, index);
@@ -51521,6 +51918,7 @@ async function runPipeline(args) {
51521
51918
  let materializationSatisfiedThisPass = false;
51522
51919
  let pendingOpsAppliedThisPass = false;
51523
51920
  let pendingOpsDidMutate = false;
51921
+ let eligiblePendingDropTagIds;
51524
51922
  let heuristicOrReasoningDidMutate = false;
51525
51923
  let prefixEditBesidesReasoningTrim = false;
51526
51924
  const recordFirstApplicationWireEdit = (beforeNewerThinking) => {
@@ -51658,8 +52056,9 @@ async function runPipeline(args) {
51658
52056
  allowFreshContentionFallback: args.forceMaterialization === true || emergencyDropEligible
51659
52057
  } : undefined;
51660
52058
  const foldDueDecision = piM0State ? mustMaterializePi(piM0State, args.db, injectionPassSnapshot?.compartments, injectionPassSnapshot) : { value: false, reason: null };
51661
- const firstRenderBust = piM0State !== undefined && !(injectionPassSnapshot?.cachedRow?.cached_m0_bytes && injectionPassSnapshot.cachedRow.cached_m1_bytes);
52059
+ const firstRenderBust = piM0State !== undefined ? !(injectionPassSnapshot?.cachedRow?.cached_m0_bytes && injectionPassSnapshot.cachedRow.cached_m1_bytes) : args.stableIdSchemeCutover === true && args.readLastServedMessages?.() === null;
51662
52060
  let foldExecutedThisPass = false;
52061
+ const effectiveSystemChangedThisPass = args.effectiveSystemHash !== undefined && lastServedSystemHashBySession.has(args.sessionId) && lastServedSystemHashBySession.get(args.sessionId) !== args.effectiveSystemHash;
51663
52062
  let foldBustsServedPrefixThisPass = false;
51664
52063
  let publishedM1RefreshedThisPass = false;
51665
52064
  let prefixPreflightContended = false;
@@ -51689,7 +52088,7 @@ async function runPipeline(args) {
51689
52088
  foldExecutedThisPass = foldExecutesThisPass(foldDueDecision.value || softRefreshOpportunity, preFoldInjectionResult.m0Materialized === true);
51690
52089
  if (foldExecutedThisPass) {
51691
52090
  const afterFold = getOrCreateSessionMeta(args.db, args.sessionId);
51692
- foldBustsServedPrefixThisPass = !protectedSignedPrefix && foldBustsServedPrefix(foldDueDecision.reason, {
52091
+ foldBustsServedPrefixThisPass = !protectedSignedPrefix && foldBustsServedPrefix(foldDueDecision.reason === "system_hash" && !effectiveSystemChangedThisPass ? undefined : foldDueDecision.reason, {
51693
52092
  m0Bytes: persistedM0BeforeFold.cachedM0Bytes ?? null,
51694
52093
  m1Bytes: persistedM0BeforeFold.cachedM1Bytes ?? null
51695
52094
  }, {
@@ -51735,15 +52134,16 @@ async function runPipeline(args) {
51735
52134
  const routinePressureAlreadyApplied = !args.sessionMeta.isSubagent && executePressureEligible && routinePressureAppliedBySession.get(args.sessionId) === true;
51736
52135
  let hasPendingMaterializeSignal = !protectedSignedPrefix && hasPendingMaterialization(args.sessionId);
51737
52136
  const heldIdleExpiryRebuild = protectedSignedPrefix && piHardSignals?.cacheExpired === true;
52137
+ const systemPrefixChangedThisPass = !prefixPreflightContended && effectiveSystemChangedThisPass;
51738
52138
  const rideSignals = {
51739
52139
  subagentExecute: args.sessionMeta.isSubagent && args.schedulerDecision === "execute",
51740
- hardFold: foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild,
52140
+ hardFold: foldBustsServedPrefixThisPass || systemPrefixChangedThisPass || firstRenderBust || heldIdleExpiryRebuild || isCacheBustingPass,
51741
52141
  force: (args.forceMaterialization === true || emergencyDropEligible) && (args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0),
51742
- explicitFlush: hasPendingMaterializeSignal || deferredMaterializeEligible && !prefixPreflightContended,
51743
- publishedHistory: !prefixPreflightContended && (args.isCacheBusting || publishedM1RefreshedThisPass || canConsumeDeferredLate && deferredHistoryWasPendingAtPassStart)
52142
+ explicitFlush: !protectedSignedPrefix && (args.userFlushOrigin === true || hasPendingMaterializationOrigin(pendingMaterializationSessions, args.sessionId, "user_flush")),
52143
+ publishedHistory: !prefixPreflightContended && publishedM1RefreshedThisPass
51744
52144
  };
51745
52145
  let prefixMutationAllowed = !protectedSignedPrefix || firstRenderBust || heldIdleExpiryRebuild || emergencyDropEligible;
51746
- let isCacheBustingPass = prefixMutationAllowed && hasReclaimRide(rideSignals);
52146
+ isCacheBustingPass = prefixMutationAllowed && hasReclaimRide(rideSignals);
51747
52147
  let publishedWorkDrainAllowed = isCacheBustingPass;
51748
52148
  let unavoidableCarriedIdentityRebuild = false;
51749
52149
  if (protectedSignedPrefix && readPiIdentityRebuilds(args.db, args.sessionId).length > 0) {
@@ -51763,7 +52163,7 @@ async function runPipeline(args) {
51763
52163
  onRejectedProjectOverride: (warning) => sessionLog(args.sessionId, warning)
51764
52164
  });
51765
52165
  let protectionFloorResolution = resolveProtectionFloor();
51766
- let shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
52166
+ let shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || systemPrefixChangedThisPass || rideSignals.explicitFlush || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || args.recoveryRebuildReason !== undefined && isCacheBustingPass || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
51767
52167
  const tTagSnapshotSync = performance.now();
51768
52168
  readPiFallbackTagRows(args.db, args.sessionId);
51769
52169
  logTransformTiming(args.sessionId, "tagSnapshotSync", tTagSnapshotSync);
@@ -51857,7 +52257,7 @@ async function runPipeline(args) {
51857
52257
  ...message,
51858
52258
  parts: message.parts
51859
52259
  }));
51860
- const recoveryNormalized = !!args.recoveryPrefix && (foldBustsServedPrefixThisPass || firstRenderBust || identityRebuild);
52260
+ const recoveryNormalized = !!args.recoveryPrefix && (isCacheBustingPass || identityRebuild);
51861
52261
  const taggingView = args.recoveryPrefix && !recoveryNormalized ? mutationView.filter((message) => !args.recoveryPrefix?.untaggedInputIds.has(message.info.id ?? "")) : mutationView;
51862
52262
  let tagTextTokenCache = piTagTextTokenCacheBySession.get(args.sessionId);
51863
52263
  if (!tagTextTokenCache) {
@@ -51974,6 +52374,7 @@ async function runPipeline(args) {
51974
52374
  const deferredHistoryRefreshWasPending = deferredHistoryWasPendingAtPassStart;
51975
52375
  const shouldReadPendingOps = !args.compactionOff && (publishedWorkDrainAllowed || args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal || foldExecutedThisPass || firstRenderBust);
51976
52376
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
52377
+ const reclaimDistance = isCacheBustingPass ? toolReclaimDistanceEntries(mutationView, targets) : [];
51977
52378
  const protectionWindowForPass = getProtectionWindowForSession(args.db, args.sessionId, protectionFloorResolution.floor);
51978
52379
  const protectedTagNumbersForPass = usesTokenProtection ? protectionWindowForPass.protectedTagNumbers : newestActiveTagNumbersByCount(getActiveTagsBySession(args.db, args.sessionId), args.protectedTags);
51979
52380
  const protectedToolTags = protectedToolTagNumbers(getActiveTagsBySession(args.db, args.sessionId), args.protectedTools);
@@ -52003,7 +52404,9 @@ async function runPipeline(args) {
52003
52404
  ...args.contextUsage.percentage >= 95 ? [] : protectedTagNumbersForPass,
52004
52405
  ...protectedToolTags,
52005
52406
  ...identityAdoption.protectedTagNumbers
52006
- ]), pendingOperationTags, pendingOps, [], new Set, (reduction) => droppedTokenReductions.push(reduction));
52407
+ ]), pendingOperationTags, pendingOps, [], new Set, (reduction) => droppedTokenReductions.push(reduction), undefined, (eligible) => {
52408
+ eligiblePendingDropTagIds = eligible;
52409
+ });
52007
52410
  if (pendingOpsDidMutate) {
52008
52411
  droppedCount += pendingOps.length;
52009
52412
  }
@@ -52097,11 +52500,11 @@ async function runPipeline(args) {
52097
52500
  if (shouldRunHeuristics && args.heuristics) {
52098
52501
  try {
52099
52502
  const tHeuristic = performance.now();
52100
- const independentMutationBeforeHeuristics = pendingOpsDidMutate || foldBustsServedPrefixThisPass || identityRebuild || firstRenderBust || heldIdleExpiryRebuild;
52503
+ const independentMutationBeforeHeuristics = pendingOpsDidMutate || systemPrefixChangedThisPass || foldBustsServedPrefixThisPass || identityRebuild || firstRenderBust || heldIdleExpiryRebuild;
52101
52504
  if (args.forceMaterialization === true && independentMutationBeforeHeuristics && getEmergencyInputSample(args.db, args.sessionId) > 0) {
52102
52505
  clearEmergencyDropSample(args.db, args.sessionId);
52103
52506
  }
52104
- routineCleanupApplied = (emergencyDropEligible ? args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0 : args.sessionMeta.isSubagent || !routinePressureAlreadyApplied) || hasPendingMaterializeSignal || deferredMaterialize || independentMutationBeforeHeuristics;
52507
+ routineCleanupApplied = (emergencyDropEligible ? args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0 : args.sessionMeta.isSubagent || !routinePressureAlreadyApplied) || hasPendingMaterializeSignal || rideSignals.explicitFlush || deferredMaterialize || independentMutationBeforeHeuristics;
52105
52508
  heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
52106
52509
  protectedTools: args.protectedTools,
52107
52510
  prepareToolRemovalMeasurements: transcript.prepareToolRemovalMeasurements,
@@ -52286,6 +52689,7 @@ async function runPipeline(args) {
52286
52689
  }
52287
52690
  }
52288
52691
  const editReclaim = buildEditSupersessionReclaim({
52692
+ watermark: reclaimMeta.toolReclaimWatermark ?? 0,
52289
52693
  db: args.db,
52290
52694
  sessionId: args.sessionId,
52291
52695
  targets: newTargets,
@@ -52413,7 +52817,7 @@ async function runPipeline(args) {
52413
52817
  setEmergencyDropSample(args.db, args.sessionId, args.contextUsage.inputTokens);
52414
52818
  }
52415
52819
  if (toolReclaimApplicationOpportunity) {
52416
- advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
52820
+ advanceToolReclaimWatermarkByDistance(args.db, args.sessionId, reclaimDistance, args.reclaimUsableWindow ?? 0);
52417
52821
  }
52418
52822
  if (autoReclaimTargetCount > 0) {
52419
52823
  sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
@@ -52489,9 +52893,9 @@ async function runPipeline(args) {
52489
52893
  db: args.db,
52490
52894
  sessionId: args.sessionId,
52491
52895
  messages: args.messages,
52492
- isCacheBusting: args.isCacheBusting && prefixMutationAllowed,
52896
+ isCacheBusting: args.isCacheBusting && isCacheBustingPass,
52493
52897
  stableIdByRef: postCommitStableIdByRef,
52494
- forceDiscovery: args.stableIdSchemeCutover === true && prefixMutationAllowed,
52898
+ forceDiscovery: args.stableIdSchemeCutover === true && isCacheBustingPass,
52495
52899
  canFirstApply: isCacheBustingPass,
52496
52900
  onLegacyReplay: (previous, next) => {
52497
52901
  const id = postCommitStableIdByRef.get(previous);
@@ -52507,8 +52911,14 @@ async function runPipeline(args) {
52507
52911
  onFirstApplication: (message, index) => recordFirstApplicationAt(message, index, 0, (item) => item && typeof item === "object" ? postCommitStableIdByRef.get(item) : undefined)
52508
52912
  });
52509
52913
  logTransformTiming(args.sessionId, "stripDroppedPlaceholders", tDroppedPlaceholders);
52510
- const deferredHistoryDrainEligible = historyWasConsumedThisPass && materializationSatisfiedThisPass && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal) && !suppressDeferredHistoryDrain && !casLost;
52511
- let preserveDeferredMaterializationForMarkerDrain = false;
52914
+ const deferredHistoryDrainEligible = (canConsumeDeferredLate && !firstRenderBust || historyWasConsumedThisPass && materializationSatisfiedThisPass && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal)) && prefixMutationAllowed && injectionResult !== null && injectionResult.contentionExhausted !== true && !prefixPreflightContended && !suppressDeferredHistoryDrain && !casLost;
52915
+ const consumePublicationRender = () => {
52916
+ if (!deferredHistoryWasPendingAtPassStart || !historyWasConsumedThisPass)
52917
+ return;
52918
+ consumeDeferredHistoryRefresh(args.sessionId);
52919
+ if (!publishedWorkDrainAllowed || pendingOpsAppliedThisPass && (!shouldRunHeuristics || heuristicsExecuted))
52920
+ deferredMaterializationConsumedThisPass = consumeDeferredMaterialization(args.sessionId) || deferredMaterializationConsumedThisPass;
52921
+ };
52512
52922
  let pendingForDrain = null;
52513
52923
  if (!deferredHistoryDrainEligible && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal)) {
52514
52924
  try {
@@ -52530,14 +52940,12 @@ async function runPipeline(args) {
52530
52940
  if (!pending) {
52531
52941
  if (injectionResult?.contentionExhausted === true) {
52532
52942
  suppressDeferredHistoryDrain = true;
52533
- preserveDeferredMaterializationForMarkerDrain = true;
52534
52943
  sessionLog(args.sessionId, "Pi deferred-history drain skipped: m[0]/m[1] used a contention fallback; preserving deferred signals");
52535
52944
  } else {
52536
- consumeDeferredHistoryRefresh(args.sessionId);
52945
+ consumePublicationRender();
52537
52946
  }
52538
52947
  } else if (!pendingPiMarkerCoveredByRenderedBoundary(pending, injectionResult)) {
52539
52948
  suppressDeferredHistoryDrain = true;
52540
- preserveDeferredMaterializationForMarkerDrain = true;
52541
52949
  const boundary = injectionResult?.renderedBoundary;
52542
52950
  const m1Coverage = injectionResult?.m1RenderedCoverage;
52543
52951
  logPiCompactionMarkerDrainGateOnce(args.sessionId, pending, "rendered-history-not-covering-marker", {
@@ -52547,8 +52955,8 @@ async function runPipeline(args) {
52547
52955
  m1CoverageEndMessageId: m1Coverage?.endMessageId
52548
52956
  });
52549
52957
  } else if (!args.appendCompaction || !args.readBranchEntries || !foldingSystemState) {
52958
+ consumePublicationRender();
52550
52959
  suppressDeferredHistoryDrain = true;
52551
- preserveDeferredMaterializationForMarkerDrain = true;
52552
52960
  if (!args.appendCompaction)
52553
52961
  logPiCompactionMarkerDrainGateOnce(args.sessionId, pending, "append-compaction-unavailable");
52554
52962
  if (!args.readBranchEntries)
@@ -52556,6 +52964,7 @@ async function runPipeline(args) {
52556
52964
  if (!foldingSystemState)
52557
52965
  logPiCompactionMarkerDrainGateOnce(args.sessionId, pending, "folding-system-state-unavailable");
52558
52966
  } else {
52967
+ consumePublicationRender();
52559
52968
  args.assertCurrentPass?.();
52560
52969
  const outcome = applyDeferredPiCompactionMarker({
52561
52970
  db: args.db,
@@ -52577,7 +52986,6 @@ async function runPipeline(args) {
52577
52986
  }
52578
52987
  } else {
52579
52988
  suppressDeferredHistoryDrain = true;
52580
- preserveDeferredMaterializationForMarkerDrain = true;
52581
52989
  if (adoption.kind === "divergent") {
52582
52990
  logPiCompactionMarkerDrainGateOnce(args.sessionId, pending, "equivalence-refused", {
52583
52991
  firstKeptEntryId: outcome.firstKeptEntryId,
@@ -52595,7 +53003,6 @@ async function runPipeline(args) {
52595
53003
  }
52596
53004
  if (outcome.kind === "waiting-for-entry") {
52597
53005
  suppressDeferredHistoryDrain = true;
52598
- preserveDeferredMaterializationForMarkerDrain = true;
52599
53006
  } else if (outcome.kind === "retryable-failure") {} else if (markerEquivalent && clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending, outcome.kind === "stale-skip" ? outcome.reason : outcome.kind)) {
52600
53007
  consumeDeferredHistoryRefresh(args.sessionId);
52601
53008
  } else if (markerEquivalent) {
@@ -52609,9 +53016,8 @@ async function runPipeline(args) {
52609
53016
  logPiCompactionMarkerDrainGateOnce(args.sessionId, pendingForDrain, "drain-threw", getPiCompactionMarkerDrainErrorFields(err));
52610
53017
  }
52611
53018
  }
52612
- if (preserveDeferredMaterializationForMarkerDrain && deferredMaterializationConsumedThisPass) {
53019
+ if ((prefixPreflightContended || injectionResult?.contentionExhausted === true) && deferredMaterializationConsumedThisPass)
52613
53020
  signalPiDeferredMaterialization(args.sessionId);
52614
- }
52615
53021
  logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now());
52616
53022
  const outputMessages = transcript.getOutputMessages();
52617
53023
  try {
@@ -52717,7 +53123,7 @@ async function runPipeline(args) {
52717
53123
  prefixMutationAllowed,
52718
53124
  strictThinkingKept: protectedSignedPrefix,
52719
53125
  signedPrefixRewritten: rewritingSignedPrefix,
52720
- prefixRebuildAlreadyPriced: firstRenderBust || heldIdleExpiryRebuild,
53126
+ prefixRebuildAlreadyPriced: firstRenderBust || heldIdleExpiryRebuild || !!args.recoveryRebuildReason && isCacheBustingPass,
52721
53127
  identityRebuilt: identityRebuild,
52722
53128
  servedTagNumbers,
52723
53129
  heuristicsExecuted,
@@ -52733,6 +53139,7 @@ async function runPipeline(args) {
52733
53139
  emergency,
52734
53140
  bustedThisPass,
52735
53141
  agentDropsAppliedThisPass: pendingOpsDidMutate,
53142
+ eligiblePendingDropTagIds,
52736
53143
  prefixEditBesidesReasoningTrim: !!args.recoveryRebuildReason || prefixEditBesidesReasoningTrim || firstRenderBust || args.isCacheBusting || hasPendingMaterializeSignal || deferredMaterializationConsumedThisPass || foldBustsServedPrefixThisPass || publishedM1RefreshedThisPass || materialized || historyWasConsumedThisPass || pendingOpsDidMutate || autoReclaimDidMutateThisPass || emergency,
52737
53144
  targetCount: targets.size,
52738
53145
  reasoningWatermark: args.sessionMeta.clearedReasoningThroughTag ?? 0,
@@ -52916,6 +53323,7 @@ function clearPiCompactionOffInMemoryState(sessionId) {
52916
53323
  clearPiChannel1State(sessionId);
52917
53324
  }
52918
53325
  function clearContextHandlerSession(sessionId) {
53326
+ lastServedSystemHashBySession.delete(sessionId);
52919
53327
  clearPiHistoryBoundaryRepairSession(sessionId);
52920
53328
  clearPiUpperReclaimSession(sessionId);
52921
53329
  invalidateTrueRawTokenCache({ sessionId, reason: "pi.branch.changed" });
@@ -53228,6 +53636,7 @@ function runManualMaintenanceCommand(args) {
53228
53636
  }
53229
53637
  const explanations = {
53230
53638
  "maintenance-disabled": "memory maintenance is disabled (maintenance.enabled: false)",
53639
+ "maintenance-model-unconfigured": "memory maintenance has no primary model configured — set maintenance.model in the user-level mmc config (fallback_models alone does not enable maintenance; the chain never inherits the historian or session model)",
53231
53640
  "run-in-flight-local": "a maintenance run is already in flight; next_due_at is due now, so the launcher retries on the next pass",
53232
53641
  "empty-pool": "memory pool is empty (no active/permanent rows) — nothing to maintain"
53233
53642
  };
@@ -54097,7 +54506,7 @@ function formatThresholdPercent(value) {
54097
54506
  }
54098
54507
 
54099
54508
  // ../plugin/src/shared/status-view-check.ts
54100
- function isRecord5(value) {
54509
+ function isRecord6(value) {
54101
54510
  return typeof value === "object" && value !== null && !Array.isArray(value);
54102
54511
  }
54103
54512
  function isNumber(value) {
@@ -54173,25 +54582,25 @@ class OptionalFields {
54173
54582
  }
54174
54583
  }
54175
54584
  function isWindowGeometry(value) {
54176
- if (!isRecord5(value) || !isNumber(value.usableSoft) || !isRecord5(value.derivation))
54585
+ if (!isRecord6(value) || !isNumber(value.usableSoft) || !isRecord6(value.derivation))
54177
54586
  return false;
54178
54587
  const derivation = value.derivation;
54179
54588
  return isNumber(derivation.window) && isNumber(derivation.reserve) && isString(derivation.reserveSource);
54180
54589
  }
54181
54590
  function isTailHygiene(value) {
54182
- return isRecord5(value) && isNumber(value.u) && isNumber(value.t) && isNumber(value.severity) && isBoolean(value.evaluable);
54591
+ return isRecord6(value) && isNumber(value.u) && isNumber(value.t) && isNumber(value.severity) && isBoolean(value.evaluable);
54183
54592
  }
54184
54593
  function isCompactionMarker(value) {
54185
- return isRecord5(value) && (value.code === null || value.code === "MC-C11") && isNumber(value.attempts) && (value.lastError === null || isString(value.lastError));
54594
+ return isRecord6(value) && (value.code === null || value.code === "MC-C11") && isNumber(value.attempts) && (value.lastError === null || isString(value.lastError));
54186
54595
  }
54187
54596
  function isConfigParseFailure(value) {
54188
- return isRecord5(value);
54597
+ return isRecord6(value);
54189
54598
  }
54190
54599
  function isConfigReloadFailure(value) {
54191
- return isRecord5(value) && isString(value.path) && isString(value.message);
54600
+ return isRecord6(value) && isString(value.path) && isString(value.message);
54192
54601
  }
54193
54602
  function isUnconfirmedMigrationHolders(value) {
54194
- return isRecord5(value) && Array.isArray(value.pids) && value.pids.every(isNumber) && isNumber(value.fromVersion) && isNumber(value.toVersion);
54603
+ return isRecord6(value) && Array.isArray(value.pids) && value.pids.every(isNumber) && isNumber(value.fromVersion) && isNumber(value.toVersion);
54195
54604
  }
54196
54605
  function isNullableNumber2(value) {
54197
54606
  return value === null || isNumber(value);
@@ -54203,7 +54612,7 @@ function isCacheTtlSource(value) {
54203
54612
  return isString(value);
54204
54613
  }
54205
54614
  function checkStatusViewSource(candidate) {
54206
- if (!isRecord5(candidate))
54615
+ if (!isRecord6(candidate))
54207
54616
  return { ok: false, fields: ["(the payload is not an object)"] };
54208
54617
  const missing = [];
54209
54618
  const numbers = {};
@@ -54823,14 +55232,14 @@ class StatusDialogComponent {
54823
55232
  this.detailRefresh = new PiStatusDetailRefresh(props.pi, props.ctx, props.deps, props.sessionId);
54824
55233
  this.detail = this.detailRefresh.refresh();
54825
55234
  openStatusDialogs.add(this);
54826
- this.refreshTimer = setInterval(() => {
55235
+ this.refreshTimer = inRootAsyncContext(() => setInterval(() => {
54827
55236
  if (this.closed)
54828
55237
  return;
54829
55238
  try {
54830
55239
  this.detail = this.detailRefresh.refresh();
54831
55240
  this.props.tui.requestRender();
54832
55241
  } catch {}
54833
- }, REFRESH_INTERVAL_MS);
55242
+ }, REFRESH_INTERVAL_MS));
54834
55243
  }
54835
55244
  handleInput(data) {
54836
55245
  if (this.closed)
@@ -55687,7 +56096,7 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
55687
56096
  }
55688
56097
  return true;
55689
56098
  };
55690
- const renewal = setInterval(() => {
56099
+ const renewal = inRootAsyncContext(() => setInterval(() => {
55691
56100
  try {
55692
56101
  renewWrapupMarker({
55693
56102
  lastCompartmentEnd: getLastCompartmentEndMessage(deps.db, sessionId)
@@ -55695,7 +56104,7 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
55695
56104
  } catch (err) {
55696
56105
  console.warn(`[magic-context][pi] /mmc-wrapup marker renewal failed for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
55697
56106
  }
55698
- }, 60000);
56107
+ }, 60000));
55699
56108
  const stopRenewal = () => clearInterval(renewal);
55700
56109
  deps.shutdownSignal?.addEventListener("abort", stopRenewal, { once: true });
55701
56110
  try {
@@ -55770,13 +56179,13 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
55770
56179
  break;
55771
56180
  }
55772
56181
  const leaseHolder = leaseResult.holderId;
55773
- const leaseRenewal = setInterval(() => {
56182
+ const leaseRenewal = inRootAsyncContext(() => setInterval(() => {
55774
56183
  try {
55775
56184
  renewCompartmentLease(deps.db, sessionId, leaseHolder);
55776
56185
  } catch (err) {
55777
56186
  console.warn(`[magic-context][pi] /mmc-wrapup compartment lease renewal failed for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
55778
56187
  }
55779
- }, COMPARTMENT_LEASE_RENEWAL_MS);
56188
+ }, COMPARTMENT_LEASE_RENEWAL_MS));
55780
56189
  const stopLeaseRenewal = () => clearInterval(leaseRenewal);
55781
56190
  deps.shutdownSignal?.addEventListener("abort", stopLeaseRenewal, {
55782
56191
  once: true
@@ -58299,8 +58708,8 @@ function resolveCtxExpandMode(args, domain) {
58299
58708
  }
58300
58709
 
58301
58710
  // ../plugin/src/tools/ctx-expand/render.ts
58302
- import { createHash as createHash24 } from "node:crypto";
58303
- function isRecord6(value) {
58711
+ import { createHash as createHash25 } from "node:crypto";
58712
+ function isRecord7(value) {
58304
58713
  return value !== null && typeof value === "object" && !Array.isArray(value);
58305
58714
  }
58306
58715
  function roleLabel(role) {
@@ -58312,7 +58721,7 @@ function roleLabel(role) {
58312
58721
  }
58313
58722
  function verboseRoleLabel(msg) {
58314
58723
  if (msg.role === "user" && msg.parts.length > 0 && msg.parts.every((part) => {
58315
- if (!isRecord6(part))
58724
+ if (!isRecord7(part))
58316
58725
  return false;
58317
58726
  if (part.type === "tool_result")
58318
58727
  return true;
@@ -58340,15 +58749,15 @@ function keyArg(input) {
58340
58749
  function asToolPart(part) {
58341
58750
  const type = typeof part.type === "string" ? part.type : "";
58342
58751
  if (type === "tool") {
58343
- const state = isRecord6(part.state) ? part.state : null;
58752
+ const state = isRecord7(part.state) ? part.state : null;
58344
58753
  const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
58345
- const metadata = state && isRecord6(state.metadata) ? state.metadata : null;
58754
+ const metadata = state && isRecord7(state.metadata) ? state.metadata : null;
58346
58755
  const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
58347
58756
  return {
58348
58757
  name: typeof part.tool === "string" ? part.tool : "tool",
58349
58758
  callId: typeof part.callID === "string" ? part.callID : "",
58350
58759
  title,
58351
- input: state && isRecord6(state.input) ? state.input : null,
58760
+ input: state && isRecord7(state.input) ? state.input : null,
58352
58761
  output
58353
58762
  };
58354
58763
  }
@@ -58357,7 +58766,7 @@ function asToolPart(part) {
58357
58766
  name: typeof part.name === "string" ? part.name : "tool",
58358
58767
  callId: typeof part.id === "string" ? part.id : "",
58359
58768
  title: null,
58360
- input: isRecord6(part.input) ? part.input : null,
58769
+ input: isRecord7(part.input) ? part.input : null,
58361
58770
  output: null
58362
58771
  };
58363
58772
  }
@@ -58386,7 +58795,7 @@ function reasoningOf(part) {
58386
58795
  return null;
58387
58796
  }
58388
58797
  function renderPartPreview(part, expandTools) {
58389
- if (!isRecord6(part))
58798
+ if (!isRecord7(part))
58390
58799
  return null;
58391
58800
  const expansion = expandToolPart(part, expandTools, true);
58392
58801
  if (expansion !== null)
@@ -58413,7 +58822,7 @@ function renderPartPreview(part, expandTools) {
58413
58822
  return ` • [${type}]`;
58414
58823
  }
58415
58824
  function renderPartFull(part) {
58416
- if (!isRecord6(part))
58825
+ if (!isRecord7(part))
58417
58826
  return null;
58418
58827
  const text = textOf(part);
58419
58828
  if (text !== null) {
@@ -58500,11 +58909,11 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
58500
58909
  const message = readRawSessionMessageById2(sessionId, owner);
58501
58910
  if (!message)
58502
58911
  return `Tag ${number}'s original tool owner is no longer in stored history.`;
58503
- const parts = message?.parts.filter((part) => isRecord6(part) && asToolPart(part)?.callId === tag.messageId) ?? [];
58504
- if (!parts.some((part) => isRecord6(part) && asToolPart(part)?.output !== null)) {
58912
+ const parts = message?.parts.filter((part) => isRecord7(part) && asToolPart(part)?.callId === tag.messageId) ?? [];
58913
+ if (!parts.some((part) => isRecord7(part) && asToolPart(part)?.output !== null)) {
58505
58914
  visitRawSessionMessages2(sessionId, message.ordinal + 1, Number.MAX_SAFE_INTEGER, (candidate) => {
58506
- const matching = candidate.parts.filter((part) => isRecord6(part) && asToolPart(part)?.callId === tag.messageId);
58507
- if (matching.some((part) => isRecord6(part) && part.type === "tool_use"))
58915
+ const matching = candidate.parts.filter((part) => isRecord7(part) && asToolPart(part)?.callId === tag.messageId);
58916
+ if (matching.some((part) => isRecord7(part) && part.type === "tool_use"))
58508
58917
  return false;
58509
58918
  parts.push(...matching);
58510
58919
  return matching.length === 0;
@@ -58521,9 +58930,9 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
58521
58930
  if (!message)
58522
58931
  return `Tag ${number}'s original text is no longer in stored history.`;
58523
58932
  const index = scoped ? Number(scoped[2]) : 0;
58524
- const piText = message.parts.filter((part) => isRecord6(part) && part.type === "text");
58933
+ const piText = message.parts.filter((part) => isRecord7(part) && part.type === "text");
58525
58934
  const part = message.parts[index];
58526
- const matching = derived ? piText.filter((part) => isRecord6(part) && typeof part.text === "string" && createHash24("sha256").update(part.text).digest("hex") === derived[3]) : [];
58935
+ const matching = derived ? piText.filter((part) => isRecord7(part) && typeof part.text === "string" && createHash25("sha256").update(part.text).digest("hex") === derived[3]) : [];
58527
58936
  const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" ? piText[index] : part;
58528
58937
  if (!selected)
58529
58938
  return `Tag ${number}'s original text part is no longer in stored history.`;
@@ -60932,7 +61341,11 @@ var __test = {
60932
61341
  },
60933
61342
  clearPiInProcessSubagentInitContext,
60934
61343
  claimPiStartupMaintenance,
60935
- clearPiStartupMaintenanceClaim
61344
+ clearPiStartupMaintenanceClaim,
61345
+ buildPiMaintenanceOptions,
61346
+ resetLoggedPiMaintenanceModelMissing() {
61347
+ loggedPiMaintenanceModelMissing = false;
61348
+ }
60936
61349
  };
60937
61350
  function registerConfiguredTodoLifecycle(pi, options) {
60938
61351
  if (!options.configured)
@@ -61167,22 +61580,32 @@ function resolveAutoSearchFromConfig(config) {
61167
61580
  minPromptChars: auto?.min_prompt_chars ?? 20
61168
61581
  };
61169
61582
  }
61583
+ var loggedPiMaintenanceModelMissing = false;
61170
61584
  function buildPiMaintenanceOptions(config, historian) {
61171
- const maintenance = config.maintenance;
61585
+ const maintenance = inheritMaintenanceModelConfig(config);
61172
61586
  if (!maintenance?.enabled)
61173
61587
  return;
61174
- if (!historian)
61588
+ const chain = resolveMaintenanceModelChain(maintenance, "pi");
61589
+ if (!chain) {
61590
+ if (!loggedPiMaintenanceModelMissing) {
61591
+ loggedPiMaintenanceModelMissing = true;
61592
+ log(`${PREFIX4} memory maintenance off: neither maintenance.model nor historian.model is configured (set either in the user-level mmc config; the session model is never inherited)`);
61593
+ }
61594
+ return;
61595
+ }
61596
+ const primary = toModelEntry(chain.primary);
61597
+ if (!primary)
61175
61598
  return;
61176
61599
  return {
61177
61600
  enabled: true,
61178
- runner: historian.runner,
61179
- model: historian.model,
61180
- fallbackModels: historian.fallbackModels,
61181
- thinkingLevel: historian.thinkingLevel,
61182
- timeoutMs: historian.timeoutMs,
61183
- temperature: historian.temperature,
61184
- maxOutputTokens: historian.maxOutputTokens,
61185
- language: historian.language,
61601
+ runner: historian?.runner ?? new PiSubagentRunner,
61602
+ model: primary.model,
61603
+ fallbackModels: chain.fallbacks,
61604
+ thinkingLevel: primary.qualifier,
61605
+ timeoutMs: config.historian_timeout_ms,
61606
+ temperature: config.historian?.temperature,
61607
+ maxOutputTokens: config.historian?.maxTokens,
61608
+ language: config.language,
61186
61609
  runConfig: {
61187
61610
  budgets: maintenance.budgets,
61188
61611
  cooldowns: maintenance.cooldowns,
@@ -61626,7 +62049,11 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
61626
62049
  return {
61627
62050
  projectPath: project.projectIdentity,
61628
62051
  directory: project.projectDir,
61629
- options: buildPiMaintenanceOptions(sampled, historian)
62052
+ options: buildPiMaintenanceOptions(sampled, historian),
62053
+ maintenanceConfig: {
62054
+ enabled: sampled.maintenance?.enabled ?? false,
62055
+ model: sampled.maintenance?.model
62056
+ }
61630
62057
  };
61631
62058
  }
61632
62059
  });