@retaw106/mmc-pi 2.1.2 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  normalizeProtectedToolName,
23
23
  mergeProtectedTools,
24
24
  detectThinkingBindingMismatch,
25
+ detectLatestTurnThinkingMismatch,
25
26
  isPrefixBoundThinkingModel,
26
27
  detectOverflow,
27
28
  stableStringify,
@@ -32,7 +33,7 @@ import {
32
33
  localBudget,
33
34
  calibrationForModelKey,
34
35
  historyLocalBudget,
35
- CTX_REDUCE_KEEP,
36
+ adoptedProtectedToolsPolicy,
36
37
  protectedToolTagNumbers,
37
38
  protectedToolTokenCount,
38
39
  removeSystemReminders,
@@ -51,6 +52,7 @@ import {
51
52
  prependTag,
52
53
  isRecord,
53
54
  TEXT_TAG_IDENTITY_MARKER,
55
+ contentTagOwnerMessageId,
54
56
  estimateImageTokensFromDataUrl,
55
57
  TODO_STATUSES,
56
58
  TODO_PRIORITIES,
@@ -135,6 +137,7 @@ import {
135
137
  buildTrueRawTokenIndex,
136
138
  computeRawRangeFingerprint,
137
139
  invalidateTrueRawTokenCache,
140
+ stripSystemInjection,
138
141
  hasUserAnswerMetadata,
139
142
  droppedInputMarker,
140
143
  createDroppedInputGuard,
@@ -165,7 +168,7 @@ import {
165
168
  getRawSessionTagKeysThrough2,
166
169
  getLegacyProtectedTailStartOrdinal2,
167
170
  readSessionChunk2
168
- } from "./index-bz61mcxg.js";
171
+ } from "./index-j0jjtcwm.js";
169
172
  import {
170
173
  registerSlowWriteReporter,
171
174
  registerSqliteDiagnosticSink,
@@ -6964,7 +6967,6 @@ var HistorianConfigSchema = AgentMetadataSchema.extend({
6964
6967
  var MagicContextConfigSchema = object({
6965
6968
  enabled: boolean2().default(true).describe("Enable magic context (default: true)"),
6966
6969
  allow_home_project: boolean2().default(false).describe("Allow Magic Context sessions launched from the exact canonical home directory. A non-git home uses its deterministic dir: identity; a home repository uses its git: identity. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace."),
6967
- auto_update: boolean2().optional().describe("DEPRECATED no-op: the automatic update check was removed (the package is private-registry-only, so public npm checks always 404). The key is still accepted so existing configs keep parsing; upgrade via the host's own plugin update channel."),
6968
6970
  language: string2().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
6969
6971
  profile: string2().trim().min(1).optional().describe("Select a named user-owned model profile. A valid project name overrides this user default; an empty string, null, or other non-string project value is ignored with a warning so the user selection still applies. Unknown names warn and use the base configuration."),
6970
6972
  profiles: ConfigProfilesSchema.optional().describe("User-level named model profiles. A profile may contain only the flat historian model-resolution fields (model, fallback_models, OpenCode variant, Pi thinking_level). Project configs may select a name but cannot define profiles."),
@@ -7436,10 +7438,6 @@ function stripUnsafeProjectConfigFields(projectRaw) {
7436
7438
  delete projectRaw.profiles;
7437
7439
  warnings.push("Ignoring profiles from project config (security: profile definitions are user-level only; a repository may select a named user profile with profile).");
7438
7440
  }
7439
- if ("auto_update" in projectRaw) {
7440
- delete projectRaw.auto_update;
7441
- warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
7442
- }
7443
7441
  if ("fail_closed_blocking" in projectRaw) {
7444
7442
  delete projectRaw.fail_closed_blocking;
7445
7443
  warnings.push("Ignoring fail_closed_blocking from project config (security: only user-level config may disable or force the loud inoperability gate).");
@@ -11973,8 +11971,8 @@ function ensureBackfillStateTable(db) {
11973
11971
  }
11974
11972
  }
11975
11973
  function withImmediateTransaction(db, fn) {
11974
+ withoutSqliteTransformPass(() => withSqliteBackgroundWriter(() => db.exec("BEGIN IMMEDIATE")));
11976
11975
  const transactionStartedAt = performance.now();
11977
- db.exec("BEGIN IMMEDIATE");
11978
11976
  try {
11979
11977
  const result = fn();
11980
11978
  db.exec("COMMIT");
@@ -12081,6 +12079,9 @@ function createPageReader(source) {
12081
12079
  };
12082
12080
  }
12083
12081
  async function runSessionProjectBackfill(db, source, options = {}) {
12082
+ return withoutSqliteTransformPass(() => withSqliteBackgroundWriter(() => runSessionProjectBackfillPass(db, source, options)));
12083
+ }
12084
+ async function runSessionProjectBackfillPass(db, source, options = {}) {
12084
12085
  const startedAt = performance.now();
12085
12086
  const harness = getHarness();
12086
12087
  const now = options.now ?? Date.now;
@@ -12215,12 +12216,12 @@ import { execFile, execFileSync as execFileSync2 } from "node:child_process";
12215
12216
  import { readFileSync as readFileSync8 } from "node:fs";
12216
12217
 
12217
12218
  // ../plugin/src/shared/pi-executable.ts
12218
- var PI_IMAGE_NAMES = new Set(["pi", "pi.cmd"]);
12219
+ var PI_IMAGE_NAMES = new Set(["pi", "pi.cmd", "omp", "oh-my-pi"]);
12219
12220
  function piHarnessKindFromExecutable(value) {
12220
12221
  const executable = (value ?? "").trim().replace(/^['"]|['"]$/g, "").replaceAll("\\", "/").split("/").at(-1)?.toLowerCase().replace(/\.(?:exe|cmd)$/, "");
12221
12222
  if (!executable || !PI_IMAGE_NAMES.has(executable))
12222
12223
  return;
12223
- return "pi";
12224
+ return executable === "pi" ? "pi" : "omp";
12224
12225
  }
12225
12226
 
12226
12227
  // ../plugin/src/shared/rpc-utils.ts
@@ -14119,6 +14120,13 @@ function getCurrentVersion(db) {
14119
14120
  function isMigrationApplied(db, version) {
14120
14121
  return db.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(version) != null;
14121
14122
  }
14123
+ function hasPendingMigrations(db) {
14124
+ const bookkeeping = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'schema_migrations'").get();
14125
+ if (bookkeeping == null)
14126
+ return true;
14127
+ const currentVersion = getCurrentVersion(db);
14128
+ return MIGRATIONS.some((candidate) => candidate.version > currentVersion && !isMigrationApplied(db, candidate.version));
14129
+ }
14122
14130
  function isSiblingMigrationConflict(db, error, version) {
14123
14131
  if (!(error instanceof Error))
14124
14132
  return false;
@@ -14391,6 +14399,8 @@ function lkgContentDigestFromFields(fields) {
14391
14399
  }
14392
14400
  return hash.digest("base64url");
14393
14401
  }
14402
+ var digestMemo = new Map;
14403
+ var DIGEST_MEMO_MAX_BYTES = 16 * 1024 * 1024;
14394
14404
  function equalContentFields(left, right) {
14395
14405
  if (left === right)
14396
14406
  return true;
@@ -15066,7 +15076,8 @@ function clampWatermark(value, maxCopiedTag) {
15066
15076
  }
15067
15077
  function copySessionStateForClone(db, sourceSessionId, destinationSessionId, filter) {
15068
15078
  return runImmediate(db, () => {
15069
- if (countRows(db, "compartments", destinationSessionId) > 0 || countRows(db, "tags", destinationSessionId) > 0 || filter.copySessionNotesAndFacts === true && (tableExists2(db, "notes") && countRows(db, "notes", destinationSessionId) > 0 || tableExists2(db, "session_facts") && countRows(db, "session_facts", destinationSessionId) > 0)) {
15079
+ const hasTemporalDecisions = tableExists2(db, "temporal_decisions");
15080
+ if (countRows(db, "compartments", destinationSessionId) > 0 || countRows(db, "tags", destinationSessionId) > 0 || hasTemporalDecisions && countRows(db, "temporal_decisions", destinationSessionId) > 0 || filter.copySessionNotesAndFacts === true && (tableExists2(db, "notes") && countRows(db, "notes", destinationSessionId) > 0 || tableExists2(db, "session_facts") && countRows(db, "session_facts", destinationSessionId) > 0)) {
15070
15081
  return {
15071
15082
  kind: "destination-not-empty",
15072
15083
  compartmentsCopied: 0,
@@ -15155,6 +15166,14 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
15155
15166
  insertPendingOp.run(destinationSessionId, row.tag_id, row.operation, row.queued_at, row.harness);
15156
15167
  }
15157
15168
  }
15169
+ if (hasTemporalDecisions) {
15170
+ const temporalRows = db.prepare("SELECT message_id,marker FROM temporal_decisions WHERE session_id=?").all(sourceSessionId);
15171
+ const insertTemporal = db.prepare("INSERT INTO temporal_decisions(session_id,message_id,marker) VALUES (?,?,?)");
15172
+ for (const row of temporalRows) {
15173
+ if (filter.includeMessageId(row.message_id))
15174
+ insertTemporal.run(destinationSessionId, mapMessageId(filter, row.message_id), row.marker);
15175
+ }
15176
+ }
15158
15177
  const inherited = filter.copySessionNotesAndFacts === true ? copyPiSessionNotesAndFacts(db, sourceSessionId, destinationSessionId, filter) : { notesCopied: 0, factsCopied: 0 };
15159
15178
  const meta = db.prepare(`SELECT cleared_reasoning_through_tag, tool_reclaim_watermark,
15160
15179
  pi_stable_id_scheme, stripped_placeholder_ids,
@@ -15991,7 +16010,7 @@ function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion)
15991
16010
  startMessageFtsRowidMapBackfill(db).then(async () => {
15992
16011
  if (!readsOpenCodeStore)
15993
16012
  return;
15994
- const { readRawSessionMessagePage, readRawSessionMessages } = await import("./read-session-chunk-pz4pxry4.js");
16013
+ const { readRawSessionMessagePage, readRawSessionMessages } = await import("./read-session-chunk-ktp56z65.js");
15995
16014
  const { startMessageTimeBackfill } = await import("./message-time-backfill-a3p9r5rh.js");
15996
16015
  await startMessageTimeBackfill(db, Object.assign(readRawSessionMessages, {
15997
16016
  readPage: readRawSessionMessagePage
@@ -17511,12 +17530,23 @@ function openDatabase(dbPathOrOptions) {
17511
17530
  closeQuietly(db);
17512
17531
  return null;
17513
17532
  }
17533
+ if (options?.allowMigrations === false && hasPendingMigrations(db)) {
17534
+ log(`[magic-context] storage not ready: pending migrations at ${dbPath}; host callers must await the async opener`);
17535
+ closeQuietly(db);
17536
+ return null;
17537
+ }
17514
17538
  if (!enforceMigrationOnOpenGuard(db, dbPath, dbDir, latestSupportedVersion)) {
17515
17539
  closeQuietly(db);
17516
17540
  return null;
17517
17541
  }
17518
17542
  initializeDatabase(db, busyTimeoutMs);
17519
- runMigrations(db);
17543
+ if (options?.allowMigrations === false) {
17544
+ if (hasPendingMigrations(db)) {
17545
+ closeQuietly(db);
17546
+ return null;
17547
+ }
17548
+ } else
17549
+ runMigrations(db);
17520
17550
  ensureContextStoreUuid(db);
17521
17551
  return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
17522
17552
  } catch (error) {
@@ -19140,7 +19170,7 @@ var CTX_REDUCE_LIGHT_DESCRIPTION = `Stamp an item as no longer needed for the wo
19140
19170
  var CTX_EXPAND_LIGHT_DESCRIPTION = "Recover one item with tag=N (a §N§ or dropped placeholder number), not a message ordinal; text or one tool call with full input/output. message=N and start/end are whole-message ordinals from history/search, never tag numbers. Recover raw conversation behind a <session-history> heading or around a mmc_search hit: mmc_expand(start, end) returns [N] U:/A: lines (~15K-token cap; oversized ranges return the head and where to continue). verbose=true lists messages with per-part previews to pick one; message=N returns that message in full, including a tool output released with mmc_reduce. Ranges after the last compartment are your live tail, not expandable.";
19141
19171
  var CTX_NOTE_LIGHT_DESCRIPTION = 'Session notes are pending intentions: work you intend to return to, with findings attached ("take a note" always qualifies). Not active steps, an executing plan, or restart insurance; a record of how things stand (world-state, a design at a point in time) with nothing you intend to do about it — that goes stale silently; a fact worth keeping is memory, the rest is nothing. First line = title (<80 chars). write saves; read lists rows (note_ids for bodies); update changes one; dismiss retires 1–50. Dismiss a note when its work lands or is abandoned; a queue you never dismiss from stops being read.';
19142
19172
  var CTX_MEMORY_LIGHT_DESCRIPTION = "Durable facts about this project, shared with every agent on it and kept for the months this work lasts; active ones are already in <project-memory> as `#id: fact`. Write one standalone fact when it must not have to be found again — a rule, an architecture fact, a constraint, a config value, a naming convention — especially what cost you turns. A pending intention with evidence is mmc_note, not memory. write (content + category); update one id (content; category optional); archive one or more ids (reason optional); merge two or more ids (content); get 1–20 ids in any status.";
19143
- var CTX_SEARCH_LIGHT_DESCRIPTION = 'Search the archive — everything that ever happened here that is not on your desk: memories not in <project-memory>, compacted conversation, notes. Phrase query as a natural-language question carrying the exact terms you expect ("where is the opencode source code path?", "why did we choose SQLite over postgres?", "how does the historian lease work?") — a keyword stack finds less. Sources (omit for all): memory (rules, conventions), message (compacted conversation; hits carry ordinals for mmc_expand), note (parked follow-ups). Memory ids alone (`#7234`) resolve directly. from/to restrict every source to an inclusive UTC date range.';
19173
+ var CTX_SEARCH_LIGHT_DESCRIPTION = 'Search past context not on your desk: memories not in <project-memory>, compacted conversation, notes. Phrase query as a natural-language question carrying the exact terms you expect ("where is the opencode source code path?", "why did we choose SQLite over postgres?", "how does the historian lease work?") — a keyword stack finds less. Sources (omit for all): memory (rules, conventions), message (compacted conversation; hits carry ordinals for mmc_expand), primer (stored project Q&A), note (parked follow-ups). Memory ids alone (`#7234`) resolve directly. from/to restrict every source to an inclusive UTC date range.';
19144
19174
 
19145
19175
  // ../plugin/src/shared/prompt-surface-runtime.ts
19146
19176
  var GUIDANCE_MARKER = "## Magic Context";
@@ -21676,9 +21706,15 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
21676
21706
 
21677
21707
  // ../plugin/src/hooks/magic-context/cache-busting-signals.ts
21678
21708
  function hasReclaimRide(signals) {
21679
- return signals.hardFold || signals.force || signals.explicitFlush || signals.publishedHistory;
21680
- }
21681
- var RECLAIM_RIDE_NAMES = ["hardFold", "force", "explicitFlush", "publishedHistory"];
21709
+ return signals.hardFold || signals.force || signals.explicitFlush || signals.publishedHistory || signals.subagentExecute === true;
21710
+ }
21711
+ var RECLAIM_RIDE_NAMES = [
21712
+ "hardFold",
21713
+ "force",
21714
+ "explicitFlush",
21715
+ "publishedHistory",
21716
+ "subagentExecute"
21717
+ ];
21682
21718
  function reclaimRideLabel(signals) {
21683
21719
  const active = RECLAIM_RIDE_NAMES.filter((name) => signals[name]);
21684
21720
  return `ride=${active.length > 0 ? active.join("+") : "none"}`;
@@ -21748,6 +21784,9 @@ var MEMORY_MAINTAINER_B_AGENT = "magic-context-maintainer-b";
21748
21784
  var MEMORY_MAINTAINER_A_ALLOWED_TOOLS = [];
21749
21785
  var MEMORY_MAINTAINER_B_ALLOWED_TOOLS = ["read", "grep", "glob"];
21750
21786
  var MEMORY_MAINTAINER_A_STEP_CAP = 4;
21787
+ function resolveMaintenanceStepCap(baseSteps, consumedSteps) {
21788
+ return Math.max(1, baseSteps - consumedSteps);
21789
+ }
21751
21790
  var MEMORY_MAINTAINER_A_SYSTEM_PROMPT = [
21752
21791
  "You are the memory-maintenance stage A screener: a hidden background agent that",
21753
21792
  "value-screens a coding project's long-term memory pool before a separate",
@@ -22018,6 +22057,9 @@ ${stageAManifest}
22018
22057
  `);
22019
22058
  }
22020
22059
 
22060
+ // ../plugin/src/shared/subagent-runner.ts
22061
+ var HISTORIAN_STEP_CAP = 4;
22062
+
22021
22063
  // ../plugin/src/v2/hooks/hidden-child.ts
22022
22064
  var HIDDEN_HISTORIAN_AGENT = "historian";
22023
22065
  var AGENT_TOOL_ALLOWLIST = {
@@ -22026,7 +22068,8 @@ var AGENT_TOOL_ALLOWLIST = {
22026
22068
  [MEMORY_MAINTAINER_B_AGENT]: MEMORY_MAINTAINER_B_ALLOWED_TOOLS
22027
22069
  };
22028
22070
  var AGENT_STEP_CAPS = {
22029
- [MEMORY_MAINTAINER_A_AGENT]: MEMORY_MAINTAINER_A_STEP_CAP
22071
+ [MEMORY_MAINTAINER_A_AGENT]: MEMORY_MAINTAINER_A_STEP_CAP,
22072
+ [HIDDEN_HISTORIAN_AGENT]: HISTORIAN_STEP_CAP
22030
22073
  };
22031
22074
  class HiddenAgentStepLimit extends Error {
22032
22075
  agent;
@@ -23478,11 +23521,10 @@ var MIN_PROACTIVE_TAIL_MESSAGE_COUNT = 12;
23478
23521
  var DEFAULT_MIN_COMMIT_CLUSTERS_FOR_TRIGGER = 3;
23479
23522
  var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
23480
23523
  var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
23481
- var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
23482
23524
  function tagOwnerMessageId(row) {
23483
23525
  if (row.type === "tool")
23484
23526
  return row.tool_owner_message_id ?? row.message_id;
23485
- return row.message_id.replace(CONTENT_TAG_OWNER_SUFFIX, "");
23527
+ return contentTagOwnerMessageId(row.message_id);
23486
23528
  }
23487
23529
  function getActiveOrDroppedTagOwnerMessageIds(db, sessionId, floor = 0) {
23488
23530
  const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id
@@ -25591,6 +25633,164 @@ function hasActiveAnthropicThinkingTurn(messages, provider, model) {
25591
25633
  }
25592
25634
  return false;
25593
25635
  }
25636
+ function protectNewTagMutations(messages, targets, protectedParts, prefixBound) {
25637
+ if (protectedParts.size === 0)
25638
+ return targets;
25639
+ const positions = new Map;
25640
+ let ordinal = 0;
25641
+ for (const message of messages)
25642
+ for (const part of message.parts)
25643
+ positions.set(part, ordinal++);
25644
+ let lastProtected = -1;
25645
+ for (const part of protectedParts)
25646
+ lastProtected = Math.max(lastProtected, positions.get(part) ?? -1);
25647
+ const result = new Map;
25648
+ for (const [tag, target] of targets) {
25649
+ const message = target.message;
25650
+ const coordinates = target.mutationParts ?? (message ? message.parts.map((part) => ({ message, part })) : []);
25651
+ const prefixEdit = prefixBound && coordinates.some(({ part }) => (positions.get(part) ?? Infinity) < lastProtected);
25652
+ const dropsThinking = target.dropReasoningParts?.some((part) => protectedParts.has(part)) === true;
25653
+ if (!prefixEdit && !dropsThinking) {
25654
+ result.set(tag, target);
25655
+ continue;
25656
+ }
25657
+ result.set(tag, {
25658
+ ...target,
25659
+ thinkingDropProtected: true,
25660
+ thinkingRewriteProtected: prefixEdit,
25661
+ canDrop: () => false,
25662
+ drop: () => "incomplete",
25663
+ truncate: () => "incomplete",
25664
+ skeletonReal: () => "incomplete",
25665
+ skeletonStripped: () => "incomplete",
25666
+ editMarker: () => "incomplete",
25667
+ editMarkerStripped: () => "incomplete",
25668
+ setContent: (content, options) => prefixEdit || options?.keepReasoning !== true ? false : target.setContent(content, options)
25669
+ });
25670
+ }
25671
+ return result;
25672
+ }
25673
+
25674
+ // ../plugin/src/hooks/magic-context/latest-thinking-recovery.ts
25675
+ var LATEST_THINKING_RESTORE = "latest_thinking_original";
25676
+ var knownRejectedSessions = new Set;
25677
+ var lastPassTurnAnchor = new Map;
25678
+ var ACTIVE = `${LATEST_THINKING_RESTORE}:`;
25679
+ var ARMED = `${LATEST_THINKING_RESTORE}_armed:`;
25680
+ var UNRECOVERABLE = `${LATEST_THINKING_RESTORE}_unavailable`;
25681
+ var LATEST_THINKING_UNSAFE = "ANTHROPIC_LATEST_TURN_EDIT_UNSAFE: The provider rejected edits to this thinking turn and its original context cannot be safely replayed. Send a new user message or /clear to continue.";
25682
+ function armLatestThinkingRecovery(db, sessionId) {
25683
+ knownRejectedSessions.add(sessionId);
25684
+ ensureSessionMetaRow(db, sessionId);
25685
+ const current = getThinkingBindingRecoveryTarget(db, sessionId);
25686
+ const anchor = lastPassTurnAnchor.get(sessionId);
25687
+ const next = current?.startsWith(ACTIVE) ? UNRECOVERABLE + ":" + current.slice(ACTIVE.length) : current?.startsWith(UNRECOVERABLE) || current?.startsWith(ARMED) ? current : anchor !== undefined ? ARMED + anchor : LATEST_THINKING_RESTORE;
25688
+ db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = ? WHERE session_id = ?").run(next, sessionId);
25689
+ }
25690
+ function clone2(value) {
25691
+ if (Array.isArray(value))
25692
+ return value.map(clone2);
25693
+ if (!isRecord(value))
25694
+ return value;
25695
+ const proto = Object.getPrototypeOf(value);
25696
+ if (proto !== Object.prototype && proto !== null)
25697
+ return value;
25698
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clone2(item)]));
25699
+ }
25700
+ function isThinkingPart(part) {
25701
+ return isRecord(part) && ["thinking", "reasoning", "redacted_thinking"].includes(String(part.type));
25702
+ }
25703
+ function replayableOriginal(part) {
25704
+ if (!isThinkingPart(part) || !isRecord(part))
25705
+ return false;
25706
+ const metadata = isRecord(part.metadata) && isRecord(part.metadata.anthropic) ? part.metadata.anthropic : {};
25707
+ const signature = part.signature ?? part.thinkingSignature ?? metadata.signature ?? part.data ?? metadata.redactedData;
25708
+ return typeof signature === "string" && signature.length > 0 && part.text !== "[cleared]" && part.thinking !== "[cleared]";
25709
+ }
25710
+ function prepareLatestThinkingRecovery(args) {
25711
+ const start = latestAssistantTurnStart(args.messages);
25712
+ const anchor = start > 0 ? args.id(args.messages[start - 1], start - 1) : undefined;
25713
+ if (anchor === undefined)
25714
+ lastPassTurnAnchor.delete(args.sessionId);
25715
+ else
25716
+ lastPassTurnAnchor.set(args.sessionId, anchor);
25717
+ let target;
25718
+ try {
25719
+ target = getThinkingBindingRecoveryTarget(args.db, args.sessionId);
25720
+ } catch (error) {
25721
+ if (knownRejectedSessions.has(args.sessionId))
25722
+ throw contextRefusalError(LATEST_THINKING_UNSAFE);
25723
+ return { restore: false, ended: false };
25724
+ }
25725
+ if (!target || !target.startsWith(LATEST_THINKING_RESTORE)) {
25726
+ knownRejectedSessions.delete(args.sessionId);
25727
+ return { restore: false, ended: false };
25728
+ }
25729
+ const armedAnchor = target.startsWith(ARMED) ? target.slice(ARMED.length) : undefined;
25730
+ if (target === LATEST_THINKING_RESTORE || armedAnchor !== undefined && armedAnchor !== anchor) {
25731
+ args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = '' WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(args.sessionId, target);
25732
+ knownRejectedSessions.delete(args.sessionId);
25733
+ return { restore: false, ended: false };
25734
+ }
25735
+ knownRejectedSessions.add(args.sessionId);
25736
+ const savedAnchor = target.startsWith(ACTIVE) ? target.slice(ACTIVE.length) : target.startsWith(UNRECOVERABLE + ":") ? target.slice(UNRECOVERABLE.length + 1) : undefined;
25737
+ if (savedAnchor !== undefined && savedAnchor !== anchor) {
25738
+ args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = '' WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(args.sessionId, target);
25739
+ knownRejectedSessions.delete(args.sessionId);
25740
+ return { restore: false, ended: true };
25741
+ }
25742
+ if (target.startsWith(UNRECOVERABLE) || !anchor)
25743
+ throw contextRefusalError(LATEST_THINKING_UNSAFE);
25744
+ if (armedAnchor !== undefined) {
25745
+ args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = ? WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(ACTIVE + anchor, args.sessionId, target);
25746
+ }
25747
+ const active = args.messages.slice(start);
25748
+ if (!active.some((message) => args.parts(message).some(replayableOriginal)))
25749
+ throw contextRefusalError(LATEST_THINKING_UNSAFE + " [missing-thinking-original]");
25750
+ const ids = new Map(args.messages.map((message, index) => [args.id(message, index), index]));
25751
+ for (const decision of getMergedReasoningStrippedIds(args.db, args.sessionId)) {
25752
+ if (decision === "binding_mismatch_order:end" || decision === TOOL_SWEEP_SCOPED_MARKER)
25753
+ continue;
25754
+ const id = decodeMergedReasoningParts(decision)?.[0] ?? (decision.startsWith("binding_mismatch:") ? decision.slice("binding_mismatch:".length) : decision);
25755
+ const index = ids.get(id);
25756
+ if (index === undefined || index >= start && !args.parts(args.messages[index]).some(replayableOriginal))
25757
+ throw contextRefusalError(LATEST_THINKING_UNSAFE + ` [missing-original:${id}]`);
25758
+ }
25759
+ return { restore: true, ended: false };
25760
+ }
25761
+ function latestThinkingRecoveryCoversActiveTurn(args) {
25762
+ let target;
25763
+ try {
25764
+ target = getThinkingBindingRecoveryTarget(args.db, args.sessionId);
25765
+ } catch {
25766
+ return false;
25767
+ }
25768
+ const bound = target?.startsWith(ARMED) ? target.slice(ARMED.length) : target?.startsWith(ACTIVE) ? target.slice(ACTIVE.length) : undefined;
25769
+ if (bound === undefined)
25770
+ return false;
25771
+ const start = latestAssistantTurnStart(args.messages);
25772
+ return start > 0 && args.id(args.messages[start - 1], start - 1) === bound;
25773
+ }
25774
+ function captureOriginalTurn(messages, parts, setParts) {
25775
+ const start = Math.max(0, latestAssistantTurnStart(messages) - 1);
25776
+ const originals = messages.slice(start).map((message) => ({ message, parts: clone2(parts(message)) }));
25777
+ const before = messages.slice(0, start);
25778
+ return (target = messages) => {
25779
+ const first = target.findIndex((message) => originals.some((original) => original.message === message || isRecord(message) && isRecord(original.message) && (isRecord(message.info) && isRecord(original.message.info) && message.info.id === original.message.info.id || message.role === original.message.role && message.timestamp !== undefined && message.timestamp === original.message.timestamp)));
25780
+ const prefix = first >= 0 ? target.slice(0, first) : target === messages ? before : target.slice();
25781
+ for (const original of originals) {
25782
+ setParts(original.message, clone2(original.parts));
25783
+ }
25784
+ target.length = 0;
25785
+ target.push(...prefix, ...originals.map((original) => original.message));
25786
+ };
25787
+ }
25788
+ function armBindingRecoverySafely(db, sessionId) {
25789
+ if (getThinkingBindingRecoveryTarget(db, sessionId)?.startsWith(LATEST_THINKING_RESTORE))
25790
+ armLatestThinkingRecovery(db, sessionId);
25791
+ else
25792
+ armThinkingBindingRecovery(db, sessionId);
25793
+ }
25594
25794
 
25595
25795
  // ../plugin/src/hooks/magic-context/reasoning-budget-status.ts
25596
25796
  var sources = new Map;
@@ -25740,51 +25940,6 @@ function readFilePath(target) {
25740
25940
  return null;
25741
25941
  }
25742
25942
 
25743
- // ../plugin/src/hooks/magic-context/system-injection-stripper.ts
25744
- var SYSTEM_INJECTION_MARKERS = [
25745
- "<!-- OMO_INTERNAL_INITIATOR -->",
25746
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
25747
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
25748
- "[Category+Skill Reminder]",
25749
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
25750
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
25751
- "[EMERGENCY CONTEXT WINDOW WARNING]",
25752
- "Unstable background agent appears idle",
25753
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
25754
- ];
25755
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
25756
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
25757
- function stripSystemInjection(text) {
25758
- let hasInjection = false;
25759
- for (const marker of SYSTEM_INJECTION_MARKERS) {
25760
- if (text.includes(marker)) {
25761
- hasInjection = true;
25762
- break;
25763
- }
25764
- }
25765
- if (SYSTEM_REMINDER_REGEX.test(text))
25766
- hasInjection = true;
25767
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
25768
- if (!hasInjection)
25769
- return null;
25770
- let cleaned = text;
25771
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
25772
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
25773
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
25774
- for (const marker of SYSTEM_INJECTION_MARKERS) {
25775
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
25776
- continue;
25777
- const idx = cleaned.indexOf(marker);
25778
- if (idx === -1)
25779
- continue;
25780
- const blockEnd = cleaned.indexOf(`
25781
-
25782
- `, idx + marker.length);
25783
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
25784
- }
25785
- return cleaned.trim();
25786
- }
25787
-
25788
25943
  // ../plugin/src/hooks/magic-context/tool-reclaim.ts
25789
25944
  function buildSyntheticToolReclaimOps(input) {
25790
25945
  const watermark = Math.max(0, input.watermark);
@@ -26417,6 +26572,10 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
26417
26572
  return !occurrences.every((occ, index) => occ.part.remove && authorization[index] !== false);
26418
26573
  };
26419
26574
  return {
26575
+ mutationParts: occurrences.map((occ) => ({
26576
+ message: { info: occ.message.info, parts: [] },
26577
+ part: occ.part
26578
+ })),
26420
26579
  measureReclaim(skeleton) {
26421
26580
  let beforeTools = 0;
26422
26581
  let afterTools = 0;
@@ -26530,6 +26689,7 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
26530
26689
  }
26531
26690
  function buildTextTarget(part, message, textPrefix) {
26532
26691
  return {
26692
+ mutationParts: [{ message: { info: message.info, parts: [] }, part }],
26533
26693
  textPrefix,
26534
26694
  setContent(content) {
26535
26695
  return part.setText(content);
@@ -26545,6 +26705,7 @@ function buildTextTarget(part, message, textPrefix) {
26545
26705
  }
26546
26706
  function buildToolTarget(part, message, tagId) {
26547
26707
  return {
26708
+ mutationParts: [{ message: { info: message.info, parts: [] }, part }],
26548
26709
  setContent(content) {
26549
26710
  return setToolContentOrText(part, content);
26550
26711
  },
@@ -33509,391 +33670,6 @@ function recordPiTransformTiming(sample) {
33509
33670
  // src/ctx-reduce-nudge-pi.ts
33510
33671
  import { randomUUID as randomUUID4 } from "node:crypto";
33511
33672
 
33512
- // src/heuristic-cleanup-pi.ts
33513
- var PI_CTX_REDUCE_KEEP = CTX_REDUCE_KEEP;
33514
- var DEDUP_SAFE_TOOLS = new Set([
33515
- "grep",
33516
- "read",
33517
- "glob",
33518
- "ast_grep_search",
33519
- "lsp_diagnostics",
33520
- "lsp_symbols",
33521
- "lsp_find_references",
33522
- "lsp_goto_definition",
33523
- "lsp_prepare_rename",
33524
- "mcp_grep",
33525
- "mcp_read",
33526
- "mcp_glob",
33527
- "mcp_ast_grep_search",
33528
- "mcp_lsp_diagnostics",
33529
- "mcp_lsp_symbols",
33530
- "mcp_lsp_find_references",
33531
- "mcp_lsp_goto_definition",
33532
- "mcp_lsp_prepare_rename"
33533
- ]);
33534
- function buildPiToolFingerprints(messages, resolveStableId) {
33535
- const fingerprints = new Map;
33536
- for (let i = 0;i < messages.length; i++) {
33537
- const message = messages[i];
33538
- if (!message || typeof message !== "object")
33539
- continue;
33540
- const msg = message;
33541
- if (msg.role !== "assistant")
33542
- continue;
33543
- if (!Array.isArray(msg.content))
33544
- continue;
33545
- const ownerMsgId = resolveStableId(message, i);
33546
- if (!ownerMsgId)
33547
- continue;
33548
- for (const part of msg.content) {
33549
- if (!part || typeof part !== "object")
33550
- continue;
33551
- const p = part;
33552
- if (p.type !== "toolCall")
33553
- continue;
33554
- if (typeof p.name !== "string")
33555
- continue;
33556
- if (!DEDUP_SAFE_TOOLS.has(p.name))
33557
- continue;
33558
- if (typeof p.id !== "string" || p.id.length === 0)
33559
- continue;
33560
- const args = p.arguments;
33561
- if (args && typeof args === "object" && "__magic_context_dropped__" in args) {
33562
- continue;
33563
- }
33564
- let serialized;
33565
- try {
33566
- serialized = JSON.stringify(args ?? {});
33567
- } catch {
33568
- continue;
33569
- }
33570
- const fingerprint = `${ownerMsgId}:${p.name}:${serialized}`;
33571
- const compositeKey = `${ownerMsgId}\x00${p.id}`;
33572
- fingerprints.set(compositeKey, fingerprint);
33573
- }
33574
- }
33575
- return fingerprints;
33576
- }
33577
- function collectStaleReduceCallIds(messages, messageIdToMaxTag, ctxReduceTagNumbers, toolAgeCutoff, resolveStableId) {
33578
- const reduceCalls = new Map;
33579
- for (let i = 0;i < messages.length; i++) {
33580
- const raw = messages[i];
33581
- if (!raw || typeof raw !== "object")
33582
- continue;
33583
- const msg = raw;
33584
- if (msg.role !== "assistant" || !Array.isArray(msg.content))
33585
- continue;
33586
- const stableId = resolveStableId(raw, i);
33587
- if (!stableId)
33588
- continue;
33589
- const ownerMaxTag = messageIdToMaxTag.get(stableId) ?? 0;
33590
- for (const part of msg.content) {
33591
- if (!part || typeof part !== "object")
33592
- continue;
33593
- const p = part;
33594
- if (p.type !== "toolCall")
33595
- continue;
33596
- if (p.name !== "mmc_reduce")
33597
- continue;
33598
- if (typeof p.id !== "string" || p.id.length === 0)
33599
- continue;
33600
- const composite = `${stableId}\x00${p.id}`;
33601
- const maxTag = ctxReduceTagNumbers.get(composite) ?? ownerMaxTag;
33602
- if (maxTag === 0)
33603
- continue;
33604
- reduceCalls.set(composite, {
33605
- composite,
33606
- callId: p.id,
33607
- maxTag,
33608
- messageIndex: i
33609
- });
33610
- }
33611
- }
33612
- const newestFirst = [...reduceCalls.values()].sort((left, right) => {
33613
- const byPosition = right.maxTag - left.maxTag || right.messageIndex - left.messageIndex;
33614
- if (byPosition !== 0)
33615
- return byPosition;
33616
- if (left.composite === right.composite)
33617
- return 0;
33618
- return left.composite < right.composite ? 1 : -1;
33619
- });
33620
- const composite = new Set;
33621
- const bareCallIds = new Set;
33622
- for (const call of newestFirst) {
33623
- if (call.maxTag > toolAgeCutoff)
33624
- continue;
33625
- composite.add(call.composite);
33626
- bareCallIds.add(call.callId);
33627
- }
33628
- return { composite, bareCallIds };
33629
- }
33630
- function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, preloadedTags, resolveId) {
33631
- const resolveStableId = (msg, index) => {
33632
- if (resolveId)
33633
- return resolveId(msg, index);
33634
- if (!msg || typeof msg !== "object")
33635
- return;
33636
- const m = msg;
33637
- const role = typeof m.role === "string" ? m.role : "unknown";
33638
- return typeof m.timestamp === "number" ? `pi-msg-${index}-${m.timestamp}-${role}` : `pi-msg-${index}-${role}`;
33639
- };
33640
- const tags = preloadedTags ?? getActiveTagsBySession(db, sessionId);
33641
- const protectedTools = config.protectedToolTags ?? protectedToolTagNumbers(tags, config.protectedTools);
33642
- const maxTag = getMaxTagNumberBySession(db, sessionId);
33643
- const protectedCutoff = config.protectedCutoff === null ? maxTag + 1 : config.protectedCutoff ?? maxTag - config.protectedTags;
33644
- const routine = config.routine !== false;
33645
- const toolAgeCutoff = protectedCutoff;
33646
- let droppedTools = 0;
33647
- let emergencyDroppedTools = 0;
33648
- let deduplicatedTools = 0;
33649
- let droppedInjections = 0;
33650
- let droppedStaleReduceCalls = 0;
33651
- const droppedTokenReductions = [];
33652
- if (config.emergency) {
33653
- const emergency = config.emergency;
33654
- const priorInputSample = getEmergencyInputSample(db, sessionId);
33655
- const candidateTags = tags.filter((t) => t.status === "active" && t.type === "tool" && targets.get(t.tagNumber)?.canDrop?.());
33656
- const recentTags = new Set(candidateTags.slice().sort((a, b) => b.tagNumber - a.tagNumber).slice(0, 20).map((tag) => tag.tagNumber));
33657
- const calibration = sessionDecisionCalibration(db, sessionId);
33658
- const activeTags = tags.filter((t) => t.status === "active").map((tag) => measureEmergencyTag(tag, targets.get(tag.tagNumber), calibration, targets.get(tag.tagNumber)?.requiresToolArcSkeleton === true || (emergency.usagePercentage ?? 0) < 95 && recentTags.has(tag.tagNumber) && hasSmallToolInput(targets.get(tag.tagNumber))));
33659
- const byTag = new Map(activeTags.map((tag) => [tag.tagNumber, tag]));
33660
- const droppableTags = candidateTags.flatMap((tag) => {
33661
- const measured = byTag.get(tag.tagNumber);
33662
- return measured ? [measured] : [];
33663
- }).filter((tag) => (tag.reclaimableTokens ?? 0) > 0);
33664
- sessionLog(sessionId, `emergency candidates: loaded=${tags.length} active=${activeTags.length} activeTools=${activeTags.filter((tag) => tag.type === "tool").length} visibleCompleteTools=${droppableTags.length} windowYields=${(emergency.usagePercentage ?? 0) >= 95} cutoff=${protectedCutoff}`);
33665
- const plan = planEmergencyDrop({
33666
- tags: droppableTags,
33667
- floorTags: activeTags,
33668
- maxTag,
33669
- protectedCutoff,
33670
- currentTotalInputTokens: emergency.currentTotalInputTokens,
33671
- ceilingTokens: emergency.ceilingTokens,
33672
- usagePercentage: emergency.usagePercentage,
33673
- priorInputSample,
33674
- hasPriorDrop: priorInputSample > 0,
33675
- passAlreadyPriced: emergency.passAlreadyPriced === true,
33676
- protectedToolTags: protectedTools
33677
- });
33678
- if (plan.shouldDrop) {
33679
- const toDrop = new Set(plan.tagNumbers);
33680
- const newestEmergencyTags = recentTags;
33681
- db.transaction(() => {
33682
- for (const tag of tags) {
33683
- if (!toDrop.has(tag.tagNumber))
33684
- continue;
33685
- if (tag.status !== "active" || tag.type !== "tool")
33686
- continue;
33687
- const target = targets.get(tag.tagNumber);
33688
- const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
33689
- const { result, mode } = applyNewToolDrop(target, {
33690
- inWindow: recent,
33691
- keepSkeleton: target?.requiresToolArcSkeleton === true
33692
- });
33693
- if (result === "removed" || result === "truncated") {
33694
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
33695
- updateTagDropMode(db, sessionId, tag.tagNumber, mode);
33696
- droppedTools++;
33697
- emergencyDroppedTools++;
33698
- droppedTokenReductions.push({
33699
- tagNumber: tag.tagNumber,
33700
- mode: mode === "full" ? "full" : "truncated"
33701
- });
33702
- }
33703
- }
33704
- }).immediate();
33705
- sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
33706
- } else {
33707
- sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
33708
- }
33709
- }
33710
- const staleReduce = routine && config.staleReduceStripEnabled ? collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), buildCtxReduceTagNumbers(tags), toolAgeCutoff, resolveStableId) : { composite: new Set, bareCallIds: new Set };
33711
- if (routine && config.staleReduceStripEnabled && (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0)) {
33712
- db.transaction(() => {
33713
- for (const tag of tags) {
33714
- if (tag.status !== "active")
33715
- continue;
33716
- if (tag.type !== "tool")
33717
- continue;
33718
- if (protectedTools.has(tag.tagNumber))
33719
- continue;
33720
- if (!tag.messageId)
33721
- continue;
33722
- const matched = tag.toolOwnerMessageId ? staleReduce.composite.has(`${tag.toolOwnerMessageId}\x00${tag.messageId}`) : staleReduce.bareCallIds.has(tag.messageId);
33723
- if (!matched)
33724
- continue;
33725
- const target = targets.get(tag.tagNumber);
33726
- if (target?.canDrop?.() === false)
33727
- continue;
33728
- const { result, mode } = applyNewToolDrop(target, { inWindow: false });
33729
- if (result === "incomplete")
33730
- continue;
33731
- updateTagDropMode(db, sessionId, tag.tagNumber, mode);
33732
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
33733
- if (result === "removed" || result === "truncated") {
33734
- droppedStaleReduceCalls++;
33735
- config.onStaleReduceEdit?.(tag);
33736
- }
33737
- }
33738
- }).immediate();
33739
- }
33740
- if (routine) {
33741
- db.transaction(() => {
33742
- for (const tag of tags) {
33743
- if (tag.status !== "active")
33744
- continue;
33745
- if (tag.tagNumber > protectedCutoff)
33746
- continue;
33747
- if (tag.type !== "message")
33748
- continue;
33749
- const target = targets.get(tag.tagNumber);
33750
- if (!target)
33751
- continue;
33752
- const content = target.getContent?.();
33753
- if (!content)
33754
- continue;
33755
- const stripped = stripSystemInjection(content);
33756
- if (stripped === null)
33757
- continue;
33758
- const strippedSource = stripTagPrefix(stripped);
33759
- if (strippedSource.trim().length === 0) {
33760
- if (target.thinkingDropProtected)
33761
- continue;
33762
- const dropResult = target.drop?.() ?? "absent";
33763
- const didReplace = dropResult === "absent" ? target.setContent(`[dropped §${tag.tagNumber}§]`) : false;
33764
- if (dropResult === "removed" || dropResult === "absent") {
33765
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
33766
- if (dropResult === "removed" || didReplace) {
33767
- droppedInjections++;
33768
- droppedTokenReductions.push({
33769
- tagNumber: tag.tagNumber,
33770
- mode: "full"
33771
- });
33772
- }
33773
- }
33774
- } else {
33775
- if (freezePiContentDecision(db, sessionId, "reminder-strip", tag.messageId)) {
33776
- if (target.setContent(stripped)) {
33777
- droppedInjections++;
33778
- droppedTokenReductions.push({
33779
- tagNumber: tag.tagNumber,
33780
- mode: "partial",
33781
- removedCharacters: Math.max(0, content.length - stripped.length)
33782
- });
33783
- }
33784
- }
33785
- }
33786
- }
33787
- }).immediate();
33788
- }
33789
- const toolFingerprints = routine ? buildPiToolFingerprints(piMessages, resolveStableId) : new Map;
33790
- if (toolFingerprints.size > 0) {
33791
- const tagsByCompositeKey = new Map;
33792
- for (const tag of tags) {
33793
- if (tag.type === "tool" && tag.status === "active" && tag.messageId) {
33794
- const key = tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId;
33795
- tagsByCompositeKey.set(key, tag);
33796
- }
33797
- }
33798
- const fingerprintGroups = new Map;
33799
- for (const [compositeKey, fingerprint] of toolFingerprints) {
33800
- const tag = tagsByCompositeKey.get(compositeKey);
33801
- if (!tag)
33802
- continue;
33803
- const group = fingerprintGroups.get(fingerprint) ?? [];
33804
- group.push(tag);
33805
- fingerprintGroups.set(fingerprint, group);
33806
- }
33807
- db.transaction(() => {
33808
- for (const [, group] of fingerprintGroups) {
33809
- if (group.length <= 1)
33810
- continue;
33811
- group.sort((a, b) => a.tagNumber - b.tagNumber);
33812
- for (let i = 0;i < group.length - 1; i++) {
33813
- const tag = group[i];
33814
- if (tag.tagNumber > protectedCutoff)
33815
- continue;
33816
- const target = targets.get(tag.tagNumber);
33817
- if (protectedTools.has(tag.tagNumber))
33818
- continue;
33819
- if (target?.canDrop?.() === false)
33820
- continue;
33821
- const { result, mode } = applyNewToolDrop(target, {
33822
- inWindow: false
33823
- });
33824
- if (result === "incomplete")
33825
- continue;
33826
- updateTagDropMode(db, sessionId, tag.tagNumber, mode);
33827
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
33828
- if (result === "removed" || result === "truncated") {
33829
- deduplicatedTools++;
33830
- droppedTokenReductions.push({
33831
- tagNumber: tag.tagNumber,
33832
- mode: mode === "full" ? "full" : "truncated"
33833
- });
33834
- }
33835
- }
33836
- }
33837
- }).immediate();
33838
- }
33839
- if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0 || droppedStaleReduceCalls > 0) {
33840
- sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale mmc_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
33841
- }
33842
- let compressedTextTags = 0;
33843
- let mutatedTextTags = 0;
33844
- if (routine && config.caveman?.enabled) {
33845
- const cavemanTags = getActiveTagsBySession(db, sessionId);
33846
- const cavemanResult = applyCavemanCleanup(sessionId, db, targets, cavemanTags, {
33847
- enabled: true,
33848
- minChars: config.caveman.minChars,
33849
- wordRules: config.caveman.wordRules,
33850
- protectedCutoff
33851
- });
33852
- compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
33853
- mutatedTextTags = cavemanResult.mutatedTextTags;
33854
- if (cavemanResult.textReductions) {
33855
- for (const r of cavemanResult.textReductions) {
33856
- droppedTokenReductions.push({
33857
- tagNumber: r.tagNumber,
33858
- mode: "partial",
33859
- removedCharacters: r.removedCharacters
33860
- });
33861
- }
33862
- }
33863
- }
33864
- return {
33865
- droppedTools,
33866
- deduplicatedTools,
33867
- droppedInjections,
33868
- droppedStaleReduceCalls,
33869
- emergencyDroppedTools,
33870
- compressedTextTags,
33871
- mutatedTextTags,
33872
- droppedTokenReductions
33873
- };
33874
- }
33875
- function buildCtxReduceTagNumbers(tags) {
33876
- const byComposite = new Map;
33877
- for (const tag of tags) {
33878
- if (tag.status !== "active" || tag.type !== "tool" || tag.toolName !== "mmc_reduce" || !tag.toolOwnerMessageId) {
33879
- continue;
33880
- }
33881
- byComposite.set(`${tag.toolOwnerMessageId}\x00${tag.messageId}`, tag.tagNumber);
33882
- }
33883
- return byComposite;
33884
- }
33885
- function buildMessageIdToMaxTagFromTargets(targets) {
33886
- const byMessage = new Map;
33887
- for (const [tagNumber, target] of targets) {
33888
- const id = target.message?.info?.id;
33889
- if (typeof id !== "string" || id.length === 0)
33890
- continue;
33891
- if (tagNumber > (byMessage.get(id) ?? 0))
33892
- byMessage.set(id, tagNumber);
33893
- }
33894
- return byMessage;
33895
- }
33896
-
33897
33673
  // src/tail-hygiene-walk-pi.ts
33898
33674
  var TAG_PREFIX = /^§(\d+)§\s*/;
33899
33675
  var SYNTHETIC_TODO_PREFIX = "mc_synthetic_todo_";
@@ -34205,14 +33981,14 @@ function excludedDraft(key, value) {
34205
33981
  tokens: 0
34206
33982
  };
34207
33983
  }
34208
- function finalizeParts(drafts, pendingDropTagNumbers, protectedTagNumbers, newestMessagePartStart) {
33984
+ function finalizeParts(drafts, pendingDropTagNumbers, protectedTagNumbers, newestMessagePartStart, protectedToolTags) {
34209
33985
  const visibleTags = new Map;
34210
33986
  for (const part of drafts) {
34211
33987
  if (part.kind !== "excluded" && part.tag)
34212
33988
  visibleTags.set(part.tag.tagNumber, part.tag);
34213
33989
  }
34214
33990
  const protectedNumbers = new Set(protectedTagNumbers);
34215
- const exemplarNumbers = [...visibleTags.values()].filter((tag) => tag.type === "tool" && tag.toolName === "mmc_reduce").sort((left, right) => right.tagNumber - left.tagNumber).slice(0, PI_CTX_REDUCE_KEEP).map((tag) => tag.tagNumber);
33991
+ const exemplarNumbers = protectedToolTags ?? protectedToolTagNumbers([...visibleTags.values()]);
34216
33992
  for (const tagNumber of exemplarNumbers)
34217
33993
  protectedNumbers.add(tagNumber);
34218
33994
  let u = 0;
@@ -34379,9 +34155,14 @@ function measurePiTailHygiene(input) {
34379
34155
  drafts.push(excludedDraft(`${key}\x00excluded`, part));
34380
34156
  }
34381
34157
  }
34382
- return finalizeParts(drafts, input.pendingDropTagNumbers ?? new Set, input.protectedTagNumbers, newestMessagePartStart);
34158
+ return finalizeParts(drafts, input.pendingDropTagNumbers ?? new Set, input.protectedTagNumbers, newestMessagePartStart, input.protectedToolTags);
34383
34159
  }
34384
34160
  function refreshPiTailHygieneBaseline(input) {
34161
+ const protectedToolsPolicy = adoptedProtectedToolsPolicy(input.protectedTools, input.previous?.protectedToolsPolicy, input.cacheBusting, !!input.previous);
34162
+ input = {
34163
+ ...input,
34164
+ protectedToolTags: protectedToolTagNumbers(input.tags, protectedToolsPolicy)
34165
+ };
34385
34166
  const rawMeasured = measurePiTailHygiene(input);
34386
34167
  const frozenCalibration = !input.cacheBusting && input.previous ? {
34387
34168
  toolsRatio: input.previous.toolsRatio,
@@ -34406,6 +34187,7 @@ function refreshPiTailHygieneBaseline(input) {
34406
34187
  const now = input.now ?? Date.now();
34407
34188
  const refrozen = (mismatch) => ({
34408
34189
  ...freezeTailHygieneMeasurement(measured),
34190
+ protectedToolsPolicy,
34409
34191
  hygieneUnitsVersion: frozenCalibration.hygieneUnitsVersion,
34410
34192
  toolsRatio: frozenCalibration.toolsRatio,
34411
34193
  proseRatio: frozenCalibration.proseRatio,
@@ -34432,6 +34214,7 @@ function refreshPiTailHygieneBaseline(input) {
34432
34214
  }
34433
34215
  return {
34434
34216
  ...input.previous,
34217
+ protectedToolsPolicy,
34435
34218
  turnDeltaU,
34436
34219
  turnDeltaT,
34437
34220
  evaluable: true,
@@ -34789,6 +34572,390 @@ function hasPiFallbackMessageTags2(db, sessionId) {
34789
34572
  return probe(db, sessionId, "message");
34790
34573
  }
34791
34574
 
34575
+ // src/heuristic-cleanup-pi.ts
34576
+ var DEDUP_SAFE_TOOLS = new Set([
34577
+ "grep",
34578
+ "read",
34579
+ "glob",
34580
+ "ast_grep_search",
34581
+ "lsp_diagnostics",
34582
+ "lsp_symbols",
34583
+ "lsp_find_references",
34584
+ "lsp_goto_definition",
34585
+ "lsp_prepare_rename",
34586
+ "mcp_grep",
34587
+ "mcp_read",
34588
+ "mcp_glob",
34589
+ "mcp_ast_grep_search",
34590
+ "mcp_lsp_diagnostics",
34591
+ "mcp_lsp_symbols",
34592
+ "mcp_lsp_find_references",
34593
+ "mcp_lsp_goto_definition",
34594
+ "mcp_lsp_prepare_rename"
34595
+ ]);
34596
+ function buildPiToolFingerprints(messages, resolveStableId) {
34597
+ const fingerprints = new Map;
34598
+ for (let i = 0;i < messages.length; i++) {
34599
+ const message = messages[i];
34600
+ if (!message || typeof message !== "object")
34601
+ continue;
34602
+ const msg = message;
34603
+ if (msg.role !== "assistant")
34604
+ continue;
34605
+ if (!Array.isArray(msg.content))
34606
+ continue;
34607
+ const ownerMsgId = resolveStableId(message, i);
34608
+ if (!ownerMsgId)
34609
+ continue;
34610
+ for (const part of msg.content) {
34611
+ if (!part || typeof part !== "object")
34612
+ continue;
34613
+ const p = part;
34614
+ if (p.type !== "toolCall")
34615
+ continue;
34616
+ if (typeof p.name !== "string")
34617
+ continue;
34618
+ if (!DEDUP_SAFE_TOOLS.has(p.name))
34619
+ continue;
34620
+ if (typeof p.id !== "string" || p.id.length === 0)
34621
+ continue;
34622
+ const args = p.arguments;
34623
+ if (args && typeof args === "object" && "__magic_context_dropped__" in args) {
34624
+ continue;
34625
+ }
34626
+ let serialized;
34627
+ try {
34628
+ serialized = JSON.stringify(args ?? {});
34629
+ } catch {
34630
+ continue;
34631
+ }
34632
+ const fingerprint = `${ownerMsgId}:${p.name}:${serialized}`;
34633
+ const compositeKey = `${ownerMsgId}\x00${p.id}`;
34634
+ fingerprints.set(compositeKey, fingerprint);
34635
+ }
34636
+ }
34637
+ return fingerprints;
34638
+ }
34639
+ function collectStaleReduceCallIds(messages, messageIdToMaxTag, ctxReduceTagNumbers, toolAgeCutoff, resolveStableId) {
34640
+ const reduceCalls = new Map;
34641
+ for (let i = 0;i < messages.length; i++) {
34642
+ const raw = messages[i];
34643
+ if (!raw || typeof raw !== "object")
34644
+ continue;
34645
+ const msg = raw;
34646
+ if (msg.role !== "assistant" || !Array.isArray(msg.content))
34647
+ continue;
34648
+ const stableId = resolveStableId(raw, i);
34649
+ if (!stableId)
34650
+ continue;
34651
+ const ownerMaxTag = messageIdToMaxTag.get(stableId) ?? 0;
34652
+ for (const part of msg.content) {
34653
+ if (!part || typeof part !== "object")
34654
+ continue;
34655
+ const p = part;
34656
+ if (p.type !== "toolCall")
34657
+ continue;
34658
+ if (p.name !== "mmc_reduce")
34659
+ continue;
34660
+ if (typeof p.id !== "string" || p.id.length === 0)
34661
+ continue;
34662
+ const composite = `${stableId}\x00${p.id}`;
34663
+ const maxTag = ctxReduceTagNumbers.get(composite) ?? ownerMaxTag;
34664
+ if (maxTag === 0)
34665
+ continue;
34666
+ reduceCalls.set(composite, {
34667
+ composite,
34668
+ callId: p.id,
34669
+ maxTag,
34670
+ messageIndex: i
34671
+ });
34672
+ }
34673
+ }
34674
+ const newestFirst = [...reduceCalls.values()].sort((left, right) => {
34675
+ const byPosition = right.maxTag - left.maxTag || right.messageIndex - left.messageIndex;
34676
+ if (byPosition !== 0)
34677
+ return byPosition;
34678
+ if (left.composite === right.composite)
34679
+ return 0;
34680
+ return left.composite < right.composite ? 1 : -1;
34681
+ });
34682
+ const composite = new Set;
34683
+ const bareCallIds = new Set;
34684
+ for (const call of newestFirst) {
34685
+ if (call.maxTag > toolAgeCutoff)
34686
+ continue;
34687
+ composite.add(call.composite);
34688
+ bareCallIds.add(call.callId);
34689
+ }
34690
+ return { composite, bareCallIds };
34691
+ }
34692
+ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, preloadedTags, resolveId) {
34693
+ const resolveStableId = (msg, index) => {
34694
+ if (resolveId)
34695
+ return resolveId(msg, index);
34696
+ if (!msg || typeof msg !== "object")
34697
+ return;
34698
+ const m = msg;
34699
+ const role = typeof m.role === "string" ? m.role : "unknown";
34700
+ return typeof m.timestamp === "number" ? `pi-msg-${index}-${m.timestamp}-${role}` : `pi-msg-${index}-${role}`;
34701
+ };
34702
+ const tags = preloadedTags ?? getActiveTagsBySession(db, sessionId);
34703
+ const protectedTools = config.protectedToolTags ?? protectedToolTagNumbers(tags, config.protectedTools);
34704
+ const maxTag = getMaxTagNumberBySession(db, sessionId);
34705
+ const protectedCutoff = config.protectedCutoff === null ? maxTag + 1 : config.protectedCutoff ?? maxTag - config.protectedTags;
34706
+ const routine = config.routine !== false;
34707
+ const toolAgeCutoff = protectedCutoff;
34708
+ let droppedTools = 0;
34709
+ let emergencyDroppedTools = 0;
34710
+ let deduplicatedTools = 0;
34711
+ let droppedInjections = 0;
34712
+ let droppedStaleReduceCalls = 0;
34713
+ const droppedTokenReductions = [];
34714
+ if (config.emergency) {
34715
+ const emergency = config.emergency;
34716
+ const priorInputSample = getEmergencyInputSample(db, sessionId);
34717
+ const candidateTags = tags.filter((t) => t.status === "active" && t.type === "tool" && targets.get(t.tagNumber)?.canDrop?.());
34718
+ const recentTags = new Set(candidateTags.slice().sort((a, b) => b.tagNumber - a.tagNumber).slice(0, 20).map((tag) => tag.tagNumber));
34719
+ const calibration = sessionDecisionCalibration(db, sessionId);
34720
+ const activeTags = tags.filter((t) => t.status === "active").map((tag) => measureEmergencyTag(tag, targets.get(tag.tagNumber), calibration, targets.get(tag.tagNumber)?.requiresToolArcSkeleton === true || (emergency.usagePercentage ?? 0) < 95 && recentTags.has(tag.tagNumber) && hasSmallToolInput(targets.get(tag.tagNumber))));
34721
+ const byTag = new Map(activeTags.map((tag) => [tag.tagNumber, tag]));
34722
+ const droppableTags = candidateTags.flatMap((tag) => {
34723
+ const measured = byTag.get(tag.tagNumber);
34724
+ return measured ? [measured] : [];
34725
+ }).filter((tag) => (tag.reclaimableTokens ?? 0) > 0);
34726
+ sessionLog(sessionId, `emergency candidates: loaded=${tags.length} active=${activeTags.length} activeTools=${activeTags.filter((tag) => tag.type === "tool").length} visibleCompleteTools=${droppableTags.length} windowYields=${(emergency.usagePercentage ?? 0) >= 95} cutoff=${protectedCutoff}`);
34727
+ const plan = planEmergencyDrop({
34728
+ tags: droppableTags,
34729
+ floorTags: activeTags,
34730
+ maxTag,
34731
+ protectedCutoff,
34732
+ currentTotalInputTokens: emergency.currentTotalInputTokens,
34733
+ ceilingTokens: emergency.ceilingTokens,
34734
+ usagePercentage: emergency.usagePercentage,
34735
+ priorInputSample,
34736
+ hasPriorDrop: priorInputSample > 0,
34737
+ passAlreadyPriced: emergency.passAlreadyPriced === true,
34738
+ protectedToolTags: protectedTools
34739
+ });
34740
+ if (plan.shouldDrop) {
34741
+ const toDrop = new Set(plan.tagNumbers);
34742
+ const newestEmergencyTags = recentTags;
34743
+ db.transaction(() => {
34744
+ for (const tag of tags) {
34745
+ if (!toDrop.has(tag.tagNumber))
34746
+ continue;
34747
+ if (tag.status !== "active" || tag.type !== "tool")
34748
+ continue;
34749
+ const target = targets.get(tag.tagNumber);
34750
+ const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
34751
+ const { result, mode } = applyNewToolDrop(target, {
34752
+ inWindow: recent,
34753
+ keepSkeleton: target?.requiresToolArcSkeleton === true
34754
+ });
34755
+ if (result === "removed" || result === "truncated") {
34756
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
34757
+ updateTagDropMode(db, sessionId, tag.tagNumber, mode);
34758
+ droppedTools++;
34759
+ emergencyDroppedTools++;
34760
+ droppedTokenReductions.push({
34761
+ tagNumber: tag.tagNumber,
34762
+ mode: mode === "full" ? "full" : "truncated"
34763
+ });
34764
+ }
34765
+ }
34766
+ }).immediate();
34767
+ sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
34768
+ } else {
34769
+ sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
34770
+ }
34771
+ }
34772
+ const staleReduce = routine && config.staleReduceStripEnabled ? collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), buildCtxReduceTagNumbers(tags), toolAgeCutoff, resolveStableId) : { composite: new Set, bareCallIds: new Set };
34773
+ if (routine && config.staleReduceStripEnabled && (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0)) {
34774
+ db.transaction(() => {
34775
+ for (const tag of tags) {
34776
+ if (tag.status !== "active")
34777
+ continue;
34778
+ if (tag.type !== "tool")
34779
+ continue;
34780
+ if (protectedTools.has(tag.tagNumber))
34781
+ continue;
34782
+ if (!tag.messageId)
34783
+ continue;
34784
+ const matched = tag.toolOwnerMessageId ? staleReduce.composite.has(`${tag.toolOwnerMessageId}\x00${tag.messageId}`) : staleReduce.bareCallIds.has(tag.messageId);
34785
+ if (!matched)
34786
+ continue;
34787
+ const target = targets.get(tag.tagNumber);
34788
+ if (target?.canDrop?.() === false)
34789
+ continue;
34790
+ const { result, mode } = applyNewToolDrop(target, { inWindow: false });
34791
+ if (result === "incomplete")
34792
+ continue;
34793
+ updateTagDropMode(db, sessionId, tag.tagNumber, mode);
34794
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
34795
+ if (result === "removed" || result === "truncated") {
34796
+ droppedStaleReduceCalls++;
34797
+ config.onStaleReduceEdit?.(tag);
34798
+ }
34799
+ }
34800
+ }).immediate();
34801
+ }
34802
+ if (routine) {
34803
+ db.transaction(() => {
34804
+ for (const tag of tags) {
34805
+ if (tag.status !== "active")
34806
+ continue;
34807
+ if (tag.tagNumber > protectedCutoff)
34808
+ continue;
34809
+ if (tag.type !== "message")
34810
+ continue;
34811
+ const target = targets.get(tag.tagNumber);
34812
+ if (!target)
34813
+ continue;
34814
+ const content = target.getContent?.();
34815
+ if (!content)
34816
+ continue;
34817
+ const stripped = stripSystemInjection(content);
34818
+ if (stripped === null)
34819
+ continue;
34820
+ const strippedSource = stripTagPrefix(stripped);
34821
+ if (strippedSource.trim().length === 0) {
34822
+ if (target.thinkingDropProtected)
34823
+ continue;
34824
+ const dropResult = target.drop?.() ?? "absent";
34825
+ const didReplace = dropResult === "absent" ? target.setContent(`[dropped §${tag.tagNumber}§]`) : false;
34826
+ if (dropResult === "removed" || dropResult === "absent") {
34827
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
34828
+ if (dropResult === "removed" || didReplace) {
34829
+ droppedInjections++;
34830
+ droppedTokenReductions.push({
34831
+ tagNumber: tag.tagNumber,
34832
+ mode: "full"
34833
+ });
34834
+ }
34835
+ }
34836
+ } else {
34837
+ if (freezePiContentDecision(db, sessionId, "reminder-strip", tag.messageId)) {
34838
+ if (target.setContent(stripped)) {
34839
+ droppedInjections++;
34840
+ droppedTokenReductions.push({
34841
+ tagNumber: tag.tagNumber,
34842
+ mode: "partial",
34843
+ removedCharacters: Math.max(0, content.length - stripped.length)
34844
+ });
34845
+ }
34846
+ }
34847
+ }
34848
+ }
34849
+ }).immediate();
34850
+ }
34851
+ const toolFingerprints = routine ? buildPiToolFingerprints(piMessages, resolveStableId) : new Map;
34852
+ if (toolFingerprints.size > 0) {
34853
+ const tagsByCompositeKey = new Map;
34854
+ for (const tag of tags) {
34855
+ if (tag.type === "tool" && tag.status === "active" && tag.messageId) {
34856
+ const key = tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId;
34857
+ tagsByCompositeKey.set(key, tag);
34858
+ }
34859
+ }
34860
+ const fingerprintGroups = new Map;
34861
+ for (const [compositeKey, fingerprint] of toolFingerprints) {
34862
+ const tag = tagsByCompositeKey.get(compositeKey);
34863
+ if (!tag)
34864
+ continue;
34865
+ const group = fingerprintGroups.get(fingerprint) ?? [];
34866
+ group.push(tag);
34867
+ fingerprintGroups.set(fingerprint, group);
34868
+ }
34869
+ db.transaction(() => {
34870
+ for (const [, group] of fingerprintGroups) {
34871
+ if (group.length <= 1)
34872
+ continue;
34873
+ group.sort((a, b) => a.tagNumber - b.tagNumber);
34874
+ for (let i = 0;i < group.length - 1; i++) {
34875
+ const tag = group[i];
34876
+ if (tag.tagNumber > protectedCutoff)
34877
+ continue;
34878
+ const target = targets.get(tag.tagNumber);
34879
+ if (protectedTools.has(tag.tagNumber))
34880
+ continue;
34881
+ if (target?.canDrop?.() === false)
34882
+ continue;
34883
+ const { result, mode } = applyNewToolDrop(target, {
34884
+ inWindow: false
34885
+ });
34886
+ if (result === "incomplete")
34887
+ continue;
34888
+ updateTagDropMode(db, sessionId, tag.tagNumber, mode);
34889
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
34890
+ if (result === "removed" || result === "truncated") {
34891
+ deduplicatedTools++;
34892
+ droppedTokenReductions.push({
34893
+ tagNumber: tag.tagNumber,
34894
+ mode: mode === "full" ? "full" : "truncated"
34895
+ });
34896
+ }
34897
+ }
34898
+ }
34899
+ }).immediate();
34900
+ }
34901
+ if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0 || droppedStaleReduceCalls > 0) {
34902
+ sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale mmc_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
34903
+ }
34904
+ let compressedTextTags = 0;
34905
+ let mutatedTextTags = 0;
34906
+ if (routine && config.caveman?.enabled) {
34907
+ const cavemanTags = getActiveTagsBySession(db, sessionId);
34908
+ const cavemanResult = applyCavemanCleanup(sessionId, db, targets, cavemanTags, {
34909
+ enabled: true,
34910
+ minChars: config.caveman.minChars,
34911
+ wordRules: config.caveman.wordRules,
34912
+ protectedCutoff
34913
+ });
34914
+ compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
34915
+ mutatedTextTags = cavemanResult.mutatedTextTags;
34916
+ if (cavemanResult.textReductions) {
34917
+ for (const r of cavemanResult.textReductions) {
34918
+ droppedTokenReductions.push({
34919
+ tagNumber: r.tagNumber,
34920
+ mode: "partial",
34921
+ removedCharacters: r.removedCharacters
34922
+ });
34923
+ }
34924
+ }
34925
+ }
34926
+ return {
34927
+ droppedTools,
34928
+ deduplicatedTools,
34929
+ droppedInjections,
34930
+ droppedStaleReduceCalls,
34931
+ emergencyDroppedTools,
34932
+ compressedTextTags,
34933
+ mutatedTextTags,
34934
+ droppedTokenReductions
34935
+ };
34936
+ }
34937
+ function buildCtxReduceTagNumbers(tags) {
34938
+ const byComposite = new Map;
34939
+ for (const tag of tags) {
34940
+ if (tag.status !== "active" || tag.type !== "tool" || tag.toolName !== "mmc_reduce" || !tag.toolOwnerMessageId) {
34941
+ continue;
34942
+ }
34943
+ byComposite.set(`${tag.toolOwnerMessageId}\x00${tag.messageId}`, tag.tagNumber);
34944
+ }
34945
+ return byComposite;
34946
+ }
34947
+ function buildMessageIdToMaxTagFromTargets(targets) {
34948
+ const byMessage = new Map;
34949
+ for (const [tagNumber, target] of targets) {
34950
+ const id = target.message?.info?.id;
34951
+ if (typeof id !== "string" || id.length === 0)
34952
+ continue;
34953
+ if (tagNumber > (byMessage.get(id) ?? 0))
34954
+ byMessage.set(id, tagNumber);
34955
+ }
34956
+ return byMessage;
34957
+ }
34958
+
34792
34959
  // src/historian-tail-pi.ts
34793
34960
  function readPiHistorianTail(db, sessionId, provider) {
34794
34961
  const end = getLastCompartmentEndMessage(db, sessionId);
@@ -35367,7 +35534,11 @@ function materializeM0Pi(state, db, passSnapshot) {
35367
35534
  modelKey: snapshotMarkers.modelKey,
35368
35535
  projectIdentity: snapshotMarkers.projectIdentity
35369
35536
  });
35370
- db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds), state.sessionId);
35537
+ const visibleMemoryIds = [
35538
+ ...new Set([...renderedMemoryIds, ...m1Render.renderedMemoryIds])
35539
+ ];
35540
+ const visibleIdsJson = JSON.stringify(visibleMemoryIds);
35541
+ db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(visibleMemoryIds.length, visibleIdsJson, state.sessionId);
35371
35542
  setCachedBoundary(db, state.sessionId, snapshotMarkers.lastBaselineEndMessageId);
35372
35543
  db.exec("COMMIT");
35373
35544
  logSlowWriteTransaction("pi_materialize_cache", transactionStartedAt);
@@ -35505,7 +35676,8 @@ ${body}
35505
35676
  if (sections.length === 0) {
35506
35677
  return {
35507
35678
  text: PI_M1_PLACEHOLDER,
35508
- memoryUpdateCount: memoryUpdates.count
35679
+ memoryUpdateCount: memoryUpdates.count,
35680
+ renderedMemoryIds: []
35509
35681
  };
35510
35682
  }
35511
35683
  return {
@@ -35516,7 +35688,8 @@ ${sections.join(`
35516
35688
  ${sections.join(`
35517
35689
  `)}
35518
35690
  </session-history-since>`,
35519
- memoryUpdateCount: memoryUpdates.count
35691
+ memoryUpdateCount: memoryUpdates.count,
35692
+ renderedMemoryIds: newMemoriesBlock ? deltaMemories.map((memory) => memory.id) : []
35520
35693
  };
35521
35694
  }
35522
35695
  function toCachedBuffer(value) {
@@ -35684,9 +35857,13 @@ function softRefreshCachedM1Pi(args) {
35684
35857
  const renderedM0Ids = readCachedM0MemoryIds(row.cached_m0_upgrade_state, parseMemoryBlockIds(row.memory_block_ids), markers.maxMemoryId);
35685
35858
  const rendered = renderM1PiWithMetadata(args.state, args.db, markers, renderedM0Ids, args.compartmentsForNormalization);
35686
35859
  const m1Bytes = Buffer.from(rendered.text, "utf8");
35860
+ const visibleMemoryIds = [
35861
+ ...new Set([...renderedM0Ids, ...rendered.renderedMemoryIds])
35862
+ ];
35863
+ const visibleIdsJson = JSON.stringify(visibleMemoryIds);
35687
35864
  const latestCompartment = args.compartmentsForNormalization.at(-1);
35688
35865
  const advancedBoundary = latestCompartment?.endMessageId && latestCompartment.endMessageId.length > 0 ? latestCompartment.endMessageId : markers.lastBaselineEndMessageId;
35689
- args.db.prepare("UPDATE session_meta SET cached_m1_bytes = ?, cached_m0_last_baseline_end_message_id = ?, cached_m0_upgrade_state = ? WHERE session_id = ?").run(m1Bytes, advancedBoundary, withCachedM0MemoryIds(row.cached_m0_upgrade_state, renderedM0Ids), args.state.sessionId);
35866
+ args.db.prepare("UPDATE session_meta SET cached_m1_bytes = ?, cached_m0_last_baseline_end_message_id = ?, memory_block_count = ?, memory_block_ids = ?, cached_m0_upgrade_state = ? WHERE session_id = ?").run(m1Bytes, advancedBoundary, visibleMemoryIds.length, visibleIdsJson, withCachedM0MemoryIds(row.cached_m0_upgrade_state, renderedM0Ids), args.state.sessionId);
35690
35867
  args.db.exec("COMMIT");
35691
35868
  logSlowWriteTransaction("pi_soft_refresh_cache", transactionStartedAt);
35692
35869
  return {
@@ -37955,6 +38132,7 @@ async function runPhase(deps, ctx, phase, stageAManifest) {
37955
38132
  messages: []
37956
38133
  };
37957
38134
  let refusalNotes = [];
38135
+ let consumedSteps = 0;
37958
38136
  for (let round = 1;round <= maxRounds; round += 1) {
37959
38137
  const decision = ctx.shouldContinue();
37960
38138
  if (decision === "stop")
@@ -37974,10 +38152,12 @@ async function runPhase(deps, ctx, phase, stageAManifest) {
37974
38152
  sessionLog(deps.parentSessionId ?? "pi-maintenance", `maintenance phase ${phase} round ${round}: producer admission refused`);
37975
38153
  break;
37976
38154
  }
38155
+ const stepCap = phase === "a" ? MEMORY_MAINTAINER_A_STEP_CAP : resolveMaintenanceStepCap(deps.options.runConfig.budgets.toolSteps, consumedSteps);
37977
38156
  const result = await session({
37978
38157
  agent: phase === "a" ? MAINTAINER_AGENT_A : MAINTAINER_AGENT_B,
37979
38158
  systemPrompt: maintainerSystemPrompt(deps, phase),
37980
- userMessage: prompt
38159
+ userMessage: prompt,
38160
+ stepCap
37981
38161
  });
37982
38162
  ctx.recordUsage(result.usage);
37983
38163
  acc.usage.inputTokens += result.usage.inputTokens;
@@ -37986,6 +38166,7 @@ async function runPhase(deps, ctx, phase, stageAManifest) {
37986
38166
  for (let step = 0;step < result.toolSteps; step += 1) {
37987
38167
  if (ctx.shouldContinue() === "continue") {
37988
38168
  ctx.recordToolStep();
38169
+ consumedSteps += 1;
37989
38170
  } else {
37990
38171
  const refusal = ctx.refuseToolStep();
37991
38172
  refusalNotes.push(refusal.message);
@@ -38058,6 +38239,7 @@ async function defaultSessionFace(deps, args) {
38058
38239
  agent: args.agent,
38059
38240
  systemPrompt: args.systemPrompt,
38060
38241
  userMessage: args.userMessage,
38242
+ stepCap: args.stepCap,
38061
38243
  model: deps.options.model,
38062
38244
  fallbackModels: deps.options.fallbackModels,
38063
38245
  timeoutMs: deps.options.timeoutMs ?? DEFAULT_MAINTAINER_TIMEOUT_MS,
@@ -40460,10 +40642,16 @@ function handlePiProviderFailure(args) {
40460
40642
  const provider = typeof message.provider === "string" ? message.provider : undefined;
40461
40643
  const model = typeof message.model === "string" ? message.model : undefined;
40462
40644
  const binding = detectThinkingBindingMismatch(message.errorMessage);
40645
+ if (detectLatestTurnThinkingMismatch(message.errorMessage) && !args.compactionOff && isAnthropicFamilyRoute(provider, model)) {
40646
+ armLatestThinkingRecovery(args.db, args.sessionId);
40647
+ clearPiLkgSessionState(args.sessionId);
40648
+ dropSlot(args.sessionId, "latest-thinking-recovery-arm");
40649
+ return { kind: "thinking_binding", armed: true };
40650
+ }
40463
40651
  if (binding.isBindingMismatch) {
40464
40652
  const enabled = args.thinkingBindingRecoveryEnabled !== false && !args.compactionOff && isPrefixBoundThinkingModel(provider, model);
40465
40653
  if (enabled) {
40466
- armThinkingBindingRecovery(args.db, args.sessionId);
40654
+ armBindingRecoverySafely(args.db, args.sessionId);
40467
40655
  clearPiLkgSessionState(args.sessionId);
40468
40656
  dropSlot(args.sessionId, "thinking-binding-recovery-arm");
40469
40657
  reportBindingRecovery(args.sessionId, args.report, `thinking-binding recovery armed from message_end (provider paths: failing=${binding.failingBlockPath ?? "?"} firstChanged=${binding.firstChangedPath ?? "?"})`);
@@ -40513,12 +40701,16 @@ function stripThinkingParts(message) {
40513
40701
  return before - strippedContent.length;
40514
40702
  }
40515
40703
  function applyPiThinkingBindingRecovery(args) {
40704
+ const protectedMessages = latestAssistantTurnMessages(args.messages);
40516
40705
  const frozenEntryIds = frozenBindingEntryIds(args.db, args.sessionId);
40517
- const flagTarget = isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
40706
+ const flagTargetCandidate = isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
40707
+ const flagTarget = flagTargetCandidate?.startsWith(LATEST_THINKING_RESTORE) ? null : flagTargetCandidate;
40518
40708
  let applied = null;
40519
40709
  if (flagTarget) {
40520
40710
  const entryIds = new Set;
40521
40711
  for (let index = 0;index < args.messages.length; index += 1) {
40712
+ if (protectedMessages.has(args.messages[index]))
40713
+ continue;
40522
40714
  const entryId = args.entryIds[index];
40523
40715
  if (entryId && hasThinkingPart(args.messages[index]))
40524
40716
  entryIds.add(entryId);
@@ -40534,7 +40726,15 @@ function applyPiThinkingBindingRecovery(args) {
40534
40726
  reportBindingRecovery(args.sessionId, args.report, `thinking-binding recovery consumed on context pass target=${flagTarget} entries=${applied.entryIds.length} [${applied.entryIds.join(",")}]`);
40535
40727
  }
40536
40728
  }
40729
+ const restoringActiveTurn = latestThinkingRecoveryCoversActiveTurn({
40730
+ db: args.db,
40731
+ sessionId: args.sessionId,
40732
+ messages: args.messages,
40733
+ id: (message, index) => resolvePiStableId(message, index, args.entryIds)
40734
+ });
40537
40735
  for (let index = 0;index < args.messages.length; index += 1) {
40736
+ if (restoringActiveTurn && protectedMessages.has(args.messages[index]))
40737
+ continue;
40538
40738
  const entryId = args.entryIds[index];
40539
40739
  if (entryId && frozenEntryIds.has(entryId))
40540
40740
  stripThinkingParts(args.messages[index]);
@@ -40546,9 +40746,12 @@ function applyPiProactiveThinkingStrip(args) {
40546
40746
  return null;
40547
40747
  if (!isPrefixBoundThinkingModel(args.provider, args.model))
40548
40748
  return null;
40749
+ const protectedMessages = latestAssistantTurnMessages(args.messages);
40549
40750
  const entryIds = [];
40550
40751
  const indices = [];
40551
40752
  for (let index = 0;index < args.messages.length; index += 1) {
40753
+ if (protectedMessages.has(args.messages[index]))
40754
+ continue;
40552
40755
  const entryId = args.entryIds[index];
40553
40756
  if (!entryId || !hasThinkingPart(args.messages[index]))
40554
40757
  continue;
@@ -44274,8 +44477,10 @@ function registerPiContextHandler(pi, baseOptions) {
44274
44477
  messages: outputMessages,
44275
44478
  tags,
44276
44479
  protectedTagNumbers,
44480
+ protectedTools: options.protectedTools,
44277
44481
  pendingDropTagNumbers,
44278
44482
  stableId,
44483
+ protectedToolTags: protectedToolTagNumbers(tags, options.protectedTools),
44279
44484
  syntheticLeadingCount: result.syntheticLeadingCount,
44280
44485
  cacheBusting: result.bustedThisPass,
44281
44486
  previous: previousBaseline,
@@ -45187,6 +45392,16 @@ async function runPipeline(args) {
45187
45392
  args.assertCurrentPass?.();
45188
45393
  if (args.compactionOff)
45189
45394
  return runCompactionOffPipeline(args);
45395
+ const thinkingRecovery = prepareLatestThinkingRecovery({
45396
+ db: args.db,
45397
+ sessionId: args.sessionId,
45398
+ messages: args.messages,
45399
+ id: (message, index) => resolvePiStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined),
45400
+ parts: (message) => Array.isArray(message?.content) ? message.content : []
45401
+ });
45402
+ if (thinkingRecovery.ended)
45403
+ signalPiPendingMaterialization(args.sessionId);
45404
+ let restoreOriginals;
45190
45405
  const activeThinkingTurn = hasActiveAnthropicThinkingTurn(args.messages, args.resolvedProviderID, args.resolvedModelID);
45191
45406
  const protectedSignedPrefix = activeThinkingTurn && args.reasoningClearing?.prefixBound === true;
45192
45407
  let foldingSystemState = null;
@@ -45445,6 +45660,10 @@ async function runPipeline(args) {
45445
45660
  afterFallbackAdoptionForTests?.(args.stableIdSchemeCutover === true);
45446
45661
  const textIdentityPlan = buildPiTextIdentityPlan(args.db, args.sessionId, args.tagger, transcript, args.reusableMessageIds);
45447
45662
  const tTag = performance.now();
45663
+ const mutationView = transcript.messages.map((message) => ({
45664
+ ...message,
45665
+ parts: message.parts
45666
+ }));
45448
45667
  let tagTextTokenCache = piTagTextTokenCacheBySession.get(args.sessionId);
45449
45668
  if (!tagTextTokenCache) {
45450
45669
  tagTextTokenCache = new Map;
@@ -45457,7 +45676,7 @@ async function runPipeline(args) {
45457
45676
  }
45458
45677
  const { targets } = runPersistedReplayStage("tagging-persistence-failure", () => args.tagger.cleanup(args.sessionId), () => {
45459
45678
  args.assertCurrentPass?.();
45460
- return tagTranscript(args.sessionId, transcript, args.tagger, args.db, {
45679
+ return tagTranscript(args.sessionId, { ...transcript, messages: mutationView }, args.tagger, args.db, {
45461
45680
  skipPrefixInjection: !ctxReduceCallable,
45462
45681
  entryFingerprintByMessageId,
45463
45682
  reuseMessageIds: textIdentityPlan.reusableMessageIds,
@@ -45475,6 +45694,35 @@ async function runPipeline(args) {
45475
45694
  });
45476
45695
  });
45477
45696
  logTransformTiming(args.sessionId, "tagMessages", tTag);
45697
+ const reasoningTagsForProtection = buildMessageIdToMaxTag(targets);
45698
+ const frozenThinkingForProtection = activeThinkingTurn ? frozenBindingEntryIds(args.db, args.sessionId) : new Set;
45699
+ const protectedThinkingProxies = new Set;
45700
+ if (activeThinkingTurn) {
45701
+ for (let i = latestAssistantTurnStart(workingMessages);i < mutationView.length; i++) {
45702
+ const id = stableIdResolver(workingMessages[i], i);
45703
+ if (!thinkingRecovery.restore && id && frozenThinkingForProtection.has(id))
45704
+ continue;
45705
+ for (let p = 0;p < mutationView[i].parts.length; p++) {
45706
+ const part = mutationView[i].parts[p];
45707
+ if (part.kind !== "thinking")
45708
+ continue;
45709
+ const local = workingMessages[i].content?.[p];
45710
+ if (!thinkingRecovery.restore && !local?.redacted && id && (reasoningTagsForProtection.get(id) ?? Infinity) <= (args.sessionMeta.clearedReasoningThroughTag ?? 0))
45711
+ continue;
45712
+ protectedThinkingProxies.add(part);
45713
+ }
45714
+ }
45715
+ }
45716
+ const newTargets = protectNewTagMutations(mutationView.map((message) => ({
45717
+ info: message.info,
45718
+ parts: message.parts
45719
+ })), targets, protectedThinkingProxies, args.reasoningClearing?.prefixBound === true);
45720
+ if (thinkingRecovery.restore) {
45721
+ transcript.commit();
45722
+ restoreOriginals = captureOriginalTurn(args.messages, (message) => message.content, (message, content) => {
45723
+ message.content = content;
45724
+ });
45725
+ }
45478
45726
  if (foldBustsServedPrefixThisPass) {
45479
45727
  try {
45480
45728
  let converted = 0;
@@ -45517,7 +45765,7 @@ async function runPipeline(args) {
45517
45765
  pendingDecisionLogObserverForTests?.(pendingDecisionLog);
45518
45766
  try {
45519
45767
  const tApplyPending = performance.now();
45520
- pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, new Set([
45768
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, new Set([
45521
45769
  ...args.contextUsage.percentage >= 95 ? [] : protectedTagNumbersForPass,
45522
45770
  ...protectedToolTagNumbers(getActiveTagsBySession(args.db, args.sessionId), args.protectedTools)
45523
45771
  ]), pendingOperationTags, pendingOps, [], new Set, (reduction) => droppedTokenReductions.push(reduction));
@@ -45530,7 +45778,8 @@ async function runPipeline(args) {
45530
45778
  pendingOpsAppliedThisPass = true;
45531
45779
  if (hasPendingMaterializeSignal) {
45532
45780
  if (args.heuristics === undefined) {
45533
- consumePendingMaterialization(args.sessionId);
45781
+ if (!pendingOps.some((op) => newTargets.get(op.tagId)?.thinkingDropProtected))
45782
+ consumePendingMaterialization(args.sessionId);
45534
45783
  }
45535
45784
  }
45536
45785
  } catch (err) {
@@ -45618,7 +45867,7 @@ async function runPipeline(args) {
45618
45867
  clearEmergencyDropSample(args.db, args.sessionId);
45619
45868
  }
45620
45869
  routineCleanupApplied = (emergencyDropEligible ? args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0 : args.sessionMeta.isSubagent || !routinePressureAlreadyApplied) || hasPendingMaterializeSignal || deferredMaterialize || independentMutationBeforeHeuristics;
45621
- heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
45870
+ heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
45622
45871
  protectedTools: args.protectedTools,
45623
45872
  protectedToolTags,
45624
45873
  protectedTags: args.protectedTags,
@@ -45635,7 +45884,7 @@ async function runPipeline(args) {
45635
45884
  caveman: args.isSubagent ? undefined : args.heuristics.caveman
45636
45885
  }, activeTags, stableIdResolver);
45637
45886
  if (!routineCleanupApplied && heuristicsResult.emergencyDroppedTools > 0) {
45638
- const ridingCleanup = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
45887
+ const ridingCleanup = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
45639
45888
  protectedTools: args.protectedTools,
45640
45889
  protectedToolTags,
45641
45890
  protectedTags: args.protectedTags,
@@ -45675,7 +45924,8 @@ async function runPipeline(args) {
45675
45924
  heuristicsExecuted = true;
45676
45925
  executedWorkThisPass = true;
45677
45926
  if (hasPendingMaterializeSignal) {
45678
- consumePendingMaterialization(args.sessionId);
45927
+ if (!pendingOps.some((op) => newTargets.get(op.tagId)?.thinkingDropProtected))
45928
+ consumePendingMaterialization(args.sessionId);
45679
45929
  }
45680
45930
  if (currentTurnId !== null) {
45681
45931
  lastHeuristicsTurnIdBySession.set(args.sessionId, currentTurnId);
@@ -45776,7 +46026,7 @@ async function runPipeline(args) {
45776
46026
  const syntheticPendingOps = buildSyntheticToolReclaimOps({
45777
46027
  db: args.db,
45778
46028
  sessionId: args.sessionId,
45779
- targets,
46029
+ targets: newTargets,
45780
46030
  watermark: reclaimMeta.toolReclaimWatermark ?? 0,
45781
46031
  pendingOps
45782
46032
  });
@@ -45787,7 +46037,7 @@ async function runPipeline(args) {
45787
46037
  const supersessionOps = buildSupersessionReclaimOps({
45788
46038
  db: args.db,
45789
46039
  sessionId: args.sessionId,
45790
- targets,
46040
+ targets: newTargets,
45791
46041
  pendingOps,
45792
46042
  recentMessageIds,
45793
46043
  protectedToolTags
@@ -45801,7 +46051,7 @@ async function runPipeline(args) {
45801
46051
  const editReclaim = buildEditSupersessionReclaim({
45802
46052
  db: args.db,
45803
46053
  sessionId: args.sessionId,
45804
- targets,
46054
+ targets: newTargets,
45805
46055
  pendingOps,
45806
46056
  recentMessageIds,
45807
46057
  protectedToolTags
@@ -45816,7 +46066,7 @@ async function runPipeline(args) {
45816
46066
  }
45817
46067
  autoReclaimTargetCount = syntheticPendingOps.length;
45818
46068
  if (syntheticPendingOps.length > 0) {
45819
- autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, protectedTagNumbersForPass, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
46069
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, protectedTagNumbersForPass, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
45820
46070
  if (autoReclaimDidMutate) {
45821
46071
  droppedCount += syntheticPendingOps.length;
45822
46072
  autoReclaimDidMutateThisPass = true;
@@ -46078,6 +46328,7 @@ async function runPipeline(args) {
46078
46328
  } catch (err) {
46079
46329
  sessionLog(args.sessionId, `token accounting failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
46080
46330
  }
46331
+ restoreOriginals?.(workingMessages);
46081
46332
  const materialized = injectionResult?.m0Materialized === true;
46082
46333
  if (args.reasoningClearing?.prefixBound && firstEditSourceOrder && firstEditParts.size > 0 && !prefixEditBesidesReasoningTrim) {
46083
46334
  const frozenThinking = frozenBindingEntryIds(args.db, args.sessionId);
@@ -46704,17 +46955,8 @@ ${result.message}`,
46704
46955
  var rebasesInFlight = new WeakMap;
46705
46956
  var repairCheckedSessions = new WeakMap;
46706
46957
 
46707
- // ../plugin/src/hooks/magic-context/historian-state-file.ts
46708
- import { mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
46709
- function cleanupHistorianStateFile(path) {
46710
- if (!path)
46711
- return;
46712
- try {
46713
- unlinkSync2(path);
46714
- } catch {}
46715
- }
46716
46958
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
46717
- import { mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
46959
+ import { mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
46718
46960
  import { join as join11 } from "node:path";
46719
46961
 
46720
46962
  // ../plugin/src/agents/historian.ts
@@ -47467,7 +47709,7 @@ function cleanupHistorianDump(sessionId, dumpPath) {
47467
47709
  if (!dumpPath)
47468
47710
  return;
47469
47711
  try {
47470
- unlinkSync3(dumpPath);
47712
+ unlinkSync2(dumpPath);
47471
47713
  } catch (error) {
47472
47714
  sessionLog(sessionId, "compartment agent: failed to remove historian response dump", {
47473
47715
  dumpPath,
@@ -47478,12 +47720,12 @@ function cleanupHistorianDump(sessionId, dumpPath) {
47478
47720
  function dumpHistorianResponse(sessionId, directory, label, text) {
47479
47721
  try {
47480
47722
  const dumpDir = historianResponseDumpDir(directory);
47481
- mkdirSync3(dumpDir, { recursive: true });
47723
+ mkdirSync2(dumpDir, { recursive: true });
47482
47724
  ensureMmcArtifactGitignore(directory);
47483
47725
  const safeSessionId = sanitizeDumpName(sessionId);
47484
47726
  const safeLabel = sanitizeDumpName(label);
47485
47727
  const dumpPath = join11(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
47486
- writeFileSync3(dumpPath, text, "utf8");
47728
+ writeFileSync2(dumpPath, text, "utf8");
47487
47729
  sessionLog(sessionId, "compartment agent: historian response dumped", {
47488
47730
  label,
47489
47731
  dumpPath
@@ -47634,7 +47876,6 @@ Could not acquire the compartment-state lease for this session.`;
47634
47876
  logPostPublishFailure(step, error);
47635
47877
  }
47636
47878
  };
47637
- let currentStateFilePath;
47638
47879
  updateSessionMeta(db, sessionId, { compartmentInProgress: true });
47639
47880
  try {
47640
47881
  const rawMessageCount = getRawSessionMessageCount2(sessionId);
@@ -47971,7 +48212,6 @@ Another process acquired the compartment-state lease before recomp could publish
47971
48212
  ${renderUserFacingFailure("recomp_unavailable")}`;
47972
48213
  } finally {
47973
48214
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
47974
- cleanupHistorianStateFile(currentStateFilePath);
47975
48215
  }
47976
48216
  }
47977
48217
 
@@ -49876,7 +50116,7 @@ function buildStatusViewFor(check, options) {
49876
50116
  // package.json
49877
50117
  var package_default = {
49878
50118
  name: "@retaw106/mmc-pi",
49879
- version: "2.1.2",
50119
+ version: "2.2.1",
49880
50120
  type: "module",
49881
50121
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
49882
50122
  main: "dist/index.js",
@@ -49907,7 +50147,7 @@ var package_default = {
49907
50147
  "dist",
49908
50148
  "README.md"
49909
50149
  ],
49910
- "//": "The build intentionally bundles typebox (no --external typebox) and keeps it a regular dependency. OMP's extension loader rewrites every bare `typebox` import to its own omptype-based shim, whose schemas are callable objects rather than JSON Schema; they fail structuredClone during tool registration, so an external typebox import stops Magic Context loading on OMP. src/typebox-bundling.test.ts guards this.",
50150
+ "//": 'The build intentionally bundles typebox (no --external typebox): OMP\'s extension loader rewrites every bare `typebox` import to its own omptype-based shim, whose schemas are callable objects rather than JSON Schema; they fail structuredClone during tool registration, so an external typebox import stops Magic Context loading on OMP. The package nonetheless declares typebox as an optional peer "*": Pi treats it as a host-provided extension package and warns on startup when it appears in dependencies (upstream issue #571). The bundled copy is the only one loaded at runtime. src/typebox-bundling.test.ts guards both facts.',
49911
50151
  scripts: {
49912
50152
  build: "bun ../../scripts/check-bun.mjs && bun ../../scripts/clean-dist-chunks.mjs dist index.js historian-calibration-extension.js config-init.js cli.js && rm -rf dist/web && bun build src/index.ts src/historian-calibration-extension.ts --outdir dist --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external node:sqlite && bun run build:web && bun run build:config-init && bun run build:cli",
49913
50153
  "build:web": "bun build src/web/main.ts --outfile dist/web/main.js --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external node:sqlite && mkdir -p dist/web/ui && cp ../plugin/src/web/ui/* dist/web/ui/",
@@ -49927,7 +50167,6 @@ var package_default = {
49927
50167
  dependencies: {
49928
50168
  "ai-tokenizer": "^1.0.6",
49929
50169
  "comment-json": "^5.0.0",
49930
- typebox: "^1.3.1",
49931
50170
  zod: "^4.1.8"
49932
50171
  },
49933
50172
  devDependencies: {
@@ -49941,11 +50180,13 @@ var package_default = {
49941
50180
  "pi-ai-086": "npm:@earendil-works/pi-ai@0.86.0",
49942
50181
  "pi-coding-agent-086": "npm:@earendil-works/pi-coding-agent@0.86.0",
49943
50182
  "pi-coding-agent-087": "npm:@earendil-works/pi-coding-agent@0.87.1",
50183
+ typebox: "^1.3.1",
49944
50184
  typescript: "^5.8.0"
49945
50185
  },
49946
50186
  peerDependencies: {
49947
50187
  "@earendil-works/pi-coding-agent": ">=0.80.2",
49948
- "@earendil-works/pi-tui": ">=0.80.2"
50188
+ "@earendil-works/pi-tui": ">=0.80.2",
50189
+ typebox: "*"
49949
50190
  },
49950
50191
  peerDependenciesMeta: {
49951
50192
  "@earendil-works/pi-coding-agent": {
@@ -49953,6 +50194,9 @@ var package_default = {
49953
50194
  },
49954
50195
  "@earendil-works/pi-tui": {
49955
50196
  optional: true
50197
+ },
50198
+ typebox: {
50199
+ optional: true
49956
50200
  }
49957
50201
  },
49958
50202
  exports: {
@@ -49969,6 +50213,7 @@ var package_default = {
49969
50213
 
49970
50214
  // src/dialogs/status-dialog.ts
49971
50215
  var REFRESH_INTERVAL_MS = 1000;
50216
+ var DETAIL_REFRESH_INTERVAL_MS = 1e4;
49972
50217
  var BORDER_ROWS = 2;
49973
50218
  var BORDER_COLUMNS = 2;
49974
50219
  var PADDING_COLUMNS = 2;
@@ -49998,6 +50243,7 @@ function stopStatusDialogRefresh() {
49998
50243
  class StatusDialogComponent {
49999
50244
  props;
50000
50245
  detail;
50246
+ detailRefresh;
50001
50247
  refreshTimer = null;
50002
50248
  closed = false;
50003
50249
  scrollOffset = 0;
@@ -50005,13 +50251,14 @@ class StatusDialogComponent {
50005
50251
  maxScrollOffset = 0;
50006
50252
  constructor(props) {
50007
50253
  this.props = props;
50008
- this.detail = buildPiStatusDetail(props.pi, props.ctx, props.deps, props.sessionId);
50254
+ this.detailRefresh = new PiStatusDetailRefresh(props.pi, props.ctx, props.deps, props.sessionId);
50255
+ this.detail = this.detailRefresh.refresh();
50009
50256
  openStatusDialogs.add(this);
50010
50257
  this.refreshTimer = setInterval(() => {
50011
50258
  if (this.closed)
50012
50259
  return;
50013
50260
  try {
50014
- this.detail = buildPiStatusDetail(this.props.pi, this.props.ctx, this.props.deps, this.props.sessionId);
50261
+ this.detail = this.detailRefresh.refresh();
50015
50262
  this.props.tui.requestRender();
50016
50263
  } catch {}
50017
50264
  }, REFRESH_INTERVAL_MS);
@@ -50248,7 +50495,152 @@ function deriveDefaultProtectedTokensFloor(usableSoft) {
50248
50495
  const val = Math.round(0.05 * soft);
50249
50496
  return Math.max(low, Math.min(64000, val));
50250
50497
  }
50498
+ function readStatusHostBytes(pi, ctx) {
50499
+ return {
50500
+ modelKey: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined,
50501
+ modelBytes: safeStringify(ctx.model),
50502
+ system: safeRead(() => readHostSystemPrompt(ctx), undefined),
50503
+ tools: safeRead(() => (pi.getAllTools?.() ?? []).map((tool) => `${tool.name ?? ""}
50504
+ ${tool.description ?? ""}
50505
+ ${safeStringify(tool.parameters)}`), [])
50506
+ };
50507
+ }
50508
+
50509
+ class PiStatusTokenCache {
50510
+ estimate;
50511
+ systemKey;
50512
+ systemTokens = 0;
50513
+ toolsKey;
50514
+ toolTokens = 0;
50515
+ constructor(estimate = estimateTokens) {
50516
+ this.estimate = estimate;
50517
+ }
50518
+ estimates(host, fallbackSystemTokens) {
50519
+ if (host.system !== undefined) {
50520
+ const key = JSON.stringify([host.modelKey, host.system]);
50521
+ if (this.systemKey !== key) {
50522
+ this.systemTokens = this.estimate(host.system);
50523
+ this.systemKey = key;
50524
+ }
50525
+ }
50526
+ const key = JSON.stringify([host.modelKey, host.tools]);
50527
+ if (this.toolsKey !== key) {
50528
+ this.toolTokens = host.tools.reduce((sum, bytes) => sum + this.estimate(bytes), 0);
50529
+ this.toolsKey = key;
50530
+ }
50531
+ return {
50532
+ systemPromptTokens: host.system === undefined ? fallbackSystemTokens : this.systemTokens,
50533
+ toolDefinitionTokens: this.toolTokens
50534
+ };
50535
+ }
50536
+ }
50537
+
50538
+ class PiStatusDetailRefresh {
50539
+ pi;
50540
+ ctx;
50541
+ deps;
50542
+ initialSessionId;
50543
+ detail;
50544
+ state;
50545
+ lastBuildAt = 0;
50546
+ detailKey = "";
50547
+ storeRevision;
50548
+ revisionDb;
50549
+ revisionStatement;
50550
+ constructor(pi, ctx, deps, initialSessionId, tokens = new PiStatusTokenCache) {
50551
+ this.pi = pi;
50552
+ this.ctx = ctx;
50553
+ this.deps = deps;
50554
+ this.initialSessionId = initialSessionId;
50555
+ this.state = { tokens };
50556
+ }
50557
+ readStoreRevision() {
50558
+ if (!isKnownAutocommit(this.deps.db))
50559
+ return;
50560
+ try {
50561
+ if (this.revisionDb !== this.deps.db) {
50562
+ this.revisionStatement = this.deps.db.prepare("SELECT total_changes() AS changes, data_version AS version FROM pragma_data_version");
50563
+ this.revisionDb = this.deps.db;
50564
+ }
50565
+ const row = this.revisionStatement?.get();
50566
+ return row ? `${row.changes}/${row.version}` : undefined;
50567
+ } catch {
50568
+ return;
50569
+ }
50570
+ }
50571
+ refresh(now = Date.now()) {
50572
+ const sessionId = resolveSessionId(this.ctx) ?? this.initialSessionId;
50573
+ const host = readStatusHostBytes(this.pi, this.ctx);
50574
+ const usage = this.ctx.getContextUsage?.();
50575
+ const { db: _db, ...config } = this.deps;
50576
+ const key = JSON.stringify([sessionId, host, usage?.contextWindow, config]);
50577
+ const databaseChanged = this.revisionDb !== this.deps.db;
50578
+ const revision = this.readStoreRevision();
50579
+ if (!this.detail || key !== this.detailKey || revision === undefined || revision !== this.storeRevision || databaseChanged || now - this.lastBuildAt >= DETAIL_REFRESH_INTERVAL_MS) {
50580
+ this.detail = buildStatusDetail(this.pi, this.ctx, this.deps, sessionId, this.state, host);
50581
+ this.detailKey = key;
50582
+ this.lastBuildAt = now;
50583
+ this.storeRevision = this.readStoreRevision();
50584
+ }
50585
+ this.detail = refreshLiveStatus(this.detail, this.state, this.ctx, usage, now);
50586
+ return this.detail;
50587
+ }
50588
+ }
50589
+ function conversationTokensFor(detail) {
50590
+ return Math.max(0, detail.inputTokens - detail.systemPromptTokens - detail.compartmentTokens - detail.factTokens - detail.memoryTokens - detail.profileTokens - detail.toolCallTokens - detail.toolDefinitionTokens);
50591
+ }
50592
+ function refreshLiveStatus(detail, state, ctx, usage, now) {
50593
+ const live = state.live;
50594
+ if (!live)
50595
+ return detail;
50596
+ const windowGeometry = resolvePiWindowGeometry({
50597
+ rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
50598
+ rawContextWindowSource: "catalog",
50599
+ model: ctx.model,
50600
+ detectedContextLimit: live.detectedContextLimit,
50601
+ provenInputTokens: live.provenInputTokens,
50602
+ persistedInputTokens: live.persistedInputTokens,
50603
+ persistedPercentage: live.persistedPercentage
50604
+ });
50605
+ const pressure = resolvePiStatusPressureSnapshot({
50606
+ providerInputLimit: live.providerInputLimit,
50607
+ sessionId: detail.sessionId,
50608
+ persistedPercentage: live.persistedPercentage,
50609
+ persistedInputTokens: live.persistedInputTokens,
50610
+ liveInputTokens: usage?.tokens,
50611
+ usableContextLimit: windowGeometry?.usableSoft
50612
+ });
50613
+ const cacheRemainingMs = cacheRemaining(detail.cacheTtl, detail.lastResponseTime, now);
50614
+ const current = {
50615
+ ...detail,
50616
+ inputTokens: pressure.inputTokens,
50617
+ usagePercentage: pressure.percentage,
50618
+ contextLimit: pressure.contextLimit ?? 0,
50619
+ windowGeometry,
50620
+ cacheRemainingMs,
50621
+ cacheExpired: detail.lastResponseTime > 0 && cacheRemainingMs === 0,
50622
+ tailHygiene: resolveTailHygieneStatus(getPiChannel1Baseline(detail.sessionId)),
50623
+ recompInFlight: isPiRecompInFlight(detail.sessionId)
50624
+ };
50625
+ return { ...current, conversationTokens: conversationTokensFor(current) };
50626
+ }
50627
+ function cacheRemaining(cacheTtl, lastResponseTime, now) {
50628
+ let ttlMs;
50629
+ try {
50630
+ ttlMs = parseCacheTtl(cacheTtl);
50631
+ } catch {
50632
+ ttlMs = 5 * 60 * 1000;
50633
+ }
50634
+ if (ttlMs === Number.POSITIVE_INFINITY)
50635
+ return ttlMs;
50636
+ return lastResponseTime > 0 ? Math.max(0, ttlMs - (now - lastResponseTime)) : ttlMs;
50637
+ }
50251
50638
  function buildPiStatusDetail(pi, ctx, deps, sessionId) {
50639
+ return buildStatusDetail(pi, ctx, deps, sessionId, {
50640
+ tokens: new PiStatusTokenCache
50641
+ });
50642
+ }
50643
+ function buildStatusDetail(pi, ctx, deps, sessionId, state, host = readStatusHostBytes(pi, ctx)) {
50252
50644
  const usage = ctx.getContextUsage?.();
50253
50645
  const meta = getOrCreateSessionMeta(deps.db, sessionId);
50254
50646
  let detectedContextLimit;
@@ -50270,6 +50662,13 @@ function buildPiStatusDetail(pi, ctx, deps, sessionId) {
50270
50662
  readBranch: () => ctx.sessionManager.getBranch(),
50271
50663
  providerInputLimit
50272
50664
  });
50665
+ state.live = {
50666
+ persistedInputTokens: meta.lastInputTokens,
50667
+ persistedPercentage: meta.lastContextPercentage,
50668
+ detectedContextLimit,
50669
+ provenInputTokens: provenInputTokens || undefined,
50670
+ providerInputLimit
50671
+ };
50273
50672
  const windowGeometry = resolvePiWindowGeometry({
50274
50673
  rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
50275
50674
  rawContextWindowSource: "catalog",
@@ -50305,29 +50704,23 @@ function buildPiStatusDetail(pi, ctx, deps, sessionId) {
50305
50704
  const factTokens = m0Blocks.factTokens;
50306
50705
  const memoryTokens = m0Blocks.memoryTokens;
50307
50706
  const profileTokens = m0Blocks.profileTokens;
50308
- let systemPromptTokens = meta.systemPromptTokens;
50309
- try {
50310
- const sysPrompt = readHostSystemPrompt(ctx);
50311
- if (sysPrompt !== undefined && sysPrompt.length > 0) {
50312
- systemPromptTokens = estimateTokens(sysPrompt);
50313
- }
50314
- } catch {}
50707
+ const { systemPromptTokens, toolDefinitionTokens } = state.tokens.estimates(host, meta.systemPromptTokens);
50315
50708
  const tags = getTagsBySession(deps.db, sessionId);
50316
50709
  const activeTags = tags.filter((tag) => tag.status === "active");
50317
50710
  const droppedTags = tags.filter((tag) => tag.status === "dropped");
50318
50711
  const activeBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize, 0);
50319
50712
  const pendingOps = readPendingOpsCount(deps.db, sessionId);
50320
50713
  const toolCallTokens = meta.toolCallTokens;
50321
- let toolDefinitionTokens = 0;
50322
- try {
50323
- const tools = pi.getAllTools?.() ?? [];
50324
- for (const tool of tools) {
50325
- toolDefinitionTokens += estimateTokens(`${tool.name ?? ""}
50326
- ${tool.description ?? ""}
50327
- ${safeStringify(tool.parameters)}`);
50328
- }
50329
- } catch {}
50330
- const conversationTokens = Math.max(0, inputTokens - systemPromptTokens - compartmentTokens - factTokens - memoryTokens - profileTokens - toolCallTokens - toolDefinitionTokens);
50714
+ const conversationTokens = conversationTokensFor({
50715
+ inputTokens,
50716
+ systemPromptTokens,
50717
+ compartmentTokens,
50718
+ factTokens,
50719
+ memoryTokens,
50720
+ profileTokens,
50721
+ toolCallTokens,
50722
+ toolDefinitionTokens
50723
+ });
50331
50724
  const workMetrics = getSessionWorkMetrics(deps.db, sessionId);
50332
50725
  const tailHygiene = resolveTailHygieneStatus(getPiChannel1Baseline(sessionId));
50333
50726
  const modelKey = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
@@ -51935,7 +52328,7 @@ import {
51935
52328
  realpathSync as realpathSync4,
51936
52329
  rmSync as rmSync2,
51937
52330
  statSync as statSync8,
51938
- writeFileSync as writeFileSync4
52331
+ writeFileSync as writeFileSync3
51939
52332
  } from "node:fs";
51940
52333
  import { createRequire } from "node:module";
51941
52334
  import { homedir as homedir5, tmpdir } from "node:os";
@@ -52456,7 +52849,7 @@ class PiSubagentRunner {
52456
52849
  try {
52457
52850
  systemPromptTempDir = mkdtempSync(join13(tmpdir(), "mc-pi-prompt-"));
52458
52851
  systemPromptPath = join13(systemPromptTempDir, "system-prompt.txt");
52459
- writeFileSync4(systemPromptPath, options.systemPrompt, "utf8");
52852
+ writeFileSync3(systemPromptPath, options.systemPrompt, "utf8");
52460
52853
  } catch (error) {
52461
52854
  cleanupSystemPromptFile();
52462
52855
  return failBeforeSpawn("spawn_failed", `failed to prepare pi system prompt file: ${error instanceof Error ? error.message : String(error)}`);
@@ -52585,6 +52978,11 @@ class PiSubagentRunner {
52585
52978
  let lastEventTimestamp = 0;
52586
52979
  const accumulatedMessages = [];
52587
52980
  accountingMessages = accumulatedMessages;
52981
+ const stepCap = options.stepCap ?? (options.agent === "magic-context-historian" ? HISTORIAN_STEP_CAP : undefined);
52982
+ let assistantSteps = 0;
52983
+ let streamedAssistantSteps = 0;
52984
+ let terminalSeen = false;
52985
+ const isAssistant = (message) => message !== null && typeof message === "object" && message.role === "assistant";
52588
52986
  rl.on("line", (line) => {
52589
52987
  if (settled || line.length === 0)
52590
52988
  return;
@@ -52618,6 +53016,37 @@ class PiSubagentRunner {
52618
53016
  event,
52619
53017
  ms: elapsedMs
52620
53018
  });
53019
+ if (stepCap !== undefined) {
53020
+ if (e.type === "message_end" && isAssistant(e.message)) {
53021
+ streamedAssistantSteps += 1;
53022
+ assistantSteps += 1;
53023
+ }
53024
+ if (e.type === "agent_end" && Array.isArray(e.messages) && !terminalSeen) {
53025
+ terminalSeen = true;
53026
+ const assistants = e.messages.filter(isAssistant);
53027
+ for (let i = streamedAssistantSteps;i < assistants.length; i += 1)
53028
+ assistantSteps += 1;
53029
+ }
53030
+ if (assistantSteps > stepCap) {
53031
+ if (e.type === "message_end")
53032
+ accumulatedMessages.push(e.message);
53033
+ if (e.type === "agent_end" && Array.isArray(e.messages))
53034
+ accountingMessages = e.messages;
53035
+ if (timeoutHandle)
53036
+ clearTimeout(timeoutHandle);
53037
+ if (drainTimerHandle)
53038
+ clearTimeout(drainTimerHandle);
53039
+ terminateChild(child);
53040
+ settle({
53041
+ ok: false,
53042
+ reason: "step_limit",
53043
+ error: `pi subagent ${options.agent} exceeded its ${stepCap}-step limit`,
53044
+ durationMs: Date.now() - startTime,
53045
+ meta: { cap: stepCap, steps: assistantSteps }
53046
+ });
53047
+ return;
53048
+ }
53049
+ }
52621
53050
  if (e.type === "auto_retry_start" || e.type === "agent_end" && event.willRetry === true) {
52622
53051
  sawAgentEnd = false;
52623
53052
  agentEndMessages = null;
@@ -53079,7 +53508,6 @@ var FULL_PARAMETER_DESCRIPTIONS = {
53079
53508
  content: "The memory text — one standalone fact (write, update, merge).",
53080
53509
  category: "Kind of fact (required for write; on update/merge optional, omitted keeps the current category).",
53081
53510
  ids: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get.",
53082
- limit: "Max results for list (default 10).",
53083
53511
  reason: "Why it is being archived (optional)."
53084
53512
  },
53085
53513
  mmc_search: {
@@ -53114,13 +53542,12 @@ var LIGHT_PARAMETER_DESCRIPTIONS = {
53114
53542
  content: "One standalone fact (write, update, merge).",
53115
53543
  category: "Kind of fact (required for write; optional on update/merge).",
53116
53544
  ids: "Ids from <project-memory>: one for update, 1+ for archive, 2+ for merge, 1–20 for get.",
53117
- limit: "Max results for list (default 10).",
53118
53545
  reason: "Why it is archived (optional)."
53119
53546
  },
53120
53547
  mmc_search: {
53121
53548
  query: "A natural-language question carrying the exact terms you expect in the answer.",
53122
53549
  limit: "Maximum results (default 10).",
53123
- sources: "Restrict to these sources; omit for all.",
53550
+ sources: "Sources; omit or [] for all.",
53124
53551
  from: "Earliest date, YYYY-MM-DD (inclusive).",
53125
53552
  to: "Latest date, YYYY-MM-DD (inclusive; default open)."
53126
53553
  }
@@ -54832,7 +55259,8 @@ function createCtxReduceTool(deps) {
54832
55259
  parts.push(`drop ${formatIds(immediateDropIds)}`);
54833
55260
  if (deferredDropIds.length > 0)
54834
55261
  parts.push(`deferred drop ${formatIds(deferredDropIds)}`);
54835
- return ok4(`Queued: ${parts.join(", ")}.${skippedNote ? ` ${skippedNote}` : ""}${inertNote ? ` ${inertNote}` : ""}${ctxReduceSelfStampNote ? ` ${ctxReduceSelfStampNote}` : ""}`);
55262
+ const held = deferredDropIds.length === 1 ? `Held: §${deferredDropIds[0]} is inside the protected working set; it applies once newer work displaces it.` : deferredDropIds.length > 1 ? `Held: ${deferredDropIds.map((id) => `§${id}`).join(", ")} are inside the protected working set; they apply once newer work displaces them.` : "";
55263
+ return ok4(`Queued: ${parts.join(", ")}.${held ? ` ${held}` : ""}${skippedNote ? ` ${skippedNote}` : ""}${inertNote ? ` ${inertNote}` : ""}${ctxReduceSelfStampNote ? ` ${ctxReduceSelfStampNote}` : ""}`);
54836
55264
  }
54837
55265
  };
54838
55266
  }