@retaw106/mmc-pi 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -139,13 +139,13 @@ import {
139
139
  getToolTagNumberByOwner,
140
140
  getNullOwnerToolTag,
141
141
  adoptNullOwnerToolTag,
142
+ BoundedSessionMap,
142
143
  resolveOpenCodeDbPath,
143
144
  hasV1MessageTables,
144
145
  assertOpenCodeStoreGeneration,
145
146
  withReadOnlySessionDb,
146
147
  expandToolPart,
147
148
  retreatPastHostUnservedRows,
148
- isCoveredInstructionRow,
149
149
  completedToolArcCrossesBoundary,
150
150
  estimateTrueRawMessageTokens,
151
151
  buildToolArcs,
@@ -182,12 +182,13 @@ import {
182
182
  getCachedAbsoluteMessageCount2,
183
183
  primeInMemoryTailRawMessageCache2,
184
184
  readRawSessionMessageIdOrdinalsForRange2,
185
+ compareRawSessionMessageOrder2,
185
186
  readRawSessionMessageById2,
186
187
  getRawSessionMessageCount2,
187
188
  getRawSessionTagKeysThrough2,
188
189
  getLegacyProtectedTailStartOrdinal2,
189
190
  readSessionChunk2
190
- } from "./index-t2aaz8cn.js";
191
+ } from "./index-07rh0nr2.js";
191
192
  import {
192
193
  inRootAsyncContext,
193
194
  registerSlowWriteReporter,
@@ -8911,7 +8912,7 @@ function parseFrozenCalibration(value) {
8911
8912
  if (typeof value !== "object" || value === null || Array.isArray(value))
8912
8913
  return null;
8913
8914
  const candidate = value;
8914
- if (typeof candidate.revision !== "string" || candidate.revision.length === 0 || typeof candidate.providerId !== "string" || typeof candidate.modelId !== "string" || !finitePositive(candidate.systemRatio) || !finitePositive(candidate.toolsRatio) || !finitePositive(candidate.proseRatio) || candidate.source !== "seed" && candidate.source !== "family-fallback") {
8915
+ if (typeof candidate.revision !== "string" || candidate.revision.length === 0 || typeof candidate.providerId !== "string" || typeof candidate.modelId !== "string" || !finitePositive(candidate.systemRatio) || !finitePositive(candidate.toolsRatio) || !finitePositive(candidate.proseRatio) || candidate.source !== "seed" && candidate.source !== "family-fallback" && candidate.source !== "model-id") {
8915
8916
  return null;
8916
8917
  }
8917
8918
  return {
@@ -10709,7 +10710,11 @@ function parsePersistedCompactionMarkerState(raw, storedTargetEndMessageId) {
10709
10710
  if (parsed && typeof parsed === "object" && typeof parsed.boundaryMessageId === "string" && typeof parsed.summaryMessageId === "string" && typeof parsed.compactionPartId === "string" && typeof parsed.summaryPartId === "string" && typeof parsed.boundaryOrdinal === "number") {
10710
10711
  const targetEndMessageId = typeof storedTargetEndMessageId === "string" && storedTargetEndMessageId.length > 0 ? storedTargetEndMessageId : typeof parsed.targetEndMessageId === "string" && parsed.targetEndMessageId.length > 0 ? parsed.targetEndMessageId : null;
10711
10712
  return {
10712
- ...parsed,
10713
+ boundaryMessageId: parsed.boundaryMessageId,
10714
+ summaryMessageId: parsed.summaryMessageId,
10715
+ compactionPartId: parsed.compactionPartId,
10716
+ summaryPartId: parsed.summaryPartId,
10717
+ boundaryOrdinal: parsed.boundaryOrdinal,
10713
10718
  targetEndMessageId
10714
10719
  };
10715
10720
  }
@@ -10720,12 +10725,32 @@ function getPersistedCompactionMarkerState(db, sessionId) {
10720
10725
  const row = db.prepare("SELECT compaction_marker_state, compaction_marker_target_end_message_id FROM session_meta WHERE session_id = ?").get(sessionId);
10721
10726
  return parsePersistedCompactionMarkerState(row?.compaction_marker_state, row?.compaction_marker_target_end_message_id);
10722
10727
  }
10723
- function setPersistedCompactionMarkerState(db, sessionId, state) {
10724
- db.transaction(() => {
10728
+ function getCompactionMarkerPublicationState(db, sessionId) {
10729
+ const row = db.prepare("SELECT compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
10730
+ const raw = row?.compaction_marker_state ?? "";
10731
+ let pending = null;
10732
+ try {
10733
+ pending = parsePersistedCompactionMarkerState(JSON.stringify(JSON.parse(raw).pending));
10734
+ } catch {}
10735
+ return { raw, pending };
10736
+ }
10737
+ function compareAndSetCompactionMarkerPublicationState(db, sessionId, expectedRaw, state, pending) {
10738
+ return db.transaction(() => {
10725
10739
  ensureSessionMetaRow(db, sessionId);
10726
- const previous = state === null ? getPersistedCompactionMarkerState(db, sessionId) ?? getDeferredClearedCompactionMarkerState(db, sessionId) : null;
10727
- const json = state ? JSON.stringify(state) : previous ? JSON.stringify({ deferredClear: previous }) : "";
10728
- db.prepare("UPDATE session_meta SET compaction_marker_state = ?, compaction_marker_target_end_message_id = ? WHERE session_id = ?").run(json, state?.targetEndMessageId ?? null, sessionId);
10740
+ let envelope = {};
10741
+ if (pending) {
10742
+ try {
10743
+ const parsed = JSON.parse(expectedRaw);
10744
+ if (parsed && typeof parsed === "object")
10745
+ envelope = parsed;
10746
+ } catch {}
10747
+ }
10748
+ const raw = JSON.stringify(pending ? { ...envelope, pending } : state);
10749
+ const result = db.prepare(`UPDATE session_meta SET compaction_marker_state = ?, compaction_marker_target_end_message_id = ?
10750
+ WHERE session_id = ? AND COALESCE(compaction_marker_state, '') = ?`).run(raw, state?.targetEndMessageId ?? null, sessionId, expectedRaw);
10751
+ if (result.changes !== 1)
10752
+ throw new Error("marker publication state changed concurrently");
10753
+ return raw;
10729
10754
  }).immediate();
10730
10755
  }
10731
10756
  function getDeferredClearedCompactionMarkerState(db, sessionId) {
@@ -11144,51 +11169,6 @@ import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypt
11144
11169
  import { lstatSync as lstatSync2, readdirSync as readdirSync3, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync3 } from "node:fs";
11145
11170
  import { basename, dirname as dirname3, join as join6 } from "node:path";
11146
11171
 
11147
- // ../plugin/src/shared/bounded-session-map.ts
11148
- class BoundedSessionMap {
11149
- maxEntries;
11150
- store = new Map;
11151
- constructor(maxEntries) {
11152
- if (!Number.isFinite(maxEntries) || maxEntries < 1) {
11153
- throw new Error(`BoundedSessionMap: maxEntries must be >= 1, got ${maxEntries}`);
11154
- }
11155
- this.maxEntries = maxEntries;
11156
- }
11157
- get(sessionId) {
11158
- const value = this.store.get(sessionId);
11159
- if (value === undefined)
11160
- return;
11161
- this.store.delete(sessionId);
11162
- this.store.set(sessionId, value);
11163
- return value;
11164
- }
11165
- peek(sessionId) {
11166
- return this.store.get(sessionId);
11167
- }
11168
- has(sessionId) {
11169
- return this.store.has(sessionId);
11170
- }
11171
- set(sessionId, value) {
11172
- if (this.store.has(sessionId)) {
11173
- this.store.delete(sessionId);
11174
- } else if (this.store.size >= this.maxEntries) {
11175
- const oldest = this.store.keys().next().value;
11176
- if (oldest !== undefined)
11177
- this.store.delete(oldest);
11178
- }
11179
- this.store.set(sessionId, value);
11180
- }
11181
- delete(sessionId) {
11182
- return this.store.delete(sessionId);
11183
- }
11184
- clear() {
11185
- this.store.clear();
11186
- }
11187
- get size() {
11188
- return this.store.size;
11189
- }
11190
- }
11191
-
11192
11172
  // ../plugin/src/shared/storage-file-mutex.ts
11193
11173
  import { execFileSync as execFileSync3 } from "node:child_process";
11194
11174
  import { createHash as createHash4, randomUUID } from "node:crypto";
@@ -12448,9 +12428,10 @@ function closeConnection(connection) {
12448
12428
  counters.readersClosed += 1;
12449
12429
  }
12450
12430
  }
12451
- function messageTimesQuery(count) {
12431
+ function messageTimesQuery(count, withVersions = false) {
12452
12432
  return `SELECT id, COALESCE(time_created, json_extract(data, '$.time.created')) AS created,
12453
12433
  json_extract(data, '$.time.completed') AS completed
12434
+ ${withVersions ? ", type, seq, time_created AS version_created, time_updated AS version_updated" : ""}
12454
12435
  FROM session_message WHERE +session_id = ? AND id IN (${Array.from({ length: count }, () => "?").join(",")})`;
12455
12436
  }
12456
12437
 
@@ -12570,9 +12551,6 @@ class V2StoreReader {
12570
12551
  return null;
12571
12552
  return typeof row.parent_id === "string" && row.parent_id.length > 0;
12572
12553
  }
12573
- sessionIds() {
12574
- return this.prepare("SELECT id FROM session_v2").all().map((row) => row.id);
12575
- }
12576
12554
  storedRowsAfter(sessionID, after) {
12577
12555
  const row = this.prepare("SELECT COUNT(*) AS count FROM session_message WHERE session_id = ? AND seq > ?").get(sessionID, after);
12578
12556
  return row.count;
@@ -12591,16 +12569,46 @@ class V2StoreReader {
12591
12569
  return row ? decode2(row) : null;
12592
12570
  });
12593
12571
  }
12594
- messageTimes(sessionID, ids) {
12595
- const times = new Map;
12572
+ messageTimeVersions(sessionID, ids, span) {
12573
+ const versions = new Map;
12574
+ if (ids.length > 0 && span && span.last - span.first < ids.length * 2) {
12575
+ const wanted = new Set(ids);
12576
+ const rows = this.prepare(`SELECT id, type, seq, time_created AS created, time_updated AS updated
12577
+ FROM session_message WHERE session_id = ? AND seq >= ? AND seq <= ?`).all(sessionID, span.first, span.last);
12578
+ for (const row of rows)
12579
+ if (wanted.has(row.id))
12580
+ versions.set(row.id, row);
12581
+ for (const [id, version] of this.messageTimeVersions(sessionID, ids.filter((id) => !versions.has(id))))
12582
+ versions.set(id, version);
12583
+ return versions;
12584
+ }
12596
12585
  for (let offset = 0;offset < ids.length; offset += 500) {
12597
12586
  const batch = ids.slice(offset, offset + 500);
12598
- const rows = this.prepare(messageTimesQuery(batch.length)).all(sessionID, ...batch);
12587
+ const rows = this.prepare(`SELECT id, type, seq, time_created AS created, time_updated AS updated
12588
+ FROM session_message WHERE +session_id = ? AND id IN (${batch.map(() => "?").join(",")})`).all(sessionID, ...batch);
12599
12589
  for (const row of rows)
12590
+ versions.set(row.id, row);
12591
+ }
12592
+ return versions;
12593
+ }
12594
+ messageTimes(sessionID, ids, versions) {
12595
+ const times = new Map;
12596
+ for (let offset = 0;offset < ids.length; offset += 500) {
12597
+ const batch = ids.slice(offset, offset + 500);
12598
+ const rows = this.prepare(messageTimesQuery(batch.length, versions !== undefined)).all(sessionID, ...batch);
12599
+ for (const row of rows) {
12600
12600
  times.set(row.id, {
12601
12601
  created: typeof row.created === "number" ? row.created : undefined,
12602
12602
  completed: typeof row.completed === "number" ? row.completed : undefined
12603
12603
  });
12604
+ versions?.set(row.id, {
12605
+ id: row.id,
12606
+ type: row.type,
12607
+ seq: row.seq,
12608
+ created: row.version_created,
12609
+ updated: row.version_updated
12610
+ });
12611
+ }
12604
12612
  }
12605
12613
  return times;
12606
12614
  }
@@ -13002,9 +13010,6 @@ function deleteUnmappedMessageFtsRows(db, sessionIds) {
13002
13010
  WHERE rowid > ? AND session_id IN (${placeholders})`).run(state.watermarkRowid, ...sessionIds);
13003
13011
  }
13004
13012
 
13005
- // ../plugin/src/features/magic-context/storage-session-tables.ts
13006
- var cleanupStatements = new WeakMap;
13007
-
13008
13013
  // ../plugin/src/features/magic-context/storage-meta-session.ts
13009
13014
  var SESSION_META_FALLBACK_SELECTS = {
13010
13015
  cache_ttl: "'5m' AS cache_ttl",
@@ -13936,9 +13941,6 @@ function matchedSearchSnippet(content, query, cap = 220) {
13936
13941
  }
13937
13942
 
13938
13943
  // ../plugin/src/features/magic-context/message-index.ts
13939
- var MESSAGE_HISTORY_ORPHAN_SAFETY_AGE_MS = 24 * 60 * 60 * 1000;
13940
- var MESSAGE_HISTORY_ORPHAN_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
13941
- var MESSAGE_HISTORY_ORPHAN_UNAVAILABLE_REPROBE_MS = 24 * 60 * 60 * 1000;
13942
13944
  var lastIndexedStatements = new WeakMap;
13943
13945
  var insertMessageStatements = new WeakMap;
13944
13946
  var upsertProgressStatements = new WeakMap;
@@ -15747,6 +15749,12 @@ function noteLkgCapturedPrefix(sessionId) {
15747
15749
  }
15748
15750
 
15749
15751
  // ../plugin/src/hooks/magic-context/lkg-slot.ts
15752
+ var POSITIONAL_SYSTEM_KEY = "\x00mc-system:";
15753
+ function lkgInputIdentity(id, role, index) {
15754
+ if (typeof id === "string" && id.length > 0)
15755
+ return id.startsWith(POSITIONAL_SYSTEM_KEY) ? null : id;
15756
+ return role === "system" ? `${POSITIONAL_SYSTEM_KEY}${index}` : null;
15757
+ }
15750
15758
  var LKG_SINGLE_SLOT_BYTES = 72 * 1024 * 1024;
15751
15759
  var LKG_TOTAL_BYTES = 160 * 1024 * 1024;
15752
15760
  var LKG_METADATA_BYTES = 256;
@@ -16175,11 +16183,8 @@ function noteEntry(sessionId, input) {
16175
16183
  const slot = getSlot(sessionId);
16176
16184
  if (!slot)
16177
16185
  return null;
16178
- const messages = input.some((message) => isCoveredInstructionRow(message.info)) ? input.filter((message) => !isCoveredInstructionRow(message.info)) : input;
16179
- const entryInputIds = messages.map((message) => {
16180
- const id = message.info?.id;
16181
- return typeof id === "string" ? id : "";
16182
- });
16186
+ const messages = input;
16187
+ const entryInputIds = messages.map((message, index) => lkgInputIdentity(message.info?.id, message.info?.role, index) ?? "");
16183
16188
  const anchorIndex = entryInputIds.indexOf(slot.lastInputMessageId);
16184
16189
  if (anchorIndex < 0)
16185
16190
  return null;
@@ -16488,6 +16493,24 @@ function freezePiContentDecision(db, sessionId, kind, messageId) {
16488
16493
  // ../plugin/src/features/magic-context/storage-native-replay.ts
16489
16494
  var NATIVE_TOOL_INPUTS = "native tool inputs";
16490
16495
  var NATIVE_REASONING_IDS = "native reasoning ids";
16496
+ var nativeWriteBarriers = new WeakMap;
16497
+ function withNativeReplayWriteBarrier(db, sessionId, beforeWrite, edit) {
16498
+ let sessions = nativeWriteBarriers.get(db);
16499
+ if (!sessions) {
16500
+ sessions = new Map;
16501
+ nativeWriteBarriers.set(db, sessions);
16502
+ }
16503
+ const prior = sessions.get(sessionId);
16504
+ sessions.set(sessionId, beforeWrite);
16505
+ try {
16506
+ return edit();
16507
+ } finally {
16508
+ if (prior)
16509
+ sessions.set(sessionId, prior);
16510
+ else
16511
+ sessions.delete(sessionId);
16512
+ }
16513
+ }
16491
16514
  function invalidPersistedReplayState(lane, sessionId) {
16492
16515
  return new Error(`invalid persisted ${lane} state for session ${sessionId}`);
16493
16516
  }
@@ -16572,6 +16595,7 @@ function saveNativeToolInputs(db, sessionId, inputs) {
16572
16595
  }
16573
16596
  if (requested.size === 0)
16574
16597
  return;
16598
+ nativeWriteBarriers.get(db)?.get(sessionId)?.();
16575
16599
  const persisted = updateReplayDocument(db, sessionId, (doc) => {
16576
16600
  const current = parseNativeReplayState(doc, sessionId);
16577
16601
  let changed = false;
@@ -16598,6 +16622,7 @@ function addNativeReasoningIds(db, sessionId, ids) {
16598
16622
  }
16599
16623
  if (requested.size === 0)
16600
16624
  return;
16625
+ nativeWriteBarriers.get(db)?.get(sessionId)?.();
16601
16626
  const persisted = updateReplayDocument(db, sessionId, (doc) => {
16602
16627
  const current = parseNativeReplayState(doc, sessionId);
16603
16628
  let changed = false;
@@ -17799,7 +17824,7 @@ function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion)
17799
17824
  startMessageFtsRowidMapBackfill(db).then(async () => {
17800
17825
  if (!readsOpenCodeStore)
17801
17826
  return;
17802
- const { readRawSessionMessagePage, readRawSessionMessages } = await import("./read-session-chunk-hcecqvhm.js");
17827
+ const { readRawSessionMessagePage, readRawSessionMessages } = await import("./read-session-chunk-qg5jfqjg.js");
17803
17828
  const { startMessageTimeBackfill } = await import("./message-time-backfill-y26yy5pp.js");
17804
17829
  await startMessageTimeBackfill(db, Object.assign(readRawSessionMessages, {
17805
17830
  readPage: readRawSessionMessagePage
@@ -21055,7 +21080,7 @@ function registerStandalonePluginMarker(version) {
21055
21080
  // package.json
21056
21081
  var package_default = {
21057
21082
  name: "@retaw106/mmc-pi",
21058
- version: "2.5.0",
21083
+ version: "2.6.0",
21059
21084
  type: "module",
21060
21085
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
21061
21086
  main: "dist/index.js",
@@ -23532,6 +23557,14 @@ var RECENT_TOOL_SKELETON_WINDOW = 20;
23532
23557
  function buildReplacementContent(tagId) {
23533
23558
  return `[dropped §${tagId}§]`;
23534
23559
  }
23560
+ var FROZEN_TEXT_ANNOTATION_SUFFIX = /(?:\n\n(?:<instruction name="deferred_notes">(?:(?!<\/instruction>)[\s\S])*<\/instruction>|<ctx-search-hint>(?:(?!<\/ctx-search-hint>)[\s\S])*<\/ctx-search-hint>))+$/;
23561
+ function renderDroppedText(tagId, renderedText) {
23562
+ const placeholder = buildReplacementContent(tagId);
23563
+ if (renderedText?.startsWith(placeholder))
23564
+ return renderedText;
23565
+ const annotations = renderedText?.match(FROZEN_TEXT_ANNOTATION_SUFFIX)?.[0] ?? "";
23566
+ return placeholder + annotations;
23567
+ }
23535
23568
  function applyNewToolDrop(target, options) {
23536
23569
  const plan = planNewToolDrop(target, options, !!options.persist);
23537
23570
  if (!plan)
@@ -23618,6 +23651,7 @@ function prepareLegacyToolSkeletonConversions(db, sessionId, targets, options =
23618
23651
  AND drop_mode IN ('truncated', 'full')
23619
23652
  ORDER BY tag_number`).all(sessionId);
23620
23653
  const rows = readRows(db);
23654
+ options.prepareRemovals?.(rows);
23621
23655
  const identity = JSON.stringify(rows);
23622
23656
  const converted = new Map;
23623
23657
  for (const row of rows) {
@@ -23647,13 +23681,21 @@ function prepareLegacyToolSkeletonConversions(db, sessionId, targets, options =
23647
23681
  };
23648
23682
  }
23649
23683
  var PENDING_OPERATION_CHUNK_SIZE = 64;
23684
+ var PENDING_OPERATION_REJECTED_LOG_CHUNK = 20;
23650
23685
  function applyPendingOperations(sessionId, db, targets, protectedTagIds, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set, onTagReduced, onBatchComplete, onEligibilitySnapshot) {
23651
23686
  let didMutateMessage = false;
23652
23687
  let diagnostics;
23653
- const reject = (reason) => {
23688
+ const reject = (tagNumber, reason, state = {}) => {
23654
23689
  if (!diagnostics)
23655
23690
  return;
23656
23691
  diagnostics.reasons[reason] = (diagnostics.reasons[reason] ?? 0) + 1;
23692
+ const persisted = state.persisted === true;
23693
+ (diagnostics.rejected ??= []).push({
23694
+ tagNumber,
23695
+ reason,
23696
+ persisted,
23697
+ queueState: state.queueState ?? (persisted ? "removed" : "retained")
23698
+ });
23657
23699
  };
23658
23700
  let admitted = false;
23659
23701
  let startedAt = 0;
@@ -23713,29 +23755,39 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23713
23755
  for (const { op: pendingOp, synthetic } of chunk) {
23714
23756
  const tagStatus = tagStatusById.get(pendingOp.tagId);
23715
23757
  if (tagStatus === "compacted" || tagStatus === "dropped") {
23716
- reject("already_reduced");
23758
+ reject(pendingOp.tagId, "already_reduced", {
23759
+ queueState: synthetic ? "none" : "removed"
23760
+ });
23717
23761
  if (!synthetic)
23718
23762
  removePendingOp(db, sessionId, pendingOp.tagId);
23719
23763
  continue;
23720
23764
  }
23721
23765
  if (removedTagIds.has(pendingOp.tagId)) {
23722
- reject("drop_incomplete");
23766
+ reject(pendingOp.tagId, "drop_incomplete", {
23767
+ queueState: synthetic ? "none" : "retained"
23768
+ });
23723
23769
  continue;
23724
23770
  }
23725
23771
  const target = targets.get(pendingOp.tagId);
23726
23772
  const veto = pendingDropVeto(pendingOp.tagId, target, protectedTagIds);
23727
23773
  if (veto) {
23728
- reject(veto);
23774
+ reject(pendingOp.tagId, veto, {
23775
+ queueState: synthetic ? "none" : "retained"
23776
+ });
23729
23777
  continue;
23730
23778
  }
23731
23779
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
23732
23780
  if (synthetic) {
23733
23781
  if (!isToolTag) {
23734
- reject("synthetic_non_tool");
23782
+ reject(pendingOp.tagId, "synthetic_non_tool", {
23783
+ queueState: "none"
23784
+ });
23735
23785
  continue;
23736
23786
  }
23737
23787
  if (target?.canDrop?.() !== true) {
23738
- reject("synthetic_target_not_droppable");
23788
+ reject(pendingOp.tagId, "synthetic_target_not_droppable", {
23789
+ queueState: "none"
23790
+ });
23739
23791
  continue;
23740
23792
  }
23741
23793
  }
@@ -23748,14 +23800,19 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23748
23800
  if (editMarkerTagIds.has(pendingOp.tagId)) {
23749
23801
  const strip = target?.hasAttachments?.() === true;
23750
23802
  if (target?.canDrop?.() === false || target?.prepareDrop?.() === false || !target) {
23751
- reject("edit_marker_incomplete");
23803
+ reject(pendingOp.tagId, "edit_marker_incomplete", {
23804
+ queueState: synthetic ? "none" : "retained"
23805
+ });
23752
23806
  continue;
23753
23807
  }
23754
23808
  persistDrop(strip ? "edit_marker_stripped" : "edit_marker");
23755
23809
  mutations.push(() => {
23756
23810
  const markResult = (strip ? target?.editMarkerStripped?.() : target?.editMarker?.()) ?? "absent";
23757
23811
  if (markResult === "incomplete" || markResult === "absent") {
23758
- reject(`edit_marker_${markResult}`);
23812
+ reject(pendingOp.tagId, `edit_marker_${markResult}`, {
23813
+ persisted: true,
23814
+ queueState: synthetic ? "none" : "removed"
23815
+ });
23759
23816
  return;
23760
23817
  }
23761
23818
  record(true);
@@ -23767,7 +23824,9 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23767
23824
  } else {
23768
23825
  const plan = planNewToolDrop(target, { inWindow: skeletonWindow.has(pendingOp.tagId) }, true, plannedRemovals);
23769
23826
  if (!plan) {
23770
- reject("drop_incomplete");
23827
+ reject(pendingOp.tagId, "drop_incomplete", {
23828
+ queueState: synthetic ? "none" : "retained"
23829
+ });
23771
23830
  continue;
23772
23831
  }
23773
23832
  persistDrop(plan.mode);
@@ -23778,7 +23837,10 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23778
23837
  mutations.push(() => {
23779
23838
  const { result: dropResult, mode: appliedMode } = plan.apply();
23780
23839
  if (dropResult === "incomplete" || synthetic && dropResult !== "removed" && dropResult !== "truncated") {
23781
- reject(`drop_${dropResult}`);
23840
+ reject(pendingOp.tagId, `drop_${dropResult}`, {
23841
+ persisted: true,
23842
+ queueState: synthetic ? "none" : "removed"
23843
+ });
23782
23844
  return;
23783
23845
  }
23784
23846
  if (dropResult === "removed" || dropResult === "truncated") {
@@ -23788,15 +23850,18 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23788
23850
  mode: appliedMode === "full" ? "full" : "truncated"
23789
23851
  });
23790
23852
  } else {
23791
- reject(`drop_${dropResult}`);
23853
+ reject(pendingOp.tagId, `drop_${dropResult}`, {
23854
+ persisted: true,
23855
+ queueState: synthetic ? "none" : "removed"
23856
+ });
23792
23857
  record(false);
23793
23858
  }
23794
23859
  });
23795
23860
  }
23796
23861
  } else if (target) {
23797
- const replacement = buildReplacementContent(pendingOp.tagId);
23862
+ const replacement = renderDroppedText(pendingOp.tagId, target.getContent?.());
23798
23863
  if (target.canSetContent?.(replacement) === false) {
23799
- reject("content_unchanged");
23864
+ reject(pendingOp.tagId, "content_unchanged");
23800
23865
  continue;
23801
23866
  }
23802
23867
  persistDrop();
@@ -23812,12 +23877,18 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23812
23877
  removedCharacters: Math.max(0, originalCharacters - replacement.length)
23813
23878
  });
23814
23879
  } else {
23815
- reject("content_unchanged");
23880
+ reject(pendingOp.tagId, "content_unchanged", {
23881
+ persisted: true,
23882
+ queueState: "removed"
23883
+ });
23816
23884
  }
23817
23885
  });
23818
23886
  } else if (!synthetic) {
23819
- reject("target_absent");
23820
23887
  persistDrop();
23888
+ reject(pendingOp.tagId, "target_absent", {
23889
+ persisted: true,
23890
+ queueState: "removed"
23891
+ });
23821
23892
  mutations.push(() => record(false));
23822
23893
  }
23823
23894
  }
@@ -23844,6 +23915,15 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23844
23915
  const reasons = Object.entries(diagnostics.reasons).sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${count}`).join(",");
23845
23916
  sessionLog(sessionId, `pending operations no-op: source=${diagnostics.source} total=${diagnostics.total} persisted=${diagnostics.persistedWithoutMutation} reasons=${reasons || "none"}`);
23846
23917
  }
23918
+ const rejected = diagnostics.rejected;
23919
+ if (rejected && rejected.length > 0) {
23920
+ const lineCount = Math.ceil(rejected.length / PENDING_OPERATION_REJECTED_LOG_CHUNK);
23921
+ for (let offset = 0;offset < rejected.length; offset += PENDING_OPERATION_REJECTED_LOG_CHUNK) {
23922
+ const entries = rejected.slice(offset, offset + PENDING_OPERATION_REJECTED_LOG_CHUNK).map((entry) => `${entry.tagNumber}:${entry.reason}:${entry.queueState}${entry.persisted ? ":persisted" : ""}`).join(",");
23923
+ const chunkLabel = lineCount > 1 ? ` chunk=${Math.floor(offset / PENDING_OPERATION_REJECTED_LOG_CHUNK) + 1}/${lineCount}` : "";
23924
+ sessionLog(sessionId, `pending operations rejected: source=${diagnostics.source} total=${diagnostics.total} mutated=${diagnostics.mutated} persisted=${diagnostics.persistedWithoutMutation}${chunkLabel} rejected=${entries}`);
23925
+ }
23926
+ }
23847
23927
  }
23848
23928
  return didMutateMessage;
23849
23929
  }
@@ -23884,7 +23964,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
23884
23964
  }
23885
23965
  }
23886
23966
  } else if (target) {
23887
- const changed = target.setContent(buildReplacementContent(tag.tagNumber));
23967
+ const changed = target.setContent(renderDroppedText(tag.tagNumber, target.getContent?.()));
23888
23968
  if (changed)
23889
23969
  didMutateMessage = true;
23890
23970
  }
@@ -23892,6 +23972,15 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
23892
23972
  }
23893
23973
  return didMutateMessage;
23894
23974
  }
23975
+ function prepareFlushedToolRemovals(targets, tags) {
23976
+ for (const tag of tags) {
23977
+ if (tag.status !== "dropped" || tag.type !== "tool")
23978
+ continue;
23979
+ if (tag.dropMode != null && tag.dropMode !== "full")
23980
+ continue;
23981
+ targets.get(tag.tagNumber)?.prepareReplayDrop?.();
23982
+ }
23983
+ }
23895
23984
 
23896
23985
  // ../plugin/src/hooks/magic-context/cache-busting-signals.ts
23897
23986
  function hasReclaimRide(signals) {
@@ -25565,6 +25654,56 @@ function isHistorianDrainBudgetSpent(args) {
25565
25654
  return true;
25566
25655
  }
25567
25656
 
25657
+ // ../plugin/src/hooks/magic-context/historian-history-wait.ts
25658
+ var historyWaits = new Map;
25659
+ function newestMessageId(snapshot) {
25660
+ try {
25661
+ const count = getRawSessionMessageOrdinalCount2(snapshot.sessionId);
25662
+ const ids = readRawSessionMessageIdOrdinalsForRange2(snapshot.sessionId, count, count);
25663
+ const newest = ids.keys().next().value;
25664
+ if (newest)
25665
+ return newest;
25666
+ } catch {}
25667
+ return snapshot.rawLastMessageIdAtTrigger;
25668
+ }
25669
+ function recoveryWindow(snapshot, percentage) {
25670
+ return percentage >= escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage || snapshot.emergencyTailScale !== undefined;
25671
+ }
25672
+ function clearHistorianHistoryWait(sessionId) {
25673
+ historyWaits.delete(sessionId);
25674
+ }
25675
+ function recordHistorianNoProgress(snapshot, emergency = false) {
25676
+ const newest = newestMessageId(snapshot);
25677
+ if (!newest)
25678
+ return;
25679
+ const previous = historyWaits.get(snapshot.sessionId);
25680
+ const sameHistory = previous?.newestMessageId === newest;
25681
+ historyWaits.set(snapshot.sessionId, {
25682
+ newestMessageId: newest,
25683
+ recoveryAttempted: sameHistory && previous.recoveryAttempted || emergency || recoveryWindow(snapshot, snapshot.usagePercentage)
25684
+ });
25685
+ }
25686
+ function shouldWaitForNewHistorianHistory(snapshot, options = {}) {
25687
+ const wait = historyWaits.get(snapshot.sessionId);
25688
+ if (!wait)
25689
+ return false;
25690
+ const newest = newestMessageId(snapshot);
25691
+ if (newest && newest !== wait.newestMessageId) {
25692
+ try {
25693
+ const order = compareRawSessionMessageOrder2(snapshot.sessionId, newest, wait.newestMessageId);
25694
+ if (order === null || order > 0)
25695
+ return false;
25696
+ } catch {}
25697
+ }
25698
+ const recovery = options.emergency || recoveryWindow(snapshot, options.usagePercentage ?? snapshot.usagePercentage);
25699
+ if (recovery && !wait.recoveryAttempted) {
25700
+ if (options.claim)
25701
+ wait.recoveryAttempted = true;
25702
+ return false;
25703
+ }
25704
+ return true;
25705
+ }
25706
+
25568
25707
  // ../plugin/src/hooks/magic-context/active-anthropic-turn.ts
25569
25708
  function isInActiveAnthropicTurn(messages, index, anthropic) {
25570
25709
  const start = activeAnthropicTurnStart(messages, anthropic);
@@ -25948,6 +26087,13 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
25948
26087
  }
25949
26088
  return { shouldFire: false };
25950
26089
  }
26090
+ if (tailInfo.boundarySnapshot && shouldWaitForNewHistorianHistory(tailInfo.boundarySnapshot, {
26091
+ usagePercentage: usage.percentage,
26092
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery
26093
+ })) {
26094
+ sessionLog(sessionId, "compartment trigger: waiting for new history after no forward progress");
26095
+ return { shouldFire: false };
26096
+ }
25951
26097
  const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
25952
26098
  const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff, eligiblePendingDropTagIds);
25953
26099
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
@@ -27139,6 +27285,93 @@ function compareOpenCodeMessagesByCanonicalOrder(sessionId, leftMessageId, right
27139
27285
  return 1;
27140
27286
  return 0;
27141
27287
  }
27288
+ function planCompactionMarker(args, boundary) {
27289
+ const identity = `${args.sessionId}\x00${args.endMessageId}`;
27290
+ return {
27291
+ boundaryMessageId: boundary.id,
27292
+ summaryMessageId: generateMessageId(boundary.timeCreated + 1, 1n, `${identity}\x00summary-message`),
27293
+ compactionPartId: generatePartId(boundary.timeCreated, 1n, `${identity}\x00compaction-part`),
27294
+ summaryPartId: generatePartId(boundary.timeCreated + 1, 2n, `${identity}\x00summary-part`)
27295
+ };
27296
+ }
27297
+ function withCompactionMarkerPublicationLock(operation) {
27298
+ return runMarkerWriteTransaction(getWritableOpenCodeDb(), "compaction-marker-replace", operation);
27299
+ }
27300
+ var MARKER_PAIR_FROM = `FROM message m
27301
+ JOIN part p ON +p.session_id = m.session_id
27302
+ AND p.message_id = json_extract(m.data, '$.parentID')
27303
+ WHERE m.session_id = ?
27304
+ AND CASE WHEN json_valid(m.data)
27305
+ THEN COALESCE(json_extract(m.data, '$.summary'), 0) ELSE 0 END = 1
27306
+ AND COALESCE(json_extract(m.data, '$.finish'), '') <> ''
27307
+ AND COALESCE(json_extract(p.data, '$.type'), '') = 'compaction'`;
27308
+ var MC_MARKER_IDENTITY = `(COALESCE(json_extract(m.data, '$.providerID'), '') = 'magic-context'
27309
+ OR COALESCE(json_extract(m.data, '$.modelID'), '') = 'magic-context'
27310
+ OR EXISTS (SELECT 1 FROM part t
27311
+ WHERE t.message_id = m.id AND +t.session_id = m.session_id
27312
+ AND COALESCE(json_extract(t.data, '$.type'), '') = 'text'
27313
+ AND COALESCE(json_extract(t.data, '$.text'), '') = ?))`;
27314
+ function inspectCompactionMarkerPublication(sessionId, recognized, summaryText) {
27315
+ const db = getWritableOpenCodeDb();
27316
+ const ids = [...new Set(recognized.map((marker) => marker.summaryMessageId))];
27317
+ const placeholders = ids.map(() => "?").join(", ");
27318
+ const rogue = db.prepare(`SELECT 1 ${MARKER_PAIR_FROM}
27319
+ AND ${MC_MARKER_IDENTITY}
27320
+ AND m.id NOT IN (${placeholders})
27321
+ LIMIT 1`).get(sessionId, summaryText, ...ids);
27322
+ if (rogue)
27323
+ return { kind: "ambiguous" };
27324
+ const pairs = db.prepare(`SELECT m.id AS summaryId, p.message_id AS boundaryId, p.id AS partId
27325
+ ${MARKER_PAIR_FROM}
27326
+ AND m.id IN (${placeholders})
27327
+ LIMIT 2`).all(sessionId, ...ids);
27328
+ if (pairs.length > 1)
27329
+ return { kind: "ambiguous" };
27330
+ const rowById = db.prepare(`SELECT 1 FROM message WHERE id = ?
27331
+ UNION ALL SELECT 1 FROM part WHERE id IN (?, ?) LIMIT 1`);
27332
+ if (pairs.length === 0) {
27333
+ for (const marker of recognized) {
27334
+ if (rowById.get(marker.summaryMessageId, marker.compactionPartId, marker.summaryPartId))
27335
+ return { kind: "ambiguous" };
27336
+ }
27337
+ return { kind: "verified", marker: null };
27338
+ }
27339
+ const pair = pairs[0];
27340
+ const marker = recognized.find((item) => item.summaryMessageId === pair.summaryId && item.boundaryMessageId === pair.boundaryId && item.compactionPartId === pair.partId);
27341
+ if (!marker)
27342
+ return { kind: "ambiguous" };
27343
+ for (const other of recognized) {
27344
+ if (other.summaryMessageId === marker.summaryMessageId && other.compactionPartId === marker.compactionPartId && other.summaryPartId === marker.summaryPartId)
27345
+ continue;
27346
+ if (rowById.get(other.summaryMessageId, other.compactionPartId, other.summaryPartId))
27347
+ return { kind: "ambiguous" };
27348
+ }
27349
+ const row = db.prepare(`SELECT c.data AS compactionData FROM message m
27350
+ JOIN message b ON b.id = ? AND b.session_id = m.session_id
27351
+ JOIN part c ON c.id = ? AND c.message_id = b.id AND c.session_id = m.session_id
27352
+ JOIN part t ON t.id = ? AND t.message_id = m.id AND t.session_id = m.session_id
27353
+ WHERE m.id = ? AND m.session_id = ?
27354
+ AND json_extract(b.data, '$.role') = 'user'
27355
+ AND json_extract(m.data, '$.role') = 'assistant'
27356
+ AND json_extract(m.data, '$.parentID') = b.id
27357
+ AND json_extract(m.data, '$.summary') = 1
27358
+ AND json_extract(m.data, '$.finish') = 'stop'
27359
+ AND json_extract(m.data, '$.error') IS NULL
27360
+ AND json_extract(m.data, '$.providerID') = 'magic-context'
27361
+ AND json_extract(m.data, '$.modelID') = 'magic-context'
27362
+ AND json_extract(t.data, '$.type') = 'text' AND json_extract(t.data, '$.text') = ?
27363
+ AND (SELECT COUNT(*) FROM part p WHERE p.message_id = m.id AND +p.session_id = m.session_id) = 1`).get(marker.boundaryMessageId, marker.compactionPartId, marker.summaryPartId, marker.summaryMessageId, sessionId, summaryText);
27364
+ if (!row || !isMcCanonicalCompactionPartData(JSON.parse(row.compactionData)) || findBoundaryUserMessage(sessionId, marker.boundaryMessageId)?.id !== marker.boundaryMessageId)
27365
+ return { kind: "ambiguous" };
27366
+ return { kind: "verified", marker };
27367
+ }
27368
+ function isCompactionMarkerIntentAbsent(marker, targetEndMessageId) {
27369
+ if (!targetEndMessageId)
27370
+ return false;
27371
+ const db = getWritableOpenCodeDb();
27372
+ return !db.prepare(`SELECT 1 FROM message WHERE id IN (?, ?)
27373
+ UNION ALL SELECT 1 FROM part WHERE id IN (?, ?) LIMIT 1`).get(targetEndMessageId, marker.summaryMessageId, marker.compactionPartId, marker.summaryPartId);
27374
+ }
27142
27375
  function removeLegacyMarkerLineageRows(db, args) {
27143
27376
  const legacySummaries = db.prepare(`SELECT m.id
27144
27377
  FROM message m
@@ -27182,10 +27415,11 @@ function injectCompactionMarker(args) {
27182
27415
  return null;
27183
27416
  }
27184
27417
  const boundaryTime = boundary.timeCreated;
27185
- const markerIdentity = `${args.sessionId}\x00${args.endMessageId}`;
27186
- const summaryMsgId = generateMessageId(boundaryTime + 1, 1n, `${markerIdentity}\x00summary-message`);
27187
- const compactionPartId = generatePartId(boundaryTime, 1n, `${markerIdentity}\x00compaction-part`);
27188
- const summaryPartId = generatePartId(boundaryTime + 1, 2n, `${markerIdentity}\x00summary-part`);
27418
+ const {
27419
+ summaryMessageId: summaryMsgId,
27420
+ compactionPartId,
27421
+ summaryPartId
27422
+ } = planCompactionMarker(args, boundary);
27189
27423
  const summaryMsgData = JSON.stringify({
27190
27424
  role: "assistant",
27191
27425
  parentID: boundary.id,
@@ -27276,25 +27510,122 @@ function replaceCompactionMarker(existing, args) {
27276
27510
  };
27277
27511
  }
27278
27512
  }
27513
+ function isMcCanonicalCompactionPartData(data) {
27514
+ if (typeof data !== "object" || data === null || Array.isArray(data))
27515
+ return false;
27516
+ const record = data;
27517
+ const keys = Object.keys(record).sort();
27518
+ return keys.length === 2 && keys[0] === "auto" && keys[1] === "type" && record.type === "compaction" && record.auto === true;
27519
+ }
27520
+
27521
+ // ../plugin/src/hooks/magic-context/compaction-marker-publication.ts
27522
+ function samePair(left, right) {
27523
+ return left.boundaryMessageId === right.boundaryMessageId && left.summaryMessageId === right.summaryMessageId && left.compactionPartId === right.compactionPartId && left.summaryPartId === right.summaryPartId;
27524
+ }
27525
+ function publishRecoverableCompactionMarker(db, args, targetIsCurrent) {
27526
+ const { sessionId } = args;
27527
+ let raw = "";
27528
+ let previous = null;
27529
+ let oldSummary = null;
27530
+ let intended;
27531
+ let reason = "publication-failed";
27532
+ let recovered;
27533
+ let cut = "uncertain";
27534
+ let writingState = false;
27535
+ const persist = (state) => {
27536
+ writingState = true;
27537
+ db.transaction(() => {
27538
+ raw = compareAndSetCompactionMarkerPublicationState(db, sessionId, raw, state, null);
27539
+ if (previous && previous.summaryMessageId !== state.summaryMessageId) {
27540
+ const tag = getTagNumberByMessageId(db, sessionId, `${previous.summaryMessageId}:p0`);
27541
+ if (tag !== null)
27542
+ updateTagStatus(db, sessionId, tag, "dropped");
27543
+ }
27544
+ }).immediate();
27545
+ writingState = false;
27546
+ previous = state;
27547
+ };
27548
+ try {
27549
+ cut = getCompactionMarkerPublicationState(db, sessionId).pending ? "uncertain" : "definitely-no-cut";
27550
+ const result = withCompactionMarkerPublicationLock(() => {
27551
+ const snapshot = getCompactionMarkerPublicationState(db, sessionId);
27552
+ if (snapshot.pending)
27553
+ cut = "uncertain";
27554
+ raw = snapshot.raw;
27555
+ previous = getPersistedCompactionMarkerState(db, sessionId) ?? getDeferredClearedCompactionMarkerState(db, sessionId);
27556
+ oldSummary = previous?.summaryMessageId ?? null;
27557
+ if (snapshot.pending && (snapshot.pending.boundaryOrdinal > args.endOrdinal || snapshot.pending.boundaryOrdinal === args.endOrdinal && snapshot.pending.targetEndMessageId !== args.endMessageId) && !isCompactionMarkerIntentAbsent(snapshot.pending, snapshot.pending.targetEndMessageId))
27558
+ throw new Error("a newer marker publication intent is pending");
27559
+ const boundary = findBoundaryUserMessage(sessionId, args.endMessageId);
27560
+ if (!targetIsCurrent() || !boundary || boundary.id !== args.resolvedBoundary?.id || boundary.timeCreated !== args.resolvedBoundary.timeCreated)
27561
+ throw new Error("marker publication frontier changed before publication");
27562
+ intended = {
27563
+ ...planCompactionMarker(args, boundary),
27564
+ boundaryOrdinal: args.endOrdinal,
27565
+ targetEndMessageId: args.endMessageId
27566
+ };
27567
+ let pending = snapshot.pending;
27568
+ if (pending) {
27569
+ const pendingTarget = pending.targetEndMessageId;
27570
+ const pendingBoundary = pendingTarget ? findBoundaryUserMessage(sessionId, pendingTarget) : null;
27571
+ const compartments = pendingTarget ? getCompartmentsByEndMessageId(db, sessionId, pendingTarget) : [];
27572
+ const validTarget = pending.targetEndMessageId === args.endMessageId && pending.boundaryOrdinal === args.endOrdinal || compartments.length === 1 && compartments[0].endMessage === pending.boundaryOrdinal;
27573
+ if (!validTarget || !pendingBoundary || !pendingTarget || !samePair(pending, planCompactionMarker({ ...args, endMessageId: pendingTarget }, pendingBoundary)))
27574
+ pending = null;
27575
+ }
27576
+ const inspection = inspectCompactionMarkerPublication(sessionId, [intended, ...previous ? [previous] : [], ...pending ? [pending] : []], args.summaryText);
27577
+ if (inspection.kind === "ambiguous") {
27578
+ reason = "ambiguous-left-alone";
27579
+ return {
27580
+ kind: cut,
27581
+ error: new Error("unverified or ambiguous host marker pairs; left alone")
27582
+ };
27583
+ }
27584
+ cut = "definitely-no-cut";
27585
+ const host = inspection.marker;
27586
+ if (host && (pending && samePair(host, pending) || samePair(host, intended))) {
27587
+ const adopted = pending && samePair(host, pending) ? pending : intended;
27588
+ recovered = adopted === pending ? "pending-intent-recovered" : "adopted-existing-pair";
27589
+ cut = "uncertain";
27590
+ if (samePair(host, intended))
27591
+ return { kind: "committed", marker: host };
27592
+ persist(adopted);
27593
+ }
27594
+ writingState = true;
27595
+ raw = compareAndSetCompactionMarkerPublicationState(db, sessionId, raw, previous, intended);
27596
+ writingState = false;
27597
+ cut = "uncertain";
27598
+ const replacement = replaceCompactionMarker(host, {
27599
+ ...args,
27600
+ resolvedBoundary: boundary
27601
+ });
27602
+ if (recovered && replacement.kind !== "committed")
27603
+ return { kind: "uncertain", error: replacement.error };
27604
+ if (replacement.kind !== "committed")
27605
+ cut = replacement.kind === "uncertain" ? "uncertain" : "definitely-no-cut";
27606
+ return replacement;
27607
+ });
27608
+ if (result.kind === "committed") {
27609
+ if (!intended)
27610
+ throw new Error("committed marker has no publication intent");
27611
+ persist(intended);
27612
+ reason = recovered ?? "published";
27613
+ }
27614
+ return result;
27615
+ } catch (error) {
27616
+ if (writingState)
27617
+ reason = "state-write-failed";
27618
+ return {
27619
+ kind: cut,
27620
+ error: error instanceof Error ? error : new Error(String(error))
27621
+ };
27622
+ } finally {
27623
+ sessionLog(sessionId, `compaction-marker publication reason=${reason} oldSummary=${oldSummary ?? "none"} newSummary=${intended?.summaryMessageId ?? "none"} target=${args.endMessageId} at=${Date.now()}`);
27624
+ }
27625
+ }
27279
27626
 
27280
27627
  // ../plugin/src/hooks/magic-context/compaction-marker-manager.ts
27281
27628
  var MARKER_SUMMARY_TEXT = "[Compacted by magic-context — session history is managed by the plugin]";
27282
- function dropMarkerSummaryTag(db, sessionId, summaryMessageId) {
27283
- const tagNumber = getTagNumberByMessageId(db, sessionId, `${summaryMessageId}:p0`);
27284
- if (tagNumber !== null)
27285
- updateTagStatus(db, sessionId, tagNumber, "dropped");
27286
- }
27287
- function persistMarkerStateAndDropReplacedTag(db, sessionId, state, replacedSummaryMessageId) {
27288
- let transactionStartedAt = 0;
27289
- db.transaction(() => {
27290
- transactionStartedAt = performance.now();
27291
- setPersistedCompactionMarkerState(db, sessionId, state);
27292
- if (replacedSummaryMessageId !== null) {
27293
- dropMarkerSummaryTag(db, sessionId, replacedSummaryMessageId);
27294
- }
27295
- }).immediate();
27296
- logSlowWriteTransaction("marker-drain", transactionStartedAt);
27297
- }
27298
27629
  function getCompartmentEndMessageIdForOrdinal(db, sessionId, endOrdinal) {
27299
27630
  const row = db.prepare(`SELECT end_message_id
27300
27631
  FROM compartments
@@ -27335,8 +27666,7 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
27335
27666
  return false;
27336
27667
  }
27337
27668
  const existing = getPersistedCompactionMarkerState(db, sessionId);
27338
- const removedSummaryMessageId = existing?.summaryMessageId ?? null;
27339
- if (existing) {
27669
+ if (existing && !getCompactionMarkerPublicationState(db, sessionId).pending) {
27340
27670
  if (existingMarkerAlreadyCoversTarget(sessionId, existing, lastCompartmentEnd, targetEndMessageId)) {
27341
27671
  return true;
27342
27672
  }
@@ -27346,22 +27676,15 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
27346
27676
  sessionLog(sessionId, `compaction-marker: no user boundary found at or before endMessageId ${targetEndMessageId} (ordinal ${lastCompartmentEnd}); preserving existing marker`);
27347
27677
  return false;
27348
27678
  }
27349
- const replacement = replaceCompactionMarker(existing, {
27679
+ const replacement = publishRecoverableCompactionMarker(db, {
27350
27680
  sessionId,
27351
27681
  endOrdinal: lastCompartmentEnd,
27352
27682
  endMessageId: targetEndMessageId,
27353
27683
  summaryText: MARKER_SUMMARY_TEXT,
27354
27684
  directory: directory ?? process.cwd(),
27355
27685
  resolvedBoundary: boundary
27356
- });
27686
+ }, () => JSON.stringify(getPersistedCompactionMarkerState(db, sessionId)) === JSON.stringify(existing) && getCompartmentEndMessageIdForOrdinal(db, sessionId, lastCompartmentEnd) === targetEndMessageId);
27357
27687
  if (replacement.kind === "committed") {
27358
- const result = replacement.marker;
27359
- persistMarkerStateAndDropReplacedTag(db, sessionId, {
27360
- ...result,
27361
- boundaryOrdinal: lastCompartmentEnd,
27362
- targetEndMessageId
27363
- }, removedSummaryMessageId);
27364
- sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
27365
27688
  return true;
27366
27689
  }
27367
27690
  return false;
@@ -28636,7 +28959,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
28636
28959
  targets,
28637
28960
  skipPrefixInjection,
28638
28961
  entryFingerprint: options.entryFingerprintByMessageId?.get(messageId) ?? null,
28639
- reuseIdentity: reuseIdentity || contentDerivedTextIds !== undefined,
28962
+ reuseIdentity: reuseIdentity || options.reuseTextMessageIds?.has(messageId) === true || contentDerivedTextIds !== undefined,
28640
28963
  timing,
28641
28964
  textIdentitySourceCache: options.textIdentitySourceCache,
28642
28965
  textTokenCache: options.textTokenCache
@@ -28705,7 +29028,11 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
28705
29028
  if (timing)
28706
29029
  timing.identity += performance.now() - identityStart;
28707
29030
  } else {
28708
- const accounting = readAggregateToolAccounting(part, timing, options.toolTokenCache, tokenCacheKey);
29031
+ const accounting = readAggregateToolAccounting(part, timing, options.toolTokenCache, tokenCacheKey, {
29032
+ byteSize: existing.maxByteSize,
29033
+ tokenCount: existing.tokenCountKnown ? existing.maxTokenCount : null,
29034
+ inputTokenCount: existing.inputTokenCount
29035
+ });
28709
29036
  text = accounting.text;
28710
29037
  if (part.kind === "tool_result") {
28711
29038
  applyGrownToolResultAccounting({
@@ -28764,6 +29091,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
28764
29091
  occurrences: [{ message, part, kind: part.kind }],
28765
29092
  maxByteSize: reusableAccounting.byteSize,
28766
29093
  maxTokenCount: reusableAccounting.tokenCount ?? 0,
29094
+ tokenCountKnown: reusableAccounting.tokenCount !== null,
28767
29095
  toolName: null,
28768
29096
  inputByteSize: reusableAccounting.inputByteSize,
28769
29097
  inputTokenCount: reusableAccounting.inputTokenCount,
@@ -28787,7 +29115,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
28787
29115
  if (timing)
28788
29116
  timing.identity += performance.now() - identityStart;
28789
29117
  } else {
28790
- const accounting = readAggregateToolAccounting(part, timing, options.toolTokenCache, tokenCacheKey);
29118
+ const accounting = readAggregateToolAccounting(part, timing, options.toolTokenCache, tokenCacheKey, tagger.getToolTagAccounting(sessionId, callId, messageId));
28791
29119
  text = accounting.text;
28792
29120
  const outputByteSize = part.kind === "tool_result" ? accounting.byteSize : 0;
28793
29121
  const outputTokenCount = part.kind === "tool_result" ? accounting.tokenCount : 0;
@@ -28805,6 +29133,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
28805
29133
  occurrences: [{ message, part, kind: part.kind }],
28806
29134
  maxByteSize: persistedAccounting?.byteSize ?? outputByteSize,
28807
29135
  maxTokenCount: persistedAccounting?.tokenCount ?? outputTokenCount,
29136
+ tokenCountKnown: persistedAccounting ? persistedAccounting.tokenCount !== null : true,
28808
29137
  toolName: accounting.toolName,
28809
29138
  inputByteSize: persistedAccounting?.inputByteSize ?? (part.kind === "tool_use" ? accounting.inputByteSize : 0),
28810
29139
  inputTokenCount: persistedAccounting?.inputTokenCount ?? (part.kind === "tool_use" ? firstInputTokenCount : null),
@@ -28859,7 +29188,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
28859
29188
  function syncToolAggregateAccounting(tagger, sessionId, aggregate) {
28860
29189
  tagger.setToolTagAccounting(sessionId, aggregate.tagId, {
28861
29190
  byteSize: aggregate.maxByteSize,
28862
- tokenCount: aggregate.maxTokenCount,
29191
+ tokenCount: aggregate.tokenCountKnown ? aggregate.maxTokenCount : null,
28863
29192
  inputByteSize: aggregate.inputByteSize,
28864
29193
  inputTokenCount: aggregate.inputTokenCount
28865
29194
  });
@@ -28879,16 +29208,19 @@ function applyGrownToolResultAccounting(args) {
28879
29208
  }
28880
29209
  args.aggregate.maxByteSize = args.byteSize;
28881
29210
  args.aggregate.maxTokenCount = tokenCount;
29211
+ args.aggregate.tokenCountKnown = true;
28882
29212
  updateTagByteSize(args.db, args.sessionId, args.aggregate.tagId, args.byteSize);
28883
29213
  updateTagTokenCount(args.db, args.sessionId, args.aggregate.tagId, tokenCount);
28884
29214
  syncToolAggregateAccounting(args.tagger, args.sessionId, args.aggregate);
28885
29215
  }
28886
- function readAggregateToolAccounting(part, timing, tokenCache, tokenCacheKey) {
29216
+ function readAggregateToolAccounting(part, timing, tokenCache, tokenCacheKey, persisted) {
28887
29217
  const text = part.getText() ?? "";
28888
29218
  const byteSize = getToolPartByteSize(part, text);
28889
- const metadata = part.getToolMetadata();
29219
+ const metadata = part.getToolMetadata({
29220
+ skipInputTokenCount: persisted?.inputTokenCount != null
29221
+ });
28890
29222
  let tokenCount = 0;
28891
- if (part.kind === "tool_result") {
29223
+ if (part.kind === "tool_result" && (!persisted || persisted.tokenCount === null || byteSize > persisted.byteSize)) {
28892
29224
  const tokenStart = timing ? performance.now() : 0;
28893
29225
  tokenCount = getCachedToolPartTokenCount(part, text, tokenCache, tokenCacheKey);
28894
29226
  if (timing)
@@ -29273,6 +29605,12 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
29273
29605
  return any ? "truncated" : "absent";
29274
29606
  },
29275
29607
  prepareDrop: () => !occurrences.some((occ) => occ.part.canRemove?.() === "defer"),
29608
+ prepareReplayDrop: () => {
29609
+ if (complete() && !requiresToolArcSkeleton) {
29610
+ for (const occ of occurrences)
29611
+ occ.part.canRemove?.();
29612
+ }
29613
+ },
29276
29614
  canDrop: () => complete() && !occurrences.some((occ) => occ.part.hasUserAnswer?.()),
29277
29615
  requiresToolArcSkeleton,
29278
29616
  readInput() {
@@ -35713,6 +36051,14 @@ async function runPiHistorian(deps) {
35713
36051
  return;
35714
36052
  }
35715
36053
  const protectedTailStart = boundarySnapshot.protectedTailStart;
36054
+ if (!forceDrainQuota && shouldWaitForNewHistorianHistory(boundarySnapshot, {
36055
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery
36056
+ })) {
36057
+ telemetry.status = "noop";
36058
+ telemetry.failureReason = "waiting for new history after no forward progress";
36059
+ sessionLog(sessionId, "historian no-op: waiting for new history after no forward progress");
36060
+ return;
36061
+ }
35716
36062
  const eligibleEndOrdinal = Math.min(boundarySnapshot.eligibleEndOrdinal, protectedTailStart);
35717
36063
  if (protectedTailStart <= offset || eligibleEndOrdinal <= offset) {
35718
36064
  telemetry.status = "noop";
@@ -35900,6 +36246,12 @@ ${chunkText}`,
35900
36246
  };
35901
36247
  retainDrainReservationForRetryThrottle = true;
35902
36248
  const historianEditorSystemPrompt = withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
36249
+ if (!forceDrainQuota) {
36250
+ shouldWaitForNewHistorianHistory(boundarySnapshot, {
36251
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery,
36252
+ claim: true
36253
+ });
36254
+ }
35903
36255
  const firstResult = await runHistorianSubagentWithTransientRetries({
35904
36256
  runner,
35905
36257
  sessionId,
@@ -36058,6 +36410,7 @@ ${chunkText}`,
36058
36410
  const failCount = incrementHistorianFailure(db, sessionId, errorMsg);
36059
36411
  await notify(buildHistorianFailureNotice(failCount, errorMsg));
36060
36412
  }
36413
+ recordHistorianNoProgress(boundarySnapshot, inEmergency);
36061
36414
  rollbackDrainReservation();
36062
36415
  return;
36063
36416
  }
@@ -36151,6 +36504,7 @@ ${chunkText}`,
36151
36504
  }
36152
36505
  db.exec("COMMIT");
36153
36506
  published = true;
36507
+ clearHistorianHistoryWait(sessionId);
36154
36508
  logSlowWriteTransaction("pi_historian_publish", transactionStartedAt);
36155
36509
  } finally {
36156
36510
  if (!published) {
@@ -36873,7 +37227,8 @@ function tagAttribution(input) {
36873
37227
  if (!cached) {
36874
37228
  removed.length = 0;
36875
37229
  added.length = 0;
36876
- added.push(...input.tags);
37230
+ for (const tag of input.tags)
37231
+ added.push(tag);
36877
37232
  cached = {
36878
37233
  tags: input.tags,
36879
37234
  messageTags: new Map,
@@ -37726,7 +38081,11 @@ function mergeChangedRows(rows, dirty, replacements) {
37726
38081
  }
37727
38082
  merged.push(tag);
37728
38083
  }
37729
- merged.push(...replacements.slice(index));
38084
+ for (;index < replacements.length; index += 1) {
38085
+ const row = replacements[index];
38086
+ if (row)
38087
+ merged.push(row);
38088
+ }
37730
38089
  return merged;
37731
38090
  }
37732
38091
  var entryReaders = new WeakMap;
@@ -39853,6 +40212,26 @@ async function executeContextRecompWithResult(deps, options = {}) {
39853
40212
  }
39854
40213
 
39855
40214
  // ../plugin/src/hooks/magic-context/history-boundary-repair.ts
40215
+ function readStoreBoundaries(db, sessionId) {
40216
+ const columns = getTableColumnNames(db, "compartments");
40217
+ const numbers = ["id", "sequence", "start_message", "end_message", "created_at"];
40218
+ const strings = ["session_id", "start_message_id", "end_message_id", "title", "content"];
40219
+ const predicates = [...numbers, ...strings].every((column) => columns.has(column)) ? [
40220
+ ...numbers.map((column) => `typeof(${column}) IN ('integer', 'real')`),
40221
+ ...strings.map((column) => `typeof(${column}) = 'text'`)
40222
+ ] : ["0"];
40223
+ for (const column of ["p1", "p2", "p3", "p4", "episode_type"]) {
40224
+ if (columns.has(column))
40225
+ predicates.push(`typeof(${column}) IN ('text', 'null')`);
40226
+ }
40227
+ for (const column of ["importance", "legacy"]) {
40228
+ if (columns.has(column))
40229
+ predicates.push(`typeof(${column}) IN ('integer', 'real', 'null')`);
40230
+ }
40231
+ return db.prepare(`SELECT sequence, ${columns.has("end_message_id") ? "end_message_id" : "NULL"} AS endMessageId
40232
+ FROM compartments WHERE session_id = ? AND ${predicates.join(" AND ")}
40233
+ ORDER BY sequence ASC`).all(sessionId);
40234
+ }
39856
40235
  function readBaseline(db, sessionId) {
39857
40236
  const row = db.prepare("SELECT cached_m0_bytes IS NOT NULL AS hasM0, cached_m0_last_baseline_end_message_id AS boundary FROM session_meta WHERE session_id = ?").get(sessionId);
39858
40237
  return {
@@ -39918,7 +40297,7 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = f
39918
40297
  }
39919
40298
  function repairMissingHistoryBoundary(args) {
39920
40299
  const { db, sessionId, isInHostStore, scope = "store" } = args;
39921
- const compartments = scope === "active-branch" ? db.prepare("SELECT sequence, COALESCE(end_message_id, '') AS endMessageId FROM compartments WHERE session_id = ? ORDER BY sequence").all(sessionId) : getCompartments(db, sessionId);
40300
+ const compartments = scope === "active-branch" ? db.prepare("SELECT sequence, COALESCE(end_message_id, '') AS endMessageId FROM compartments WHERE session_id = ? ORDER BY sequence").all(sessionId) : readStoreBoundaries(db, sessionId);
39922
40301
  let latestIndex = compartments.length - 1;
39923
40302
  while (latestIndex >= 0 && !compartments[latestIndex]?.endMessageId)
39924
40303
  latestIndex -= 1;
@@ -39962,7 +40341,7 @@ function repairMissingHistoryBoundary(args) {
39962
40341
  let dropped;
39963
40342
  let anchorForDrop = anchor;
39964
40343
  try {
39965
- if (scope === "active-branch" && anchor) {
40344
+ if (anchor) {
39966
40345
  const row = db.prepare("SELECT end_message AS endMessage FROM compartments WHERE session_id = ? AND sequence = ?").get(sessionId, anchor.sequence);
39967
40346
  if (!row)
39968
40347
  return { kind: "unresolved", reason: "busy", missingEndMessageId: missing };
@@ -41533,6 +41912,79 @@ function canRemoveNativeToolCall(message, toolCallId) {
41533
41912
  var NATIVE_TOOL_REMOVAL_MARKER = '{"__magic_context_remove_tool_arc__":true}';
41534
41913
 
41535
41914
  // src/native-replay-state-pi.ts
41915
+ var removalBatches = new WeakMap;
41916
+
41917
+ class PiToolRemovalBatchError extends Error {
41918
+ constructor(cause) {
41919
+ super("Native tool removal batch could not be persisted", { cause });
41920
+ }
41921
+ }
41922
+ function isTransactionOpen(db) {
41923
+ const connection = db;
41924
+ return connection.inTransaction === true || connection.isTransaction === true;
41925
+ }
41926
+ function withPiToolRemovalBatch(args, edit) {
41927
+ let sessions = removalBatches.get(args.db);
41928
+ const enclosing = sessions?.get(args.sessionId);
41929
+ if (enclosing)
41930
+ return edit(enclosing.flush);
41931
+ if (!sessions) {
41932
+ sessions = new Map;
41933
+ removalBatches.set(args.db, sessions);
41934
+ }
41935
+ let publishing = false;
41936
+ const batch = {
41937
+ saved: args.saved,
41938
+ planned: new Map(args.saved),
41939
+ requested: new Map,
41940
+ pending: new Map,
41941
+ flushed: false,
41942
+ flush() {
41943
+ if (batch.flushed)
41944
+ return;
41945
+ batch.persistPending();
41946
+ batch.flushed = true;
41947
+ },
41948
+ persistPending() {
41949
+ if (publishing || batch.pending.size === 0)
41950
+ return;
41951
+ if (!isTransactionOpen(args.db))
41952
+ throw new Error("Native removal publication requires a caller-owned transaction");
41953
+ try {
41954
+ publishing = true;
41955
+ saveNativeToolInputs(args.db, args.sessionId, batch.pending);
41956
+ batch.pending.clear();
41957
+ } catch (cause) {
41958
+ throw new PiToolRemovalBatchError(cause);
41959
+ } finally {
41960
+ publishing = false;
41961
+ }
41962
+ }
41963
+ };
41964
+ sessions.set(args.sessionId, batch);
41965
+ try {
41966
+ const result = withNativeReplayWriteBarrier(args.db, args.sessionId, batch.persistPending, () => edit(batch.flush));
41967
+ if (batch.requested.size > 0 && !batch.flushed)
41968
+ throw new Error("Native removal markers were not flushed in the caller's transaction");
41969
+ const saved = args.saved;
41970
+ if (saved && batch.requested.size > 0) {
41971
+ const prior = new Map([...batch.requested.keys()].map((id) => [id, saved.get(id)]));
41972
+ args.rollbackObservations?.push(() => {
41973
+ for (const [id, value] of prior) {
41974
+ if (value === undefined)
41975
+ saved.delete(id);
41976
+ else
41977
+ saved.set(id, value);
41978
+ }
41979
+ });
41980
+ for (const [id, marker] of batch.requested)
41981
+ saved.set(id, marker);
41982
+ }
41983
+ return result;
41984
+ } finally {
41985
+ sessions.delete(args.sessionId);
41986
+ }
41987
+ }
41536
41988
  function applyNativeToolInputReplayPi(args, saved) {
41537
41989
  if (saved.size === 0 && (!args.canApply || args.changes.size === 0))
41538
41990
  return 0;
@@ -41644,6 +42096,12 @@ function applyNativeReasoningReplayPi(args, saved) {
41644
42096
  function preparePiToolRemovalMeasurements(args) {
41645
42097
  if (!args.saved || !args.canApply)
41646
42098
  return;
42099
+ const batch = removalBatches.get(args.db)?.get(args.sessionId);
42100
+ if (batch?.saved === args.saved) {
42101
+ for (const callId of args.callIds)
42102
+ authorizePiToolRemoval({ ...args, callId });
42103
+ return;
42104
+ }
41647
42105
  const requested = new Map;
41648
42106
  for (const callId of args.callIds) {
41649
42107
  if (args.saved.get(callId) !== NATIVE_TOOL_REMOVAL_MARKER)
@@ -41659,9 +42117,54 @@ function preparePiToolRemovalMeasurements(args) {
41659
42117
  args.saved.set(callId, marker);
41660
42118
  } catch {}
41661
42119
  }
42120
+ function preparePiToolRemovalChecks(args) {
42121
+ if (!args.db || !isTransactionOpen(args.db) || args.rows.length === 0)
42122
+ return;
42123
+ const occurrences = new Map;
42124
+ for (const raw of args.messages) {
42125
+ if (!raw || typeof raw !== "object")
42126
+ continue;
42127
+ const message = raw;
42128
+ if (message.role !== "assistant" || !Array.isArray(message.content))
42129
+ continue;
42130
+ for (const rawPart of message.content) {
42131
+ if (!rawPart || typeof rawPart !== "object")
42132
+ continue;
42133
+ const part = rawPart;
42134
+ if (part.type === "toolCall" && typeof part.id === "string")
42135
+ occurrences.set(part.id, (occurrences.get(part.id) ?? 0) + 1);
42136
+ }
42137
+ }
42138
+ const requested = [];
42139
+ for (const row of args.rows) {
42140
+ if (row.callId === null || occurrences.get(row.callId) !== 1)
42141
+ continue;
42142
+ const target = args.targets.get(row.tagNumber);
42143
+ if (target?.requiresToolArcSkeleton || target?.canDrop?.() !== true)
42144
+ continue;
42145
+ if (!target.mutationParts?.some(({ part }) => part !== null && typeof part === "object" && ("kind" in part) && part.kind === "tool_use" && ("id" in part) && part.id === row.callId))
42146
+ continue;
42147
+ requested.push(row.callId);
42148
+ }
42149
+ if (requested.length)
42150
+ args.prepare(requested);
42151
+ }
41662
42152
  function authorizePiToolRemoval(args) {
41663
42153
  if (!args.saved)
41664
42154
  return false;
42155
+ const batch = removalBatches.get(args.db)?.get(args.sessionId);
42156
+ if (batch?.saved === args.saved) {
42157
+ if (batch.planned.get(args.callId) === NATIVE_TOOL_REMOVAL_MARKER)
42158
+ return true;
42159
+ if (!args.canApply)
42160
+ return false;
42161
+ if (batch.flushed)
42162
+ throw new Error("Tool removal requested after lane publication");
42163
+ batch.planned.set(args.callId, NATIVE_TOOL_REMOVAL_MARKER);
42164
+ batch.requested.set(args.callId, NATIVE_TOOL_REMOVAL_MARKER);
42165
+ batch.pending.set(args.callId, NATIVE_TOOL_REMOVAL_MARKER);
42166
+ return true;
42167
+ }
41665
42168
  if (args.saved.get(args.callId) === NATIVE_TOOL_REMOVAL_MARKER)
41666
42169
  return true;
41667
42170
  if (!args.canApply)
@@ -48029,7 +48532,7 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
48029
48532
  setToolOutput() {
48030
48533
  throw new Error("setToolOutput on assistant part");
48031
48534
  },
48032
- getToolMetadata() {
48535
+ getToolMetadata(options) {
48033
48536
  const current = working[messageIndex].content;
48034
48537
  const p = current[partIndex];
48035
48538
  if (p?.type !== "toolCall") {
@@ -48040,7 +48543,7 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
48040
48543
  try {
48041
48544
  const serialized = JSON.stringify(p.arguments);
48042
48545
  inputByteSize = serialized.length;
48043
- inputTokenCount = serialized ? estimateTokens(serialized) : 0;
48546
+ inputTokenCount = serialized && !options?.skipInputTokenCount ? estimateTokens(serialized) : 0;
48044
48547
  } catch {
48045
48548
  inputByteSize = 0;
48046
48549
  inputTokenCount = 0;
@@ -48473,6 +48976,7 @@ function observePipelineResultForTests(sessionId, result) {
48473
48976
  }
48474
48977
  var variantChangeLogObserverForTests;
48475
48978
  var beforePipelineForTests;
48979
+ var thinkingFitInputObserverForTests;
48476
48980
  function logPiLkgRecovery(sessionId, message) {
48477
48981
  lkgRecoveryLogObserverForTests?.(message);
48478
48982
  sessionLog(sessionId, message);
@@ -48603,6 +49107,7 @@ function buildPiTextIdentityPlan(db, sessionId, tagger, transcript, reuseCandida
48603
49107
  sourceCache.set(tagId, source);
48604
49108
  }
48605
49109
  const driftedMessageIds = new Set;
49110
+ const reusableTextMessageIds = new Set;
48606
49111
  const legacyReminderTagNumbers = new Set;
48607
49112
  for (const [messageId, currentSources] of currentSourcesByMessageId) {
48608
49113
  const legacyRows = legacyRowsByMessageId.get(messageId) ?? [];
@@ -48630,6 +49135,8 @@ function buildPiTextIdentityPlan(db, sessionId, tagger, transcript, reuseCandida
48630
49135
  });
48631
49136
  if (!vectorMatches)
48632
49137
  driftedMessageIds.add(messageId);
49138
+ else if (!messageId.startsWith("pi-msg-") && legacyRows.every((row) => sourceCache.has(row.tagId) && !legacyReminderTagNumbers.has(row.tagId)))
49139
+ reusableTextMessageIds.add(messageId);
48633
49140
  }
48634
49141
  const reusableMessageIds = new Set;
48635
49142
  for (const messageId of reuseCandidates) {
@@ -48640,7 +49147,8 @@ function buildPiTextIdentityPlan(db, sessionId, tagger, transcript, reuseCandida
48640
49147
  driftedMessageIds,
48641
49148
  legacyReminderTagNumbers,
48642
49149
  reusableMessageIds,
48643
- sourceCache
49150
+ sourceCache,
49151
+ reusableTextMessageIds
48644
49152
  };
48645
49153
  }
48646
49154
  var piBranchProjectionBySession = new Map;
@@ -50665,15 +51173,7 @@ function registerPiContextHandler(pi, baseOptions) {
50665
51173
  contextLimit: rawFallbackLimit,
50666
51174
  forcePercentage: forceMaterializationPercentage
50667
51175
  });
50668
- let inputForThinkingFit = (getHarness() !== "pi" || nativeFitPossible) && isPrefixBoundThinkingModel(ctx.model?.provider, ctx.model?.id) ? structuredClone(event.messages) : null;
50669
- let inputForFitReclaim = nativeFitPossible ? inputForThinkingFit ?? structuredClone(event.messages) : undefined;
50670
- if (fitInputStart !== undefined)
50671
- recordPiTransformTiming({
50672
- sessionId,
50673
- stage: "fitInputPreparation",
50674
- elapsedMs: performance.now() - fitInputStart,
50675
- extra: inputForFitReclaim ? "snapshot" : "no-snapshot"
50676
- });
51176
+ const fitPredictionMs = fitInputStart === undefined ? undefined : performance.now() - fitInputStart;
50677
51177
  const pipelineArgs = {
50678
51178
  recoveryPrefix: recovery.prefix,
50679
51179
  recoveryRebuildReason: recovery.rebuildReason,
@@ -50765,7 +51265,25 @@ function registerPiContextHandler(pi, baseOptions) {
50765
51265
  throw new PiLkgAdmissionReplay(replay.messages, "held tag-identity rebuild: replaying the served prefix", "held_identity_repair");
50766
51266
  }
50767
51267
  };
51268
+ const fitCopyStart = fitPredictionMs === undefined ? undefined : performance.now();
51269
+ const thinkingFitHeld = hasActiveAnthropicThinkingTurn(pipelineArgs.messages, pipelineArgs.resolvedProviderID, pipelineArgs.resolvedModelID) && pipelineArgs.reasoningClearing?.prefixBound === true;
51270
+ let inputForThinkingFit = !pipelineArgs.compactionOff && (getHarness() !== "pi" || nativeFitPossible) && isPrefixBoundThinkingModel(ctx.model?.provider, ctx.model?.id) && thinkingFitHeld ? structuredClone(event.messages) : null;
51271
+ let inputForFitReclaim = nativeFitPossible ? inputForThinkingFit ?? structuredClone(event.messages) : undefined;
51272
+ if (fitPredictionMs !== undefined && fitCopyStart !== undefined)
51273
+ recordPiTransformTiming({
51274
+ sessionId,
51275
+ stage: "fitInputPreparation",
51276
+ elapsedMs: fitPredictionMs + (performance.now() - fitCopyStart),
51277
+ extra: inputForFitReclaim ? "snapshot" : "no-snapshot"
51278
+ });
50768
51279
  let result = await budget.waitMandatory(runPipeline(pipelineArgs));
51280
+ thinkingFitInputObserverForTests?.({
51281
+ sessionId,
51282
+ held: thinkingFitHeld,
51283
+ strictThinkingKept: result.strictThinkingKept,
51284
+ thinkingCopy: inputForThinkingFit,
51285
+ fitReclaimCopy: inputForFitReclaim
51286
+ });
50769
51287
  observePipelineResultForTests(sessionId, result);
50770
51288
  logTransformTiming(sessionId, "runPipeline", tRunPipeline);
50771
51289
  if (result.identityRebuilt || result.lkgFrozenReleaseReason)
@@ -52350,6 +52868,21 @@ async function runPipeline(args) {
52350
52868
  try {
52351
52869
  nativeRemovalInputs = getNativeReplayState(args.db, args.sessionId).toolInputs;
52352
52870
  } catch {}
52871
+ const removalBatch = (edit) => withPiToolRemovalBatch({ db: args.db, sessionId: args.sessionId, saved: nativeRemovalInputs }, edit);
52872
+ const applyToolRemovalGroup = (edit) => {
52873
+ const savedRemovalInputs = nativeRemovalInputs;
52874
+ nativeRemovalInputs = savedRemovalInputs && new Map(savedRemovalInputs);
52875
+ try {
52876
+ return args.db.transaction(() => removalBatch((flush) => {
52877
+ const result = edit();
52878
+ flush();
52879
+ return result;
52880
+ })).immediate();
52881
+ } catch (cause) {
52882
+ nativeRemovalInputs = savedRemovalInputs;
52883
+ throw cause instanceof PiToolRemovalBatchError ? cause : new PiToolRemovalBatchError(cause);
52884
+ }
52885
+ };
52353
52886
  const tTranscriptBuild = performance.now();
52354
52887
  const transcript = createPiTranscript(args.messages, args.sessionId, args.messages.map(stableIdResolver), {
52355
52888
  preserveReasoningToolArcs: args.reasoningClearing?.preserveReasoningToolArcs,
@@ -52624,6 +53157,7 @@ async function runPipeline(args) {
52624
53157
  skipPrefixInjection: !ctxReduceCallable,
52625
53158
  entryFingerprintByMessageId,
52626
53159
  reuseMessageIds: textIdentityPlan.reusableMessageIds,
53160
+ reuseTextMessageIds: textIdentityPlan.reusableTextMessageIds,
52627
53161
  textIdentityDriftMessageIds: textIdentityPlan.driftedMessageIds,
52628
53162
  textIdentitySourceCache: textIdentityPlan.sourceCache,
52629
53163
  textTokenCache: tagTextTokenCache,
@@ -52705,17 +53239,34 @@ async function runPipeline(args) {
52705
53239
  });
52706
53240
  }
52707
53241
  if (foldBustsServedPrefixThisPass) {
53242
+ const savedRemovalInputs = nativeRemovalInputs;
52708
53243
  try {
53244
+ nativeRemovalInputs = savedRemovalInputs && new Map(savedRemovalInputs);
52709
53245
  let converted = 0;
52710
- args.db.transaction(() => {
53246
+ args.db.transaction(() => removalBatch((flush) => {
52711
53247
  converted = convertLegacyToolSkeletons(args.db, args.sessionId, targets, {
52712
- keepFullDrop: (callId) => nativeRemovalInputs === undefined || callId !== null && nativeRemovalInputs.get(callId) === NATIVE_TOOL_REMOVAL_MARKER
53248
+ prepareRemovals: (rows) => preparePiToolRemovalChecks({
53249
+ db: args.db,
53250
+ rows,
53251
+ targets,
53252
+ messages: args.messages,
53253
+ prepare: transcript.prepareToolRemovalMeasurements
53254
+ }),
53255
+ keepFullDrop: (callId) => nativeRemovalInputs === undefined || callId !== null && authorizePiToolRemoval({
53256
+ db: args.db,
53257
+ sessionId: args.sessionId,
53258
+ callId,
53259
+ saved: nativeRemovalInputs,
53260
+ canApply: false
53261
+ }) === true
52713
53262
  }).size;
52714
- }).immediate();
53263
+ flush();
53264
+ })).immediate();
52715
53265
  if (converted > 0) {
52716
53266
  sessionLog(args.sessionId, `pi HARD fold converted ${converted} legacy dropped-tool skeleton(s) to real-or-absent`);
52717
53267
  }
52718
53268
  } catch (error) {
53269
+ nativeRemovalInputs = savedRemovalInputs;
52719
53270
  sessionLog(args.sessionId, `pi legacy dropped-tool skeleton conversion failed (kept legacy bytes): ${error instanceof Error ? error.message : String(error)}`);
52720
53271
  }
52721
53272
  }
@@ -52749,13 +53300,13 @@ async function runPipeline(args) {
52749
53300
  pendingDecisionLogObserverForTests?.(pendingDecisionLog);
52750
53301
  try {
52751
53302
  const tApplyPending = performance.now();
52752
- pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, new Set([
53303
+ pendingOpsDidMutate = applyToolRemovalGroup(() => applyPendingOperations(args.sessionId, args.db, newTargets, new Set([
52753
53304
  ...args.contextUsage.percentage >= 95 ? [] : protectedTagNumbersForPass,
52754
53305
  ...protectedToolTags,
52755
53306
  ...identityAdoption.protectedTagNumbers
52756
53307
  ]), pendingOperationTags, pendingOps, [], new Set, (reduction) => droppedTokenReductions.push(reduction), undefined, (eligible) => {
52757
53308
  eligiblePendingDropTagIds = eligible;
52758
- });
53309
+ }));
52759
53310
  if (pendingOpsDidMutate) {
52760
53311
  droppedCount += pendingOps.length;
52761
53312
  }
@@ -52785,7 +53336,26 @@ async function runPipeline(args) {
52785
53336
  const flushedDroppedTags = getDroppedTagsByNumbers(args.db, args.sessionId, targetTagNumbers);
52786
53337
  logTransformTiming(args.sessionId, "getDroppedTagsByNumbers", tGetTags, `targets=${targetTagNumbers.length} fetched=${flushedDroppedTags.length}`);
52787
53338
  const tFlushed = performance.now();
52788
- runPersistedReplayStage("flushed-status-failure", undefined, () => applyFlushedStatuses(args.sessionId, args.db, targets, flushedDroppedTags));
53339
+ runPersistedReplayStage("flushed-status-failure", undefined, () => {
53340
+ const render = () => applyFlushedStatuses(args.sessionId, args.db, targets, flushedDroppedTags);
53341
+ const savedRemovalInputs = nativeRemovalInputs;
53342
+ const needsRemovalPreparation = savedRemovalInputs !== undefined && flushedDroppedTags.some((tag) => tag.type === "tool" && tag.dropMode === "full" && savedRemovalInputs.get(tag.messageId) !== NATIVE_TOOL_REMOVAL_MARKER);
53343
+ if (!isCacheBustingPass || !needsRemovalPreparation)
53344
+ return render();
53345
+ try {
53346
+ nativeRemovalInputs = savedRemovalInputs && new Map(savedRemovalInputs);
53347
+ return args.db.transaction(() => removalBatch((flush) => {
53348
+ prepareFlushedToolRemovals(targets, flushedDroppedTags);
53349
+ flush();
53350
+ return render();
53351
+ })).immediate();
53352
+ } catch (error) {
53353
+ nativeRemovalInputs = savedRemovalInputs;
53354
+ if (error instanceof PiToolRemovalBatchError)
53355
+ return render();
53356
+ throw error;
53357
+ }
53358
+ });
52789
53359
  logTransformTiming(args.sessionId, "applyFlushedStatuses", tFlushed);
52790
53360
  logTransformTiming(args.sessionId, "batchFinalize:flushed", tFlushed);
52791
53361
  const messageIdToMaxTag = buildMessageIdToMaxTag(targets);
@@ -52847,6 +53417,7 @@ async function runPipeline(args) {
52847
53417
  pendingDecisionLogObserverForTests?.(heuristicsDecisionLog);
52848
53418
  }
52849
53419
  if (shouldRunHeuristics && args.heuristics) {
53420
+ const heuristics = args.heuristics;
52850
53421
  try {
52851
53422
  const tHeuristic = performance.now();
52852
53423
  const independentMutationBeforeHeuristics = pendingOpsDidMutate || systemPrefixChangedThisPass || foldBustsServedPrefixThisPass || identityRebuild || firstRenderBust || heldIdleExpiryRebuild;
@@ -52854,7 +53425,7 @@ async function runPipeline(args) {
52854
53425
  clearEmergencyDropSample(args.db, args.sessionId);
52855
53426
  }
52856
53427
  routineCleanupApplied = (emergencyDropEligible ? args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0 : args.sessionMeta.isSubagent || !routinePressureAlreadyApplied) || hasPendingMaterializeSignal || rideSignals.explicitFlush || deferredMaterialize || independentMutationBeforeHeuristics;
52857
- heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
53428
+ heuristicsResult = applyToolRemovalGroup(() => applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
52858
53429
  protectedTools: args.protectedTools,
52859
53430
  prepareToolRemovalMeasurements: transcript.prepareToolRemovalMeasurements,
52860
53431
  protectedToolTags,
@@ -52869,10 +53440,10 @@ async function runPipeline(args) {
52869
53440
  usagePercentage: args.contextUsage.percentage,
52870
53441
  passAlreadyPriced: independentMutationBeforeHeuristics
52871
53442
  } : undefined,
52872
- caveman: args.isSubagent ? undefined : args.heuristics.caveman
52873
- }, activeTags, stableIdResolver);
53443
+ caveman: args.isSubagent ? undefined : heuristics.caveman
53444
+ }, activeTags, stableIdResolver));
52874
53445
  if (!routineCleanupApplied && heuristicsResult.emergencyDroppedTools > 0) {
52875
- const ridingCleanup = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
53446
+ const ridingCleanup = applyToolRemovalGroup(() => applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
52876
53447
  protectedTools: args.protectedTools,
52877
53448
  protectedToolTags,
52878
53449
  protectedTags: args.protectedTags,
@@ -52880,8 +53451,8 @@ async function runPipeline(args) {
52880
53451
  routine: true,
52881
53452
  staleReduceStripEnabled: true,
52882
53453
  onStaleReduceEdit: recordStaleReduceEdit,
52883
- caveman: args.isSubagent ? undefined : args.heuristics.caveman
52884
- }, getActiveTagsBySession(args.db, args.sessionId), stableIdResolver);
53454
+ caveman: args.isSubagent ? undefined : heuristics.caveman
53455
+ }, getActiveTagsBySession(args.db, args.sessionId), stableIdResolver));
52885
53456
  heuristicsResult = {
52886
53457
  droppedTools: heuristicsResult.droppedTools + ridingCleanup.droppedTools,
52887
53458
  deduplicatedTools: heuristicsResult.deduplicatedTools + ridingCleanup.deduplicatedTools,
@@ -52920,6 +53491,8 @@ async function runPipeline(args) {
52920
53491
  }
52921
53492
  logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} staleReduce=${heuristicsResult.droppedStaleReduceCalls} compressedTextTags=${heuristicsResult.compressedTextTags} mutatedTextTags=${heuristicsResult.mutatedTextTags}`);
52922
53493
  } catch (err) {
53494
+ if (err instanceof PiToolRemovalBatchError)
53495
+ throw err;
52923
53496
  sessionLog(args.sessionId, `heuristic cleanup failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
52924
53497
  }
52925
53498
  }
@@ -53056,7 +53629,7 @@ async function runPipeline(args) {
53056
53629
  }
53057
53630
  autoReclaimTargetCount = syntheticPendingOps.length;
53058
53631
  if (syntheticPendingOps.length > 0) {
53059
- autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, protectedTagNumbersForPass, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
53632
+ autoReclaimDidMutate = applyToolRemovalGroup(() => applyPendingOperations(args.sessionId, args.db, newTargets, protectedTagNumbersForPass, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction)));
53060
53633
  if (autoReclaimDidMutate) {
53061
53634
  droppedCount += syntheticPendingOps.length;
53062
53635
  autoReclaimDidMutateThisPass = true;
@@ -58898,17 +59471,17 @@ var FULL_PARAMETER_DESCRIPTIONS = {
58898
59471
  message: "Message ordinal from a <session-history> heading or a mmc_search hit, not a tag number. Returns that one message in full. Use alone."
58899
59472
  },
58900
59473
  mmc_note: {
58901
- action: "write | read | update | dismiss. Defaults to write when content is given, else read.",
59474
+ action: "write | read | update | dismiss.",
58902
59475
  content: "Note text for write/update: first line is the title (under 80 chars), then the detail.",
58903
59476
  filter: "Read filter: active (default), all, dismissed.",
58904
59477
  limit: "Rows per read (default 25).",
58905
59478
  offset: "Skip this many newest rows (default 0).",
58906
- note_ids: "Note ids: one for update, 1–50 for dismiss or read (read returns full bodies). Ignored by write."
59479
+ note_ids: "Note ids: one for update, 1–50 for dismiss, or 1–50 to read those notes in full. Write and a plain read take an empty list."
58907
59480
  },
58908
59481
  mmc_memory: {
58909
59482
  action: "write | update | archive | merge | get",
58910
59483
  content: "The memory text — one standalone fact (write, update, merge).",
58911
- category: "Kind of fact (required for write; on update/merge optional, omitted keeps the current category).",
59484
+ category: "Kind of fact (required for write; on update/merge, pass the memory's current category to keep it).",
58912
59485
  ids: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get.",
58913
59486
  reason: "Why it is being archived (optional)."
58914
59487
  },
@@ -58932,17 +59505,17 @@ var LIGHT_PARAMETER_DESCRIPTIONS = {
58932
59505
  message: "Message ordinal from a <session-history> heading or a mmc_search hit, not a tag number. Returns that one message in full. Use alone."
58933
59506
  },
58934
59507
  mmc_note: {
58935
- action: "write | read | update | dismiss (default: write with content, else read).",
59508
+ action: "write | read | update | dismiss.",
58936
59509
  content: "Note text: first line title (<80 chars), then detail.",
58937
59510
  filter: "Read filter: active (default), all, dismissed.",
58938
59511
  limit: "Rows per read (default 25).",
58939
59512
  offset: "Skip newest rows (default 0).",
58940
- note_ids: "One id for update, 1–50 for dismiss or read (full bodies). Ignored by write."
59513
+ note_ids: "One id for update, 1–50 for dismiss, or 1–50 to read those notes in full. Write and a plain read take an empty list."
58941
59514
  },
58942
59515
  mmc_memory: {
58943
59516
  action: "write | update | archive | merge | get",
58944
59517
  content: "One standalone fact (write, update, merge).",
58945
- category: "Kind of fact (required for write; optional on update/merge).",
59518
+ category: "Kind of fact (required for write; on update/merge pass the current category to keep it).",
58946
59519
  ids: "Ids from <project-memory>: one for update, 1+ for archive, 2+ for merge, 1–20 for get.",
58947
59520
  reason: "Why it is archived (optional)."
58948
59521
  },
@@ -59888,18 +60461,18 @@ function runImmediateTransaction2(db, fn) {
59888
60461
  var PRIMARY_ACTIONS = ["write", "update", "archive", "merge", "get"];
59889
60462
  var GET_MAX_IDS = 20;
59890
60463
  var ParamsShape = {
59891
- action: Optional(Union(PRIMARY_ACTIONS.map((a) => Literal(a)), {
60464
+ action: Union(PRIMARY_ACTIONS.map((a) => Literal(a)), {
59892
60465
  description: "write | update | archive | merge | get"
59893
- })),
59894
- content: Optional(String2({
60466
+ }),
60467
+ content: String2({
59895
60468
  description: "The memory text — one standalone fact (write, update, merge)."
59896
- })),
59897
- category: Optional(Union(V2_MEMORY_CATEGORIES.map((c) => Literal(c)), {
59898
- description: "Kind of fact (required for write; on update/merge optional, omitted keeps the current category)."
59899
- })),
59900
- ids: Optional(_Array_(Number2(), {
60469
+ }),
60470
+ category: Union(V2_MEMORY_CATEGORIES.map((c) => Literal(c)), {
60471
+ description: "Kind of fact (required for write; on update/merge, pass the memory's current category to keep it)."
60472
+ }),
60473
+ ids: _Array_(Number2(), {
59901
60474
  description: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get."
59902
- })),
60475
+ }),
59903
60476
  reason: Optional(String2({
59904
60477
  description: "Why it is being archived (optional)."
59905
60478
  }))
@@ -60337,7 +60910,7 @@ function renderGlance(notes, options) {
60337
60910
  const remaining = ordered.length - options.offset - page.length;
60338
60911
  const footer = remaining > 0 ? `
60339
60912
 
60340
- Showing ${page.length} of ${ordered.length} — ${remaining} older: mmc_note(action="read", offset=${options.offset + page.length})` : "";
60913
+ Showing ${page.length} of ${ordered.length} — ${remaining} older: mmc_note(action="read", content="", note_ids=[], offset=${options.offset + page.length})` : "";
60341
60914
  return `## Notes
60342
60915
 
60343
60916
  ${rows}${footer}`;
@@ -60367,22 +60940,21 @@ function formatWriteReply(noteId, tray, nowMs) {
60367
60940
  // src/tools/ctx-note.ts
60368
60941
  var FILTER_VALUES = ["active", "dismissed", "all"];
60369
60942
  var ParamsSchema2 = _Object_({
60370
- action: Optional(Union([
60943
+ action: Union([
60371
60944
  Literal("write"),
60372
60945
  Literal("read"),
60373
60946
  Literal("dismiss"),
60374
60947
  Literal("update")
60375
60948
  ], {
60376
- description: "write | read | update | dismiss. Defaults to write when content is given, else read."
60377
- })),
60378
- content: Optional(String2({
60949
+ description: "write | read | update | dismiss."
60950
+ }),
60951
+ content: String2({
60379
60952
  description: "Note text for write/update: first line is the title (under 80 chars), then the detail."
60380
- })),
60381
- note_ids: Optional(_Array_(Integer({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER }), {
60382
- minItems: 1,
60953
+ }),
60954
+ note_ids: _Array_(Integer({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER }), {
60383
60955
  maxItems: 50,
60384
- description: "Note ids: one for update, 1–50 for dismiss or read (read returns full bodies). Ignored by write."
60385
- })),
60956
+ description: "Note ids: one for update, 1–50 for dismiss, or 1–50 to read those notes in full. Write and a plain read take an empty list."
60957
+ }),
60386
60958
  filter: Optional(Union(FILTER_VALUES.map((value) => Literal(value)), {
60387
60959
  description: "Read filter: active (default), all, dismissed."
60388
60960
  })),
@@ -60413,7 +60985,7 @@ function captureAnchorOrdinal(db, sessionId) {
60413
60985
  }
60414
60986
  var DISMISS_FOOTER = `
60415
60987
 
60416
- To dismiss a stale note: mmc_note(action="dismiss", note_ids=[N])`;
60988
+ To dismiss a stale note: mmc_note(action="dismiss", note_ids=[N], content="")`;
60417
60989
  function formatDismissResults(results) {
60418
60990
  const dismissedCount = results.filter((result) => result.outcome === "dismissed").length;
60419
60991
  return `Dismissed ${dismissedCount} of ${results.length} notes.
@@ -60463,7 +61035,8 @@ function createCtxNoteTool(deps) {
60463
61035
  });
60464
61036
  const sessionId = ctx.sessionManager.getSessionId();
60465
61037
  const action = params.action ?? (params.content?.trim() ? "write" : "read");
60466
- const noteIds = action === "dismiss" || action === "update" || action === "read" && params.note_ids !== undefined ? parseNoteIds(action, params.note_ids) : undefined;
61038
+ const targetedReadIds = action === "read" && Array.isArray(params.note_ids) && params.note_ids.length === 0 ? undefined : params.note_ids;
61039
+ const noteIds = action === "dismiss" || action === "update" || action === "read" && targetedReadIds !== undefined ? parseNoteIds(action, targetedReadIds) : undefined;
60467
61040
  if (typeof noteIds === "string")
60468
61041
  return err3(noteIds);
60469
61042
  if (action === "write") {
@@ -62272,7 +62845,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
62272
62845
  harness: PI_HARNESS_KIND
62273
62846
  });
62274
62847
  const emptyHistorian = empty.find((chain) => chain.owner === "historian");
62275
- const historianLine = historian ? `registered historian trigger (model=${historian.model}${historian.fallbackModels?.length ? `, fallbacks=${historian.fallbackModels.join(",")}` : ""}, executeThreshold=${formatExecuteThresholdForLog(historian.executeThresholdPercentage)})` : emptyHistorian ? `registered historian trigger: DISABLED (no configured model is registered in Pi: ${formatEmptyPiModelChain(emptyHistorian)})` : undefined;
62848
+ const historianLine = historian ? `registered historian trigger (model=${historian.model}${historian.fallbackModels?.length ? `, fallbacks=${historian.fallbackModels.map(formatPiModelForLog).join(",")}` : ""}, executeThreshold=${formatExecuteThresholdForLog(historian.executeThresholdPercentage)})` : emptyHistorian ? `registered historian trigger: DISABLED (no configured model is registered in Pi: ${formatEmptyPiModelChain(emptyHistorian)})` : undefined;
62276
62849
  if (historianLine && !loggedPiModelChainLines.has(historianLine)) {
62277
62850
  loggedPiModelChainLines.add(historianLine);
62278
62851
  info(historianLine);
@@ -62820,6 +63393,11 @@ function formatExecuteThresholdForLog(value) {
62820
63393
  const base = `${value.default}%`;
62821
63394
  return overrides.length > 0 ? `${base} (${overrides.join(", ")})` : base;
62822
63395
  }
63396
+ function formatPiModelForLog(entry) {
63397
+ if (typeof entry === "string")
63398
+ return entry;
63399
+ return `${entry.model}${entry.qualifier ? ` (thinking_level=${entry.qualifier})` : ""}`;
63400
+ }
62823
63401
  export {
62824
63402
  __test,
62825
63403
  canonicalPiModelKey,