@retaw106/mmc-oc 2.5.0 → 2.7.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.
Files changed (121) hide show
  1. package/dist/{context-xx09sh1g.js → context-9q17e52a.js} +553 -214
  2. package/dist/features/magic-context/auto-search-diagnostics.d.ts +27 -0
  3. package/dist/features/magic-context/auto-search-diagnostics.d.ts.map +1 -0
  4. package/dist/features/magic-context/compaction-marker.d.ts +34 -6
  5. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  6. package/dist/features/magic-context/message-index.d.ts +1 -24
  7. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  8. package/dist/features/magic-context/search.d.ts +4 -0
  9. package/dist/features/magic-context/search.d.ts.map +1 -1
  10. package/dist/features/magic-context/session-decision-calibration.d.ts.map +1 -1
  11. package/dist/features/magic-context/session-retention.fixture.d.ts +4 -0
  12. package/dist/features/magic-context/session-retention.fixture.d.ts.map +1 -0
  13. package/dist/features/magic-context/storage-meta-persisted.d.ts +21 -1
  14. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  15. package/dist/features/magic-context/storage-native-replay.d.ts +2 -0
  16. package/dist/features/magic-context/storage-native-replay.d.ts.map +1 -1
  17. package/dist/features/magic-context/storage-session-tables.d.ts +4 -11
  18. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  19. package/dist/features/magic-context/tool-definition-tokens.d.ts +113 -58
  20. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  21. package/dist/features/magic-context/work-metrics.d.ts +5 -3
  22. package/dist/features/magic-context/work-metrics.d.ts.map +1 -1
  23. package/dist/hooks/magic-context/apply-operations.d.ts +32 -0
  24. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/ask-answer-notice.d.ts +25 -0
  26. package/dist/hooks/magic-context/ask-answer-notice.d.ts.map +1 -0
  27. package/dist/hooks/magic-context/auto-search-observation.d.ts +13 -0
  28. package/dist/hooks/magic-context/auto-search-observation.d.ts.map +1 -0
  29. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +5 -5
  31. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/compaction-marker-publication.d.ts +14 -0
  33. package/dist/hooks/magic-context/compaction-marker-publication.d.ts.map +1 -0
  34. package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts +11 -0
  36. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +11 -3
  39. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/historian-history-wait.d.ts +16 -0
  41. package/dist/hooks/magic-context/historian-history-wait.d.ts.map +1 -0
  42. package/dist/hooks/magic-context/history-boundary-repair.d.ts +1 -1
  43. package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts +3 -1
  47. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/lkg-replay-fit.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/lkg-slot.d.ts +2 -0
  51. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/read-session-db.d.ts +8 -2
  54. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/read-session-raw.d.ts +20 -0
  56. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/signed-thinking-fit-snapshot.d.ts +42 -0
  58. package/dist/hooks/magic-context/signed-thinking-fit-snapshot.d.ts.map +1 -0
  59. package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
  60. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/todo-view.d.ts +2 -2
  62. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  63. package/dist/{index-bpjjcdy9.js → index-8rmt6s4f.js} +77 -31
  64. package/dist/{index-mjf0p7yq.js → index-cgjyhwdk.js} +1 -1
  65. package/dist/{index-k25thxq7.js → index-s3ebcy63.js} +1 -1
  66. package/dist/{index-1vxffj5j.js → index-t0zx3tgz.js} +408 -89
  67. package/dist/{index-0d40b4q8.js → index-xrxpbe74.js} +9355 -9215
  68. package/dist/index.js +14 -4
  69. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  70. package/dist/{read-session-chunk-eb3fy45y.js → read-session-chunk-2s8x6xps.js} +2 -2
  71. package/dist/{recomp-orchestrator-mmpy5cb7.js → recomp-orchestrator-gzd1at06.js} +5 -5
  72. package/dist/{send-session-notification-zpqs213d.js → send-session-notification-vdppp5x6.js} +2 -2
  73. package/dist/shared/search-text.d.ts +2 -0
  74. package/dist/shared/search-text.d.ts.map +1 -1
  75. package/dist/shared/tag-transcript.d.ts +2 -0
  76. package/dist/shared/tag-transcript.d.ts.map +1 -1
  77. package/dist/shared/transcript.d.ts +3 -1
  78. package/dist/shared/transcript.d.ts.map +1 -1
  79. package/dist/tools/ctx-expand/render.d.ts +5 -3
  80. package/dist/tools/ctx-expand/render.d.ts.map +1 -1
  81. package/dist/tools/ctx-expand/tools.d.ts +2 -0
  82. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  83. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  84. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  85. package/dist/tools/parameter-descriptions.d.ts +10 -10
  86. package/dist/tools/parameter-descriptions.d.ts.map +1 -1
  87. package/dist/tui.js +2144 -0
  88. package/dist/v2/hooks/context.d.ts +5 -1
  89. package/dist/v2/hooks/context.d.ts.map +1 -1
  90. package/dist/v2/hooks/store.d.ts.map +1 -1
  91. package/dist/v2/message-times.d.ts +17 -0
  92. package/dist/v2/message-times.d.ts.map +1 -0
  93. package/dist/v2/server.js +11211 -10345
  94. package/dist/v2/store-reader.d.ts +14 -4
  95. package/dist/v2/store-reader.d.ts.map +1 -1
  96. package/dist/v2/tui/client.d.ts +57 -0
  97. package/dist/v2/tui/client.d.ts.map +1 -0
  98. package/dist/v2/tui/context-rpc.d.ts +63 -0
  99. package/dist/v2/tui/context-rpc.d.ts.map +1 -0
  100. package/dist/v2/tui/entry.d.ts +8 -0
  101. package/dist/v2/tui/entry.d.ts.map +1 -0
  102. package/dist/v2/tui/notification-socket.d.ts +26 -0
  103. package/dist/v2/tui/notification-socket.d.ts.map +1 -0
  104. package/dist/v2/tui/session-directory.d.ts +22 -0
  105. package/dist/v2/tui/session-directory.d.ts.map +1 -0
  106. package/dist/v2/tui/types.d.ts +110 -0
  107. package/dist/v2/tui/types.d.ts.map +1 -0
  108. package/dist/web/main.js +85 -11
  109. package/dist/{wrapup-orchestrator-jq589pgr.js → wrapup-orchestrator-ghq4y406.js} +5 -5
  110. package/package.json +13 -3
  111. package/src/config/latch-permanence-guard.test.ts +0 -5
  112. package/src/shared/prompt-surface-a1-golden.md +8 -9
  113. package/src/shared/search-text.test.ts +36 -1
  114. package/src/shared/search-text.ts +48 -0
  115. package/src/shared/sqlite-transaction-mode-guard.test.ts +2 -0
  116. package/src/shared/tag-transcript.ts +47 -4
  117. package/src/shared/transcript.ts +1 -1
  118. package/src/shared/write-transaction-attribution.test.ts +1 -6
  119. package/tui.js +4 -0
  120. package/dist/features/magic-context/message-index-sweep.d.ts +0 -11
  121. package/dist/features/magic-context/message-index-sweep.d.ts.map +0 -1
@@ -71,21 +71,10 @@ import {
71
71
  searchMemoriesFTSUnion,
72
72
  clearMemoryVerifications,
73
73
  queueMemoryMutation,
74
- BoundedSessionMap,
75
- registerOwnRpcServerInstance,
76
- rpcPortDir,
77
- rpcPortFilePath,
78
- parseRpcPortFile,
79
- inspectProcessesAsync,
80
- dirtyLkgRecoveryRevision,
81
- ownsLkgDirtyFence,
82
- invalidateCleanLkgRecoveryFence,
74
+ scheduleAfterBootQuiet,
83
75
  cleanSearchText,
84
- keywordRelevance,
76
+ hintKeywordRelevance,
85
77
  matchedSearchSnippet,
86
- gaDatabasePath,
87
- RAW_MESSAGE_TYPES,
88
- V2StoreReaderPool,
89
78
  getLastIndexedOrdinal,
90
79
  getIndexedMessageIdAtOrdinal,
91
80
  getIndexedMessageCorpusSize,
@@ -93,8 +82,11 @@ import {
93
82
  isMessageIndexReconciledThrough,
94
83
  clearIndexedMessages,
95
84
  indexMessagesAfterOrdinal,
96
- sweepOrphanedOpenCodeMessageIndexesCooperatively,
97
- scheduleAfterBootQuiet,
85
+ registerOwnRpcServerInstance,
86
+ rpcPortDir,
87
+ rpcPortFilePath,
88
+ parseRpcPortFile,
89
+ inspectProcessesAsync,
98
90
  acquireCompartmentLease,
99
91
  releaseCompartmentLeaseBestEffort,
100
92
  isCompartmentLeaseHeld,
@@ -103,7 +95,7 @@ import {
103
95
  ensureSessionMetaRow,
104
96
  clearCachedM0M1,
105
97
  clearCachedM0,
106
- getCompartments,
98
+ getTableColumnNames,
107
99
  getLastCompartmentEndMessage,
108
100
  getLastCompartmentEndMessageId,
109
101
  getAuthorityManagedMarker,
@@ -163,6 +155,7 @@ import {
163
155
  clearEmergencyRecovery,
164
156
  clearDetectedContextLimit,
165
157
  getPersistedCompactionMarkerState,
158
+ clearCompactionMarkerPublicationIntent,
166
159
  setPersistedCompactionMarkerState,
167
160
  getDeferredClearedCompactionMarkerState,
168
161
  retireDeferredClearedCompactionMarkerState,
@@ -191,10 +184,16 @@ import {
191
184
  resolveEpochFloorForPass,
192
185
  providerResponseFailed,
193
186
  isSuccessfulProviderCompletion,
187
+ dirtyLkgRecoveryRevision,
188
+ ownsLkgDirtyFence,
189
+ invalidateCleanLkgRecoveryFence,
194
190
  rememberUnpublishedAutoSearchNoHint,
195
191
  getUnpublishedAutoSearchNoHint,
196
192
  forgetUnpublishedAutoSearchNoHint,
197
193
  registerAutoSearchWork,
194
+ gaDatabasePath,
195
+ RAW_MESSAGE_TYPES,
196
+ V2StoreReaderPool,
198
197
  getOrCreateSessionMeta,
199
198
  updateSessionMeta,
200
199
  advanceToolReclaimWatermark,
@@ -226,6 +225,7 @@ import {
226
225
  lkgDirtyRevision,
227
226
  readLkgRecoveryPrefix,
228
227
  noteLkgHealthyPrefix,
228
+ lkgInputIdentity,
229
229
  beginLkgPass,
230
230
  getInMemorySlot,
231
231
  getSlot,
@@ -277,6 +277,7 @@ import {
277
277
  HISTORIAN_RECOMP_AGENT,
278
278
  HISTORIAN_EDITOR_AGENT,
279
279
  runTokenLog,
280
+ shouldWaitForNewHistorianHistory,
280
281
  extractM0Block,
281
282
  TEMPORAL_MARKER_REPLAY_PATTERN,
282
283
  collectTemporalCandidates,
@@ -307,7 +308,7 @@ import {
307
308
  recordHighPressureNoEligibleHead,
308
309
  getActiveCompartmentRun,
309
310
  startCompartmentAgent
310
- } from "./index-0d40b4q8.js";
311
+ } from "./index-xrxpbe74.js";
311
312
  import {
312
313
  getHarness,
313
314
  getDataDir,
@@ -450,8 +451,9 @@ import {
450
451
  createDroppedInputGuard,
451
452
  assertExecutableToolInput,
452
453
  isEditTool,
454
+ BoundedSessionMap,
455
+ recordToolDefinitionSet,
453
456
  getCurrentToolSetHash,
454
- recordToolDefinition,
455
457
  getMeasuredToolDefinitionTokens,
456
458
  describeFinalWireTail,
457
459
  estimatePartImageTokens,
@@ -487,13 +489,12 @@ import {
487
489
  hasRawSessionMessageById2,
488
490
  readRawSessionMessageById2,
489
491
  readSessionChunk2
490
- } from "./index-1vxffj5j.js";
492
+ } from "./index-t0zx3tgz.js";
491
493
  import {
492
494
  normalizeModelEntry2,
493
495
  resolveHistorianModel2
494
496
  } from "./index-azbmnekv.js";
495
497
  import {
496
- Database,
497
498
  getSqliteMemoryStats,
498
499
  withSqliteBackgroundWriter,
499
500
  withSqliteTransformPass,
@@ -520,7 +521,7 @@ import {
520
521
  latestRealUserTurnIdFromOpenCodeDb,
521
522
  getMessageTimesFromOpenCodeDb,
522
523
  findLastAssistantModelFromOpenCodeDb
523
- } from "./index-bpjjcdy9.js";
524
+ } from "./index-8rmt6s4f.js";
524
525
  import {
525
526
  registerNotificationSink2,
526
527
  pushNotification2,
@@ -533,7 +534,7 @@ import {
533
534
  getErrorMessage,
534
535
  flushIgnoredMessages2,
535
536
  sendStatusNotification2
536
- } from "./index-k25thxq7.js";
537
+ } from "./index-s3ebcy63.js";
537
538
  import {
538
539
  logSlowWriteTransaction
539
540
  } from "./index-yjv8dpht.js";
@@ -6371,6 +6372,26 @@ class EmergencyFailClosedError extends Error {
6371
6372
  }
6372
6373
 
6373
6374
  // src/hooks/magic-context/history-boundary-repair.ts
6375
+ function readStoreBoundaries(db, sessionId) {
6376
+ const columns = getTableColumnNames(db, "compartments");
6377
+ const numbers = ["id", "sequence", "start_message", "end_message", "created_at"];
6378
+ const strings = ["session_id", "start_message_id", "end_message_id", "title", "content"];
6379
+ const predicates = [...numbers, ...strings].every((column) => columns.has(column)) ? [
6380
+ ...numbers.map((column) => `typeof(${column}) IN ('integer', 'real')`),
6381
+ ...strings.map((column) => `typeof(${column}) = 'text'`)
6382
+ ] : ["0"];
6383
+ for (const column of ["p1", "p2", "p3", "p4", "episode_type"]) {
6384
+ if (columns.has(column))
6385
+ predicates.push(`typeof(${column}) IN ('text', 'null')`);
6386
+ }
6387
+ for (const column of ["importance", "legacy"]) {
6388
+ if (columns.has(column))
6389
+ predicates.push(`typeof(${column}) IN ('integer', 'real', 'null')`);
6390
+ }
6391
+ return db.prepare(`SELECT sequence, ${columns.has("end_message_id") ? "end_message_id" : "NULL"} AS endMessageId
6392
+ FROM compartments WHERE session_id = ? AND ${predicates.join(" AND ")}
6393
+ ORDER BY sequence ASC`).all(sessionId);
6394
+ }
6374
6395
  function readBaseline(db, sessionId) {
6375
6396
  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);
6376
6397
  return {
@@ -6436,7 +6457,7 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = f
6436
6457
  }
6437
6458
  function repairMissingHistoryBoundary(args) {
6438
6459
  const { db, sessionId, isInHostStore, scope = "store" } = args;
6439
- 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);
6460
+ 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);
6440
6461
  let latestIndex = compartments.length - 1;
6441
6462
  while (latestIndex >= 0 && !compartments[latestIndex]?.endMessageId)
6442
6463
  latestIndex -= 1;
@@ -6480,7 +6501,7 @@ function repairMissingHistoryBoundary(args) {
6480
6501
  let dropped;
6481
6502
  let anchorForDrop = anchor;
6482
6503
  try {
6483
- if (scope === "active-branch" && anchor) {
6504
+ if (anchor) {
6484
6505
  const row = db.prepare("SELECT end_message AS endMessage FROM compartments WHERE session_id = ? AND sequence = ?").get(sessionId, anchor.sequence);
6485
6506
  if (!row)
6486
6507
  return { kind: "unresolved", reason: "busy", missingEndMessageId: missing };
@@ -6571,6 +6592,38 @@ function seedSessionCacheTtlIfUnsynced(args) {
6571
6592
  return true;
6572
6593
  }
6573
6594
 
6595
+ // src/features/magic-context/auto-search-diagnostics.ts
6596
+ var memoryHintContent = new WeakMap;
6597
+ function withMemoryHintContent(result, fullContent) {
6598
+ memoryHintContent.set(result, fullContent);
6599
+ return result;
6600
+ }
6601
+ function createAutoSearchDiagnostics(threshold) {
6602
+ return {
6603
+ threshold,
6604
+ lanes: {},
6605
+ pool: "unknown",
6606
+ poolRows: null,
6607
+ peek: "not-run",
6608
+ workerStarted: false
6609
+ };
6610
+ }
6611
+ function autoSearchRelevance(query, result) {
6612
+ const content = result.source === "memory" ? memoryHintContent.get(result) ?? result.content : result.content;
6613
+ if (cleanSearchText(content).length === 0)
6614
+ return 0;
6615
+ return result.source === "message" || "matchType" in result ? hintKeywordRelevance(query, content) : result.score;
6616
+ }
6617
+ function recordAutoSearchLane(diagnostics, lane, query, results) {
6618
+ if (!diagnostics)
6619
+ return;
6620
+ const scores = results.map((result) => result.relevanceScore ?? autoSearchRelevance(query, result));
6621
+ diagnostics.lanes[lane] = {
6622
+ top: scores.length ? Math.max(...scores) : null,
6623
+ nearMisses: scores.filter((score) => score >= 0.5 && score < diagnostics.threshold).length
6624
+ };
6625
+ }
6626
+
6574
6627
  // src/features/magic-context/literal-probes.ts
6575
6628
  var MAX_PROBES = 5;
6576
6629
  var MIN_PROBE_LENGTH = 3;
@@ -7043,7 +7096,7 @@ function mergeMemoryResults(args) {
7043
7096
  suppressedVisibleIds.push(id);
7044
7097
  continue;
7045
7098
  }
7046
- results.push({
7099
+ results.push(withMemoryHintContent({
7047
7100
  source: "memory",
7048
7101
  content: previewText(memory.content),
7049
7102
  score,
@@ -7051,7 +7104,7 @@ function mergeMemoryResults(args) {
7051
7104
  category: memory.category,
7052
7105
  matchType: "fts",
7053
7106
  sourceName: args.sourceNameByMemoryId?.get(memory.id)
7054
- });
7107
+ }, memory.content));
7055
7108
  }
7056
7109
  return {
7057
7110
  results: results.sort((left, right) => {
@@ -7648,8 +7701,12 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
7648
7701
  options.diagnostics.suppressedLiveMessageMatches = outcome.suppressedLiveMessageMatches;
7649
7702
  }
7650
7703
  messageResults = outcome.results.filter((result) => result.messageId !== options.excludeMessageId);
7704
+ recordAutoSearchLane(options.autoSearchDiagnostics, "messages", trimmedQuery, messageResults);
7705
+ options.onAutoSearchDiagnostics?.();
7651
7706
  }
7652
7707
  const workspace = resolveSearchWorkspaceContext(db, projectPath);
7708
+ if (options.autoSearchDiagnostics)
7709
+ options.autoSearchDiagnostics.pool = "off";
7653
7710
  const [memoryOutcome, primerResults, noteResults] = await Promise.all([
7654
7711
  runMemory ? searchMemories({
7655
7712
  db,
@@ -7684,6 +7741,10 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
7684
7741
  if (options.diagnostics) {
7685
7742
  options.diagnostics.suppressedVisibleMemoryIds = memoryOutcome.suppressedVisibleIds;
7686
7743
  }
7744
+ recordAutoSearchLane(options.autoSearchDiagnostics, "memory", trimmedQuery, memoryOutcome.results);
7745
+ recordAutoSearchLane(options.autoSearchDiagnostics, "primers", trimmedQuery, primerResults);
7746
+ recordAutoSearchLane(options.autoSearchDiagnostics, "notes", trimmedQuery, noteResults);
7747
+ options.onAutoSearchDiagnostics?.();
7687
7748
  const results = [...memoryOutcome.results, ...primerResults, ...messageResults, ...noteResults].sort(compareUnifiedResults).slice(0, limit);
7688
7749
  const countRetrievals = options.countRetrievals ?? true;
7689
7750
  if (countRetrievals) {
@@ -7705,7 +7766,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
7705
7766
  if (options.autoHint) {
7706
7767
  return results.map((result) => ({
7707
7768
  ...result,
7708
- relevanceScore: cleanSearchText(result.content).length === 0 ? 0 : keywordRelevance(trimmedQuery, result.content)
7769
+ relevanceScore: autoSearchRelevance(trimmedQuery, result)
7709
7770
  }));
7710
7771
  }
7711
7772
  return results;
@@ -8381,6 +8442,32 @@ function selectAutoSearchResults(results, threshold) {
8381
8442
  return results.filter((result) => relevance(result) >= threshold).sort((left, right) => relevance(right) - relevance(left));
8382
8443
  }
8383
8444
 
8445
+ // src/hooks/magic-context/auto-search-observation.ts
8446
+ var disabledTurns = new WeakMap;
8447
+ function logDisabledAutoSearch(db, sessionId, messageId, threshold, startedAt) {
8448
+ const sessions = disabledTurns.get(db) ?? new Map;
8449
+ disabledTurns.set(db, sessions);
8450
+ if (sessions.get(sessionId) === messageId)
8451
+ return;
8452
+ sessions.set(sessionId, messageId);
8453
+ observeAutoSearch(sessionId, threshold, startedAt).finish("disabled");
8454
+ }
8455
+ function observeAutoSearch(sessionId, threshold, startedAt) {
8456
+ const diagnostics = createAutoSearchDiagnostics(threshold);
8457
+ let logged = false;
8458
+ return {
8459
+ diagnostics,
8460
+ finish(outcome, reason) {
8461
+ if (logged)
8462
+ return;
8463
+ logged = true;
8464
+ const lanes = Object.entries(diagnostics.lanes).map(([lane, value]) => `${lane}:${value.top?.toFixed(3) ?? "none"}`).join(",");
8465
+ const nearMisses = Object.values(diagnostics.lanes).reduce((sum, lane) => sum + lane.nearMisses, 0);
8466
+ sessionLog(sessionId, `auto-search: decision outcome=${outcome}${reason ? ` reason=${reason}` : ""} lanes=${lanes || "none"} nearMisses=${nearMisses} threshold=${threshold} pool=${diagnostics.pool} rows=${diagnostics.poolRows ?? "unknown"} workerStarted=${diagnostics.workerStarted ?? "unknown"} peek=${diagnostics.peek} elapsedMs=${Math.round(performance.now() - startedAt)}`);
8467
+ }
8468
+ };
8469
+ }
8470
+
8384
8471
  // src/hooks/magic-context/transform-message-helpers.ts
8385
8472
  function isMeaningfulUserMessage(msg) {
8386
8473
  return msg.info.role === "user" && hasMeaningfulUserText2(msg.parts);
@@ -8571,8 +8658,12 @@ async function coalesceAutoSearchTurn(db, sessionId, messageId, operation, repla
8571
8658
  async function runAutoSearchHint(args) {
8572
8659
  const startedAt = performance.now();
8573
8660
  const { sessionId, db, messages, options } = args;
8574
- if (!options.enabled)
8661
+ if (!options.enabled) {
8662
+ const tail = findLatestMeaningfulUserMessage(messages);
8663
+ if (tail && typeof tail.info.id === "string" && messages.at(-1) === tail)
8664
+ logDisabledAutoSearch(db, sessionId, tail.info.id, options.scoreThreshold, startedAt);
8575
8665
  return AUTO_SEARCH_OK;
8666
+ }
8576
8667
  const userMsg = findLatestMeaningfulUserMessage(messages);
8577
8668
  if (!userMsg || typeof userMsg.info.id !== "string")
8578
8669
  return AUTO_SEARCH_OK;
@@ -8615,6 +8706,7 @@ async function runAutoSearchHint(args) {
8615
8706
  } catch {}
8616
8707
  return AUTO_SEARCH_OK;
8617
8708
  }
8709
+ const observation = observeAutoSearch(sessionId, options.scoreThreshold, startedAt);
8618
8710
  const writeNoHintAndReconcile = (reason) => {
8619
8711
  let outcome;
8620
8712
  try {
@@ -8626,20 +8718,23 @@ async function runAutoSearchHint(args) {
8626
8718
  } catch (error) {
8627
8719
  if (isTransientSqliteError(error)) {
8628
8720
  rememberUnpublishedAutoSearchNoHint(sessionId, userMsgId, reason);
8721
+ observation.finish("error", "storage-busy");
8629
8722
  return { ok: false, kind: "storage-busy" };
8630
8723
  }
8631
8724
  throw error;
8632
8725
  }
8633
- if (!outcome.ok)
8726
+ if (!outcome.ok) {
8727
+ observation.finish("error", "cas-exhausted");
8634
8728
  return { ok: false, kind: "cas-exhaustion" };
8729
+ }
8635
8730
  if (outcome.kind === "already-present" && outcome.decision.decision === "hint") {
8636
8731
  appendReminderToUserMessageById(messages, userMsgId, outcome.decision.text);
8637
8732
  }
8733
+ observation.finish(outcome.decision.decision === "hint" ? "attached" : reason === "below-threshold" ? "below-threshold" : reason === "error" ? "error" : "no-candidates", reason);
8638
8734
  return AUTO_SEARCH_OK;
8639
8735
  };
8640
8736
  const rawPartsText = collectUserPromptParts(userMsg);
8641
8737
  if (hasStackedAugmentation(rawPartsText)) {
8642
- sessionLog(sessionId, "auto-search: skipping — user message already carries augmentation/hint");
8643
8738
  return writeNoHintAndReconcile("stacked");
8644
8739
  }
8645
8740
  const rawPrompt = extractUserPromptText(userMsg);
@@ -8655,6 +8750,7 @@ async function runAutoSearchHint(args) {
8655
8750
  if (checkDeadline())
8656
8751
  return null;
8657
8752
  const searchOptions = {
8753
+ autoSearchDiagnostics: observation.diagnostics,
8658
8754
  limit: 10,
8659
8755
  autoHint: true,
8660
8756
  maxMessageOrdinal: Math.max(0, getLastCompartmentEndMessage(db, sessionId)),
@@ -8668,16 +8764,15 @@ async function runAutoSearchHint(args) {
8668
8764
  countRetrievals: false
8669
8765
  });
8670
8766
  }, startedAt);
8671
- } catch (error) {
8767
+ } catch {
8672
8768
  if (lifecycleSignal.aborted)
8673
8769
  return AUTO_SEARCH_OK;
8674
- log(`[auto-search] unified search failed for session ${sessionId} (no-hint frozen for this turn): ${error instanceof Error ? error.message : String(error)}`);
8675
8770
  const outcome = writeNoHintAndReconcile("error");
8676
8771
  return outcome.ok ? { ok: false, kind: "search-failure" } : outcome;
8677
8772
  }
8678
8773
  if (results === null) {
8679
8774
  persistAutoSearchSkip(db, sessionId, userMsgId, "timeout");
8680
- sessionLog(sessionId, `auto-search: timed out after ${AUTO_SEARCH_TIMEOUT_MS}ms, skipping hint for this turn (frozen)`);
8775
+ observation.finish("timeout");
8681
8776
  return { ok: false, kind: "timeout" };
8682
8777
  }
8683
8778
  if (results.length === 0) {
@@ -8685,7 +8780,6 @@ async function runAutoSearchHint(args) {
8685
8780
  }
8686
8781
  results = selectAutoSearchResults(results, options.scoreThreshold);
8687
8782
  if (results.length === 0) {
8688
- sessionLog(sessionId, `auto-search: no fragment meets relevance threshold ${options.scoreThreshold}`);
8689
8783
  return writeNoHintAndReconcile("below-threshold");
8690
8784
  }
8691
8785
  const hintText = buildAutoSearchHint(results, {
@@ -8704,13 +8798,13 @@ ${hintText}`;
8704
8798
  text: payload
8705
8799
  });
8706
8800
  if (!outcome.ok) {
8707
- sessionLog(sessionId, `auto-search: CAS exhausted for ${userMsgId}; skipping wire append`);
8801
+ observation.finish("error", "cas-exhausted");
8708
8802
  return { ok: false, kind: "cas-exhaustion" };
8709
8803
  }
8710
8804
  if (outcome.decision.decision === "hint") {
8711
8805
  appendReminderToUserMessageById(messages, userMsgId, outcome.decision.text);
8712
8806
  }
8713
- sessionLog(sessionId, `auto-search: attached hint to ${userMsgId} (${results.length} fragments, top score ${results[0].score.toFixed(3)})`);
8807
+ observation.finish(outcome.decision.decision === "hint" ? "attached" : "no-candidates");
8714
8808
  return AUTO_SEARCH_OK;
8715
8809
  }, replayServedDecision);
8716
8810
  }
@@ -11363,6 +11457,8 @@ function reconcileCompactionMode(args) {
11363
11457
  return NO_TRANSITION;
11364
11458
  if (stored === "off_cleanup_pending") {
11365
11459
  const markerCleanup = (args.cleanupMarkers ?? cleanupOffMarkers)(sessionId);
11460
+ if (markerCleanup.verified)
11461
+ clearCompactionMarkerPublicationIntent(db, sessionId);
11366
11462
  return {
11367
11463
  ...NO_TRANSITION,
11368
11464
  recordToWrite: markerCleanup.verified ? "off" : null,
@@ -11377,7 +11473,9 @@ function reconcileCompactionMode(args) {
11377
11473
  if (markerCleanup.removedRows > 0)
11378
11474
  clearedSomething = true;
11379
11475
  if (getPersistedCompactionMarkerState(db, sessionId) !== null) {
11380
- setPersistedCompactionMarkerState(db, sessionId, null);
11476
+ setPersistedCompactionMarkerState(db, sessionId, null, {
11477
+ preservePending: !markerCleanup.verified
11478
+ });
11381
11479
  clearedSomething = true;
11382
11480
  }
11383
11481
  const pendingMarker = getPendingCompactionMarkerState(db, sessionId);
@@ -11920,6 +12018,13 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
11920
12018
  }
11921
12019
  return { shouldFire: false };
11922
12020
  }
12021
+ if (tailInfo.boundarySnapshot && shouldWaitForNewHistorianHistory(tailInfo.boundarySnapshot, {
12022
+ usagePercentage: usage.percentage,
12023
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery
12024
+ })) {
12025
+ sessionLog(sessionId, "compartment trigger: waiting for new history after no forward progress");
12026
+ return { shouldFire: false };
12027
+ }
11923
12028
  const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
11924
12029
  const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff, eligiblePendingDropTagIds);
11925
12030
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
@@ -12104,6 +12209,27 @@ function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, execu
12104
12209
  }) / 100) * historyBudgetPercentage);
12105
12210
  }
12106
12211
 
12212
+ // src/hooks/magic-context/signed-thinking-fit-snapshot.ts
12213
+ function takeSignedThinkingFitSnapshot(args) {
12214
+ let snapshot = null;
12215
+ if (args.held && !args.compactionOff) {
12216
+ try {
12217
+ snapshot = cloneKeepingCoveredInstructionMarks(args.entryOrder);
12218
+ } catch (error) {
12219
+ sessionLog(args.sessionId, `signed thinking fit: could not copy the pass input (${error instanceof Error ? error.message : String(error)}); an over-window held request will be refused rather than retried`);
12220
+ }
12221
+ }
12222
+ snapshotObserver?.({
12223
+ sessionId: args.sessionId,
12224
+ held: args.held,
12225
+ activeAnthropicThinkingTurn: args.activeAnthropicThinkingTurn,
12226
+ compactionOff: args.compactionOff,
12227
+ snapshot
12228
+ });
12229
+ return snapshot;
12230
+ }
12231
+ var snapshotObserver;
12232
+
12107
12233
  // src/hooks/magic-context/temporal-served-projection.ts
12108
12234
  function readLkgTemporalDecisions(db, sessionId, runtime) {
12109
12235
  const slot = getInMemorySlot(sessionId) ?? loadPersistedLkgSlot(db, sessionId);
@@ -12601,6 +12727,14 @@ var RECENT_TOOL_SKELETON_WINDOW = 20;
12601
12727
  function buildReplacementContent(tagId) {
12602
12728
  return `[dropped §${tagId}§]`;
12603
12729
  }
12730
+ 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>))+$/;
12731
+ function renderDroppedText(tagId, renderedText) {
12732
+ const placeholder = buildReplacementContent(tagId);
12733
+ if (renderedText?.startsWith(placeholder))
12734
+ return renderedText;
12735
+ const annotations = renderedText?.match(FROZEN_TEXT_ANNOTATION_SUFFIX)?.[0] ?? "";
12736
+ return placeholder + annotations;
12737
+ }
12604
12738
  function applyNewToolDrop(target, options) {
12605
12739
  const plan = planNewToolDrop(target, options, !!options.persist);
12606
12740
  if (!plan)
@@ -12682,6 +12816,7 @@ function prepareLegacyToolSkeletonConversions(db, sessionId, targets, options =
12682
12816
  AND drop_mode IN ('truncated', 'full')
12683
12817
  ORDER BY tag_number`).all(sessionId);
12684
12818
  const rows = readRows(db);
12819
+ options.prepareRemovals?.(rows);
12685
12820
  const identity = JSON.stringify(rows);
12686
12821
  const converted = new Map;
12687
12822
  for (const row of rows) {
@@ -12721,13 +12856,21 @@ function renderConvertedToolSkeletons(targets, converted) {
12721
12856
  return didMutate;
12722
12857
  }
12723
12858
  var PENDING_OPERATION_CHUNK_SIZE = 64;
12859
+ var PENDING_OPERATION_REJECTED_LOG_CHUNK = 20;
12724
12860
  function applyPendingOperations(sessionId, db, targets, protectedTagIds, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set, onTagReduced, onBatchComplete, onEligibilitySnapshot) {
12725
12861
  let didMutateMessage = false;
12726
12862
  let diagnostics;
12727
- const reject = (reason) => {
12863
+ const reject = (tagNumber, reason, state = {}) => {
12728
12864
  if (!diagnostics)
12729
12865
  return;
12730
12866
  diagnostics.reasons[reason] = (diagnostics.reasons[reason] ?? 0) + 1;
12867
+ const persisted = state.persisted === true;
12868
+ (diagnostics.rejected ??= []).push({
12869
+ tagNumber,
12870
+ reason,
12871
+ persisted,
12872
+ queueState: state.queueState ?? (persisted ? "removed" : "retained")
12873
+ });
12731
12874
  };
12732
12875
  let admitted = false;
12733
12876
  let startedAt = 0;
@@ -12787,29 +12930,39 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12787
12930
  for (const { op: pendingOp, synthetic } of chunk) {
12788
12931
  const tagStatus = tagStatusById.get(pendingOp.tagId);
12789
12932
  if (tagStatus === "compacted" || tagStatus === "dropped") {
12790
- reject("already_reduced");
12933
+ reject(pendingOp.tagId, "already_reduced", {
12934
+ queueState: synthetic ? "none" : "removed"
12935
+ });
12791
12936
  if (!synthetic)
12792
12937
  removePendingOp(db, sessionId, pendingOp.tagId);
12793
12938
  continue;
12794
12939
  }
12795
12940
  if (removedTagIds.has(pendingOp.tagId)) {
12796
- reject("drop_incomplete");
12941
+ reject(pendingOp.tagId, "drop_incomplete", {
12942
+ queueState: synthetic ? "none" : "retained"
12943
+ });
12797
12944
  continue;
12798
12945
  }
12799
12946
  const target = targets.get(pendingOp.tagId);
12800
12947
  const veto = pendingDropVeto(pendingOp.tagId, target, protectedTagIds);
12801
12948
  if (veto) {
12802
- reject(veto);
12949
+ reject(pendingOp.tagId, veto, {
12950
+ queueState: synthetic ? "none" : "retained"
12951
+ });
12803
12952
  continue;
12804
12953
  }
12805
12954
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
12806
12955
  if (synthetic) {
12807
12956
  if (!isToolTag) {
12808
- reject("synthetic_non_tool");
12957
+ reject(pendingOp.tagId, "synthetic_non_tool", {
12958
+ queueState: "none"
12959
+ });
12809
12960
  continue;
12810
12961
  }
12811
12962
  if (target?.canDrop?.() !== true) {
12812
- reject("synthetic_target_not_droppable");
12963
+ reject(pendingOp.tagId, "synthetic_target_not_droppable", {
12964
+ queueState: "none"
12965
+ });
12813
12966
  continue;
12814
12967
  }
12815
12968
  }
@@ -12822,14 +12975,19 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12822
12975
  if (editMarkerTagIds.has(pendingOp.tagId)) {
12823
12976
  const strip = target?.hasAttachments?.() === true;
12824
12977
  if (target?.canDrop?.() === false || target?.prepareDrop?.() === false || !target) {
12825
- reject("edit_marker_incomplete");
12978
+ reject(pendingOp.tagId, "edit_marker_incomplete", {
12979
+ queueState: synthetic ? "none" : "retained"
12980
+ });
12826
12981
  continue;
12827
12982
  }
12828
12983
  persistDrop(strip ? "edit_marker_stripped" : "edit_marker");
12829
12984
  mutations.push(() => {
12830
12985
  const markResult = (strip ? target?.editMarkerStripped?.() : target?.editMarker?.()) ?? "absent";
12831
12986
  if (markResult === "incomplete" || markResult === "absent") {
12832
- reject(`edit_marker_${markResult}`);
12987
+ reject(pendingOp.tagId, `edit_marker_${markResult}`, {
12988
+ persisted: true,
12989
+ queueState: synthetic ? "none" : "removed"
12990
+ });
12833
12991
  return;
12834
12992
  }
12835
12993
  record(true);
@@ -12841,7 +12999,9 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12841
12999
  } else {
12842
13000
  const plan = planNewToolDrop(target, { inWindow: skeletonWindow.has(pendingOp.tagId) }, true, plannedRemovals);
12843
13001
  if (!plan) {
12844
- reject("drop_incomplete");
13002
+ reject(pendingOp.tagId, "drop_incomplete", {
13003
+ queueState: synthetic ? "none" : "retained"
13004
+ });
12845
13005
  continue;
12846
13006
  }
12847
13007
  persistDrop(plan.mode);
@@ -12852,7 +13012,10 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12852
13012
  mutations.push(() => {
12853
13013
  const { result: dropResult, mode: appliedMode } = plan.apply();
12854
13014
  if (dropResult === "incomplete" || synthetic && dropResult !== "removed" && dropResult !== "truncated") {
12855
- reject(`drop_${dropResult}`);
13015
+ reject(pendingOp.tagId, `drop_${dropResult}`, {
13016
+ persisted: true,
13017
+ queueState: synthetic ? "none" : "removed"
13018
+ });
12856
13019
  return;
12857
13020
  }
12858
13021
  if (dropResult === "removed" || dropResult === "truncated") {
@@ -12862,15 +13025,18 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12862
13025
  mode: appliedMode === "full" ? "full" : "truncated"
12863
13026
  });
12864
13027
  } else {
12865
- reject(`drop_${dropResult}`);
13028
+ reject(pendingOp.tagId, `drop_${dropResult}`, {
13029
+ persisted: true,
13030
+ queueState: synthetic ? "none" : "removed"
13031
+ });
12866
13032
  record(false);
12867
13033
  }
12868
13034
  });
12869
13035
  }
12870
13036
  } else if (target) {
12871
- const replacement = buildReplacementContent(pendingOp.tagId);
13037
+ const replacement = renderDroppedText(pendingOp.tagId, target.getContent?.());
12872
13038
  if (target.canSetContent?.(replacement) === false) {
12873
- reject("content_unchanged");
13039
+ reject(pendingOp.tagId, "content_unchanged");
12874
13040
  continue;
12875
13041
  }
12876
13042
  persistDrop();
@@ -12886,12 +13052,18 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12886
13052
  removedCharacters: Math.max(0, originalCharacters - replacement.length)
12887
13053
  });
12888
13054
  } else {
12889
- reject("content_unchanged");
13055
+ reject(pendingOp.tagId, "content_unchanged", {
13056
+ persisted: true,
13057
+ queueState: "removed"
13058
+ });
12890
13059
  }
12891
13060
  });
12892
13061
  } else if (!synthetic) {
12893
- reject("target_absent");
12894
13062
  persistDrop();
13063
+ reject(pendingOp.tagId, "target_absent", {
13064
+ persisted: true,
13065
+ queueState: "removed"
13066
+ });
12895
13067
  mutations.push(() => record(false));
12896
13068
  }
12897
13069
  }
@@ -12918,6 +13090,15 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12918
13090
  const reasons = Object.entries(diagnostics.reasons).sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${count}`).join(",");
12919
13091
  sessionLog(sessionId, `pending operations no-op: source=${diagnostics.source} total=${diagnostics.total} persisted=${diagnostics.persistedWithoutMutation} reasons=${reasons || "none"}`);
12920
13092
  }
13093
+ const rejected = diagnostics.rejected;
13094
+ if (rejected && rejected.length > 0) {
13095
+ const lineCount = Math.ceil(rejected.length / PENDING_OPERATION_REJECTED_LOG_CHUNK);
13096
+ for (let offset = 0;offset < rejected.length; offset += PENDING_OPERATION_REJECTED_LOG_CHUNK) {
13097
+ const entries = rejected.slice(offset, offset + PENDING_OPERATION_REJECTED_LOG_CHUNK).map((entry) => `${entry.tagNumber}:${entry.reason}:${entry.queueState}${entry.persisted ? ":persisted" : ""}`).join(",");
13098
+ const chunkLabel = lineCount > 1 ? ` chunk=${Math.floor(offset / PENDING_OPERATION_REJECTED_LOG_CHUNK) + 1}/${lineCount}` : "";
13099
+ sessionLog(sessionId, `pending operations rejected: source=${diagnostics.source} total=${diagnostics.total} mutated=${diagnostics.mutated} persisted=${diagnostics.persistedWithoutMutation}${chunkLabel} rejected=${entries}`);
13100
+ }
13101
+ }
12921
13102
  }
12922
13103
  return didMutateMessage;
12923
13104
  }
@@ -12958,7 +13139,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
12958
13139
  }
12959
13140
  }
12960
13141
  } else if (target) {
12961
- const changed = target.setContent(buildReplacementContent(tag.tagNumber));
13142
+ const changed = target.setContent(renderDroppedText(tag.tagNumber, target.getContent?.()));
12962
13143
  if (changed)
12963
13144
  didMutateMessage = true;
12964
13145
  }
@@ -16463,7 +16644,7 @@ function createTransform(deps) {
16463
16644
  const startTime = performance.now();
16464
16645
  const historianRun = deps.resolveHistorianRun?.();
16465
16646
  const messages = output.messages;
16466
- const originalForThinkingFit = messages.some((message) => message.parts.some((part) => isRecord(part) && ["reasoning", "thinking", "redacted_thinking"].includes(String(part.type)))) ? cloneKeepingCoveredInstructionMarks(messages) : null;
16647
+ const entryOrderForThinkingFit = messages.slice();
16467
16648
  const coveredHostInstructions = takeCoveredInstructionRows(messages);
16468
16649
  const passOutcome = createPassOutcome();
16469
16650
  const tSessionId = performance.now();
@@ -16478,8 +16659,21 @@ function createTransform(deps) {
16478
16659
  const temporalReplayIds = temporalCandidates ? messages.flatMap((message) => message.info.id ? [message.info.id] : []) : undefined;
16479
16660
  logTransformTiming(sessionId, "findSessionId", tSessionId, `messages=${messages.length}`);
16480
16661
  const tLkgEntry = performance.now();
16481
- const lkgInput = projectEntry(sessionId, messages);
16662
+ const lkgInput = projectEntry(sessionId, entryOrderForThinkingFit);
16482
16663
  const recovery = deps.compactionOff ? {} : readLkgRecoveryPrefix(sessionId, lkgInput, messages, deps.db);
16664
+ const recoveryTailSources = [];
16665
+ if (recovery.prefix) {
16666
+ const priorIdentities = new Set(recovery.prefix.inputs.map((input) => input.id));
16667
+ lkgInput.forEach((input, index) => {
16668
+ const identity = lkgInputIdentity(input.id, input.role, index);
16669
+ if (!identity || priorIdentities.has(identity))
16670
+ return;
16671
+ recoveryTailSources.push({
16672
+ identity,
16673
+ pristineSystem: input.id === null && input.role === "system" ? structuredClone(entryOrderForThinkingFit[index]) : undefined
16674
+ });
16675
+ });
16676
+ }
16483
16677
  logTransformTiming(sessionId, "lkg.entryProjection", tLkgEntry, entryReuse ? `reused=${entryReuse.reused} retained=${entryReuse.retained} retainedBytes=${entryReuse.retainedBytes}` : undefined);
16484
16678
  const resolvedSessionId = sessionId;
16485
16679
  const runNotificationParams = (sid) => {
@@ -16781,6 +16975,13 @@ function createTransform(deps) {
16781
16975
  activeThinkingTurn ||= hasActiveAnthropicThinkingTurn(messages, resolvedProviderID, modelForBudget?.modelID);
16782
16976
  activeThinkingPrefixHold ||= activeThinkingTurn && isPrefixBoundThinkingModel(resolvedProviderID, modelForBudget?.modelID);
16783
16977
  freezeM0M1 ||= activeThinkingPrefixHold;
16978
+ const originalForThinkingFit = takeSignedThinkingFitSnapshot({
16979
+ sessionId,
16980
+ entryOrder: entryOrderForThinkingFit,
16981
+ held: activeThinkingPrefixHold,
16982
+ activeAnthropicThinkingTurn: activeThinkingTurn,
16983
+ compactionOff
16984
+ });
16784
16985
  if (freezeM0M1 && prefixHoldLogTurnIds.get(sessionId) !== currentTurnId) {
16785
16986
  prefixHoldLogTurnIds.set(sessionId, currentTurnId);
16786
16987
  const reason = activeThinkingPrefixHold ? "active thinking turn (prefix-bound model)" : "session directory unresolved";
@@ -16805,7 +17006,8 @@ function createTransform(deps) {
16805
17006
  systemPromptTokens: sessionMeta.systemPromptTokens,
16806
17007
  providerID: modelForBudget?.providerID,
16807
17008
  modelID: modelForBudget?.modelID,
16808
- agentName: runNotificationParams(sessionId)?.agent
17009
+ agentName: runNotificationParams(sessionId)?.agent,
17010
+ sessionId
16809
17011
  });
16810
17012
  const incomingTokens = incoming.messageTokens.conversation + incoming.messageTokens.toolCall;
16811
17013
  if (unmanagedOverWindowCarried || isClearlyOverWindow(incomingTokens, resolvedContextLimit)) {
@@ -17293,7 +17495,8 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17293
17495
  systemPromptTokens: sessionMeta.systemPromptTokens,
17294
17496
  providerID: modelForBudget?.providerID,
17295
17497
  modelID: modelForBudget?.modelID,
17296
- agentName: notificationParams.agent
17498
+ agentName: notificationParams.agent,
17499
+ sessionId
17297
17500
  });
17298
17501
  contextUsage = resolveUnknownUsageFromWireEstimate({
17299
17502
  usage: contextUsage,
@@ -17424,9 +17627,15 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17424
17627
  }
17425
17628
  });
17426
17629
  if (recovery.prefix && !recoveryNormalized) {
17427
- const priorIds = new Set(recovery.prefix.inputs.map((input) => input.id));
17428
- const newIds = new Set(lkgInput.flatMap((input) => input.id && !priorIds.has(input.id) ? [input.id] : []));
17429
- const suffix = messages.filter((message) => message.info.id && newIds.has(message.info.id));
17630
+ const renderedById = new Map;
17631
+ for (const message of messages) {
17632
+ if (!message.info.id)
17633
+ continue;
17634
+ const group = renderedById.get(message.info.id) ?? [];
17635
+ group.push(message);
17636
+ renderedById.set(message.info.id, group);
17637
+ }
17638
+ const suffix = recoveryTailSources.flatMap(({ identity, pristineSystem }) => pristineSystem ? [pristineSystem] : renderedById.get(identity) ?? []);
17430
17639
  messages.splice(0, messages.length, ...JSON.parse(recovery.prefix.json), ...suffix);
17431
17640
  }
17432
17641
  const recoveryReleaseReason = recovery.rebuildReason ?? (recoveryNormalized ? "recovery_tail_normalized" : undefined);
@@ -17443,6 +17652,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17443
17652
  providerID: modelForBudget?.providerID,
17444
17653
  modelID: modelForBudget?.modelID,
17445
17654
  agentName: notificationParams.agent,
17655
+ sessionId,
17446
17656
  systemPromptHash: sessionMeta.systemPromptHash
17447
17657
  }, boundaryContextLimit);
17448
17658
  } catch {
@@ -17457,13 +17667,15 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17457
17667
  providerID: modelForBudget?.providerID,
17458
17668
  modelID: modelForBudget?.modelID,
17459
17669
  agentName: notificationParams.agent,
17670
+ sessionId,
17460
17671
  systemPromptHash: sessionMeta.systemPromptHash
17461
17672
  }, boundaryContextLimit) : undefined);
17462
17673
  if (finalWireEstimate) {
17463
17674
  sessionLog(sessionId, `transform: final-wire telemetry estimate=${finalWireEstimate.tokens} trusted=${finalWireEstimate.trusted} conversation=${finalWireEstimate.messageTokens.conversation} tools=${finalWireEstimate.messageTokens.toolCall} system=${finalWireEstimate.systemTokens} toolDefinitions=${finalWireEstimate.toolDefinitionTokens ?? "unknown"} tail=${finalWireTail}`);
17464
17675
  }
17465
17676
  if ((finalWireEstimate?.refusalGrade || finalWireEstimate?.refusalBasis === "byte-bound" && finalWireEstimate.refusalTokens !== undefined) && boundaryContextLimit > 0 && (finalWireEstimate.refusalTokens ?? finalWireEstimate.tokens) > boundaryContextLimit) {
17466
- if (!thinkingFitRetries.has(sessionId) && activeThinkingPrefixHold && originalForThinkingFit && hasActiveAnthropicThinkingTurn(messages, resolvedProviderID, modelForBudget?.modelID)) {
17677
+ const signedFitRetryWanted = !thinkingFitRetries.has(sessionId) && activeThinkingPrefixHold && hasActiveAnthropicThinkingTurn(messages, resolvedProviderID, modelForBudget?.modelID);
17678
+ if (signedFitRetryWanted && originalForThinkingFit) {
17467
17679
  thinkingFitRetries.add(sessionId);
17468
17680
  requestPendingMaterialization(deps.pendingMaterializationSessions, sessionId, "fit_retry");
17469
17681
  messages.splice(0, messages.length, ...originalForThinkingFit);
@@ -17474,7 +17686,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17474
17686
  }
17475
17687
  return;
17476
17688
  }
17477
- if (thinkingFitRetries.has(sessionId) || postTransformResult.thinkingReleasedForFit)
17689
+ if (signedFitRetryWanted) {
17690
+ sessionLog(sessionId, "signed thinking fit: the held request is over the window and no copy of the pass input exists to retry from; refusing it");
17691
+ }
17692
+ if (signedFitRetryWanted || thinkingFitRetries.has(sessionId) || postTransformResult.thinkingReleasedForFit)
17478
17693
  throw contextRefusalError("The request remains over the model window after stripping signed thinking and reclaiming context");
17479
17694
  }
17480
17695
  if (postTransformResult.prefixTrimStatus === "refused") {
@@ -17483,7 +17698,8 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17483
17698
  systemPromptTokens: sessionMeta.systemPromptTokens,
17484
17699
  providerID: modelForBudget?.providerID,
17485
17700
  modelID: modelForBudget?.modelID,
17486
- agentName: notificationParams.agent
17701
+ agentName: notificationParams.agent,
17702
+ sessionId
17487
17703
  });
17488
17704
  if (untrimmed.tokens > boundaryContextLimit) {
17489
17705
  sessionLog(sessionId, `history boundary unresolved: prefix trim refused and the untrimmed request estimate ${untrimmed.tokens} (trusted=${untrimmed.trusted}) exceeds the context limit ${boundaryContextLimit}; not sending it`);
@@ -17496,7 +17712,8 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17496
17712
  systemPromptTokens: sessionMeta.systemPromptTokens,
17497
17713
  providerID: modelForBudget?.providerID,
17498
17714
  modelID: modelForBudget?.modelID,
17499
- agentName: notificationParams.agent
17715
+ agentName: notificationParams.agent,
17716
+ sessionId
17500
17717
  });
17501
17718
  const servedTokens = served.toolDefinitionsMeasured ? served.rawTokens ?? served.tokens : Math.max(0, sessionMeta.systemPromptTokens) + served.messageTokens.conversation + served.messageTokens.toolCall;
17502
17719
  if (!Number.isFinite(servedTokens) || servedTokens > unmanagedOverWindowPass.limit) {
@@ -17574,7 +17791,8 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17574
17791
  systemPromptTokens: sessionMeta.systemPromptTokens,
17575
17792
  providerID: modelForBudget?.providerID,
17576
17793
  modelID: modelForBudget?.modelID,
17577
- agentName: notificationParams.agent
17794
+ agentName: notificationParams.agent,
17795
+ sessionId
17578
17796
  });
17579
17797
  } catch (error) {
17580
17798
  sessionLog(sessionId, "degraded pass size guard could not estimate:", error);
@@ -17786,6 +18004,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17786
18004
  providerID: modelForBudget?.providerID,
17787
18005
  modelID: modelForBudget?.modelID,
17788
18006
  agentName: notificationParams.agent,
18007
+ sessionId,
17789
18008
  systemPromptHash: sessionMeta.systemPromptHash
17790
18009
  });
17791
18010
  try {
@@ -18257,8 +18476,26 @@ WHERE session_id = ?
18257
18476
  AND data IS NOT NULL
18258
18477
  AND (time_created > ? OR (time_created = ? AND id > ?))
18259
18478
  ORDER BY time_created, id`;
18260
- function readAssistantUsageRowsAfter(openCodeDb, sessionId, afterTimeCreated, afterId) {
18261
- const rows = openCodeDb.prepare(ASSISTANT_USAGE_ROWS_AFTER_SQL).all(sessionId, afterTimeCreated, afterTimeCreated, afterId);
18479
+ var V2_ASSISTANT_USAGE_ROWS_AFTER_SQL = `
18480
+ WITH newer AS (
18481
+ SELECT id, seq, data FROM session_message
18482
+ WHERE session_id = ? AND type = 'assistant' AND data IS NOT NULL AND seq > ?
18483
+ UNION ALL
18484
+ SELECT id, seq, data FROM session_message
18485
+ WHERE session_id = ? AND type = 'assistant' AND data IS NOT NULL AND seq = ? AND id > ?
18486
+ )
18487
+ SELECT
18488
+ json_extract(data, '$.agent') AS agent,
18489
+ seq AS time_created,
18490
+ id AS id,
18491
+ COALESCE(json_extract(data, '$.tokens.input'), 0)
18492
+ + COALESCE(json_extract(data, '$.tokens.cache.read'), 0)
18493
+ + COALESCE(json_extract(data, '$.tokens.cache.write'), 0) AS prompt,
18494
+ COALESCE(json_extract(data, '$.tokens.output'), 0) AS output
18495
+ FROM newer
18496
+ ORDER BY seq, id`;
18497
+ function readAssistantUsageRowsAfter(openCodeDb, sessionId, afterTimeCreated, afterId, generation = "v1") {
18498
+ const rows = openCodeDb.prepare(generation === "v2" ? V2_ASSISTANT_USAGE_ROWS_AFTER_SQL : ASSISTANT_USAGE_ROWS_AFTER_SQL).all(...generation === "v2" ? [sessionId, afterTimeCreated, sessionId, afterTimeCreated, afterId] : [sessionId, afterTimeCreated, afterTimeCreated, afterId]);
18262
18499
  return rows.map((r) => ({
18263
18500
  agent: r.agent ?? null,
18264
18501
  timeCreated: Number(r.time_created ?? 0),
@@ -18267,8 +18504,8 @@ function readAssistantUsageRowsAfter(openCodeDb, sessionId, afterTimeCreated, af
18267
18504
  output: Number(r.output ?? 0)
18268
18505
  }));
18269
18506
  }
18270
- function computeOpenCodeWorkMetricsIncremental(openCodeDb, sessionId, carry) {
18271
- const rows = readAssistantUsageRowsAfter(openCodeDb, sessionId, carry.lastTimeCreated, carry.lastId);
18507
+ function computeOpenCodeWorkMetricsIncremental(openCodeDb, sessionId, carry, generation = "v1") {
18508
+ const rows = readAssistantUsageRowsAfter(openCodeDb, sessionId, carry.lastTimeCreated, carry.lastId, generation);
18272
18509
  if (rows.length === 0) {
18273
18510
  return { carry, metrics: metricsFromCarry(carry) };
18274
18511
  }
@@ -18466,13 +18703,15 @@ function resolveTailHygieneStatus(tsBaseline, rustBaseline) {
18466
18703
  // src/plugin/rpc-handlers.ts
18467
18704
  var workMetricsCarryBySession = new Map;
18468
18705
  function resolveSidebarWorkMetrics(db, sessionId, persistedNewWork, persistedTotalInput) {
18469
- if (!openCodeDbExists()) {
18706
+ const generation = getHarness() === "opencode2" ? "v2" : "v1";
18707
+ if (!openCodeDbExists(generation)) {
18470
18708
  return { newWorkTokens: persistedNewWork, totalInputTokens: persistedTotalInput };
18471
18709
  }
18472
18710
  try {
18473
- const carry = workMetricsCarryBySession.get(sessionId) ?? emptyWorkMetricsCarry();
18474
- const { carry: nextCarry, metrics } = withReadOnlySessionDb((openCodeDb) => computeOpenCodeWorkMetricsIncremental(openCodeDb, sessionId, carry));
18475
- workMetricsCarryBySession.set(sessionId, nextCarry);
18711
+ const carryKey = `${generation}:${sessionId}`;
18712
+ const carry = workMetricsCarryBySession.get(carryKey) ?? emptyWorkMetricsCarry();
18713
+ const { carry: nextCarry, metrics } = withReadOnlySessionDb((openCodeDb) => computeOpenCodeWorkMetricsIncremental(openCodeDb, sessionId, carry, generation), generation);
18714
+ workMetricsCarryBySession.set(carryKey, nextCarry);
18476
18715
  try {
18477
18716
  setSessionWorkMetrics(db, sessionId, metrics.newWorkTokens, metrics.totalInputTokens);
18478
18717
  } catch {}
@@ -18591,7 +18830,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
18591
18830
  if (model) {
18592
18831
  activeProviderID = model.providerID;
18593
18832
  activeModelID = model.modelID;
18594
- measuredToolDefTokens = getMeasuredToolDefinitionTokens(model.providerID, model.modelID, agent) ?? 0;
18833
+ measuredToolDefTokens = getMeasuredToolDefinitionTokens(model.providerID, model.modelID, agent, sessionId) ?? 0;
18595
18834
  }
18596
18835
  }
18597
18836
  const contextLimit = activeProviderID && activeModelID ? resolveContextLimit(activeProviderID, activeModelID, {
@@ -19077,8 +19316,8 @@ function registerRpcHandlers(rpcServer, args) {
19077
19316
  const db = readDatabase();
19078
19317
  if (!db)
19079
19318
  return { ok: false, error: "db unavailable" };
19080
- const { runManagedRecomp } = await import("./recomp-orchestrator-mmpy5cb7.js");
19081
- const { sendIgnoredMessage } = await import("./send-session-notification-zpqs213d.js");
19319
+ const { runManagedRecomp } = await import("./recomp-orchestrator-gzd1at06.js");
19320
+ const { sendIgnoredMessage } = await import("./send-session-notification-vdppp5x6.js");
19082
19321
  log(`[rpc] recomp requested for session ${sessionId}`);
19083
19322
  if (!args.client && !args.hiddenCompletionExecutor) {
19084
19323
  return {
@@ -19127,7 +19366,7 @@ function registerRpcHandlers(rpcServer, args) {
19127
19366
  const db = readDatabase();
19128
19367
  if (!db)
19129
19368
  return { ok: false, error: "db unavailable" };
19130
- const { runManagedWrapup } = await import("./wrapup-orchestrator-jq589pgr.js");
19369
+ const { runManagedWrapup } = await import("./wrapup-orchestrator-ghq4y406.js");
19131
19370
  const model = liveSessionState.liveModelBySession.get(sessionId);
19132
19371
  const contextLimit = model ? resolveContextLimit(model.providerID, model.modelID, { db, sessionID: sessionId }) : 128000;
19133
19372
  const ctx = {
@@ -19822,18 +20061,18 @@ var FULL_PARAMETER_DESCRIPTIONS = {
19822
20061
  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."
19823
20062
  },
19824
20063
  mmc_note: {
19825
- action: "write | read | update | dismiss. Defaults to write when content is given, else read.",
20064
+ action: "write | read | update | dismiss.",
19826
20065
  content: "Note text for write/update: first line is the title (under 80 chars), then the detail.",
19827
20066
  filter: "Read filter: active (default), all, dismissed.",
19828
20067
  limit: "Rows per read (default 25).",
19829
20068
  offset: "Skip this many newest rows (default 0).",
19830
- note_ids: "Note ids: one for update, 1–50 for dismiss or read (read returns full bodies). Ignored by write."
20069
+ 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."
19831
20070
  },
19832
20071
  mmc_memory: {
19833
20072
  action: "write | update | archive | merge | get",
19834
- content: "The memory text — one standalone fact (write, update, merge).",
19835
- category: "Kind of fact (required for write; on update/merge optional, omitted keeps the current category).",
19836
- ids: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get.",
20073
+ content: "The memory text — one standalone fact (write, update, merge). Archive and get take an empty string.",
20074
+ category: "Kind of fact: the new fact's kind for write; on update/merge, the memory's current category to keep it. Archive and get ignore it.",
20075
+ ids: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get. Write takes an empty list.",
19837
20076
  reason: "Why it is being archived (optional)."
19838
20077
  },
19839
20078
  mmc_search: {
@@ -19856,18 +20095,18 @@ var LIGHT_PARAMETER_DESCRIPTIONS = {
19856
20095
  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."
19857
20096
  },
19858
20097
  mmc_note: {
19859
- action: "write | read | update | dismiss (default: write with content, else read).",
20098
+ action: "write | read | update | dismiss.",
19860
20099
  content: "Note text: first line title (<80 chars), then detail.",
19861
20100
  filter: "Read filter: active (default), all, dismissed.",
19862
20101
  limit: "Rows per read (default 25).",
19863
20102
  offset: "Skip newest rows (default 0).",
19864
- note_ids: "One id for update, 1–50 for dismiss or read (full bodies). Ignored by write."
20103
+ 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."
19865
20104
  },
19866
20105
  mmc_memory: {
19867
20106
  action: "write | update | archive | merge | get",
19868
- content: "One standalone fact (write, update, merge).",
19869
- category: "Kind of fact (required for write; optional on update/merge).",
19870
- ids: "Ids from <project-memory>: one for update, 1+ for archive, 2+ for merge, 1–20 for get.",
20107
+ content: "One standalone fact (write, update, merge). Archive and get: empty string.",
20108
+ category: "Kind of fact (write; on update/merge pass the current category to keep it). Archive and get ignore it.",
20109
+ ids: "Ids from <project-memory>: one for update, 1+ for archive, 2+ for merge, 1–20 for get. Write: empty list.",
19871
20110
  reason: "Why it is archived (optional)."
19872
20111
  },
19873
20112
  mmc_search: {
@@ -22427,6 +22666,96 @@ async function createV2HiddenCompletionExecutor(host, options) {
22427
22666
  };
22428
22667
  }
22429
22668
 
22669
+ // src/v2/message-times.ts
22670
+ function isSettled(entry, readAt) {
22671
+ return (entry.version.type === "user" || entry.time.completed !== undefined) && typeof entry.version.updated === "number" && Number.isFinite(entry.version.updated) && entry.version.updated <= readAt - 1000;
22672
+ }
22673
+ function sameVersion(a, b) {
22674
+ return a.type === b.type && a.seq === b.seq && a.created === b.created && a.updated === b.updated;
22675
+ }
22676
+
22677
+ class V2MessageTimesCache {
22678
+ capacity;
22679
+ sessions = new Map;
22680
+ constructor(capacity = 64) {
22681
+ this.capacity = capacity;
22682
+ }
22683
+ delete(sessionID) {
22684
+ this.sessions.delete(sessionID);
22685
+ }
22686
+ deleteMessage(sessionID, messageID) {
22687
+ this.sessions.get(sessionID)?.entries.delete(messageID);
22688
+ }
22689
+ clear() {
22690
+ this.sessions.clear();
22691
+ }
22692
+ pass(sessionID) {
22693
+ const previous = this.sessions.get(sessionID);
22694
+ const current = { entries: new Map };
22695
+ this.sessions.delete(sessionID);
22696
+ this.sessions.set(sessionID, current);
22697
+ while (this.sessions.size > this.capacity) {
22698
+ const oldest = this.sessions.keys().next().value;
22699
+ if (oldest === undefined)
22700
+ break;
22701
+ this.sessions.delete(oldest);
22702
+ }
22703
+ const passTimes = new Map;
22704
+ return (reader, ids) => {
22705
+ const missing = [...new Set(ids)].filter((id) => !passTimes.has(id));
22706
+ if (missing.length > 0) {
22707
+ const readAt = Date.now();
22708
+ const identity = reader.hostIdentity();
22709
+ current.identity = identity;
22710
+ const reusable = previous?.identity === identity ? previous.entries : undefined;
22711
+ const known = [];
22712
+ const refresh = [];
22713
+ let first = Infinity;
22714
+ let last = -Infinity;
22715
+ for (const id of missing) {
22716
+ const entry = reusable?.get(id);
22717
+ if (entry && isSettled(entry, readAt)) {
22718
+ known.push(id);
22719
+ first = Math.min(first, entry.version.seq);
22720
+ last = Math.max(last, entry.version.seq);
22721
+ } else
22722
+ refresh.push(id);
22723
+ }
22724
+ const versions = reader.messageTimeVersions(sessionID, known, { first, last });
22725
+ for (const id of known) {
22726
+ const version = versions.get(id);
22727
+ const cached = reusable?.get(id);
22728
+ if (!version) {
22729
+ passTimes.set(id, null);
22730
+ } else if (cached && sameVersion(cached.version, version) && (version.type === "user" || cached.time.completed !== undefined)) {
22731
+ current.entries.set(id, cached);
22732
+ passTimes.set(id, cached.time);
22733
+ } else
22734
+ refresh.push(id);
22735
+ }
22736
+ const times = reader.messageTimes(sessionID, refresh, versions);
22737
+ for (const id of refresh) {
22738
+ const time = times.get(id);
22739
+ const version = versions.get(id);
22740
+ passTimes.set(id, time ?? null);
22741
+ if (time && version) {
22742
+ const entry = { version, time };
22743
+ if (isSettled(entry, readAt))
22744
+ current.entries.set(id, entry);
22745
+ }
22746
+ }
22747
+ }
22748
+ const result = new Map;
22749
+ for (const id of ids) {
22750
+ const time = passTimes.get(id);
22751
+ if (time)
22752
+ result.set(id, { ...time });
22753
+ }
22754
+ return result;
22755
+ };
22756
+ }
22757
+ }
22758
+
22430
22759
  // src/v2/hooks/channel2.ts
22431
22760
  import { randomBytes as randomBytes2 } from "node:crypto";
22432
22761
  var pendingCandidates = new WeakMap;
@@ -23365,7 +23694,7 @@ function projectRawMessages(rows, ordinalFor) {
23365
23694
  const message = {
23366
23695
  id: row.id,
23367
23696
  ordinal: ordinalFor(row, index),
23368
- role: row.type === "assistant" ? "assistant" : "user",
23697
+ role: row.type === "assistant" ? "assistant" : row.type === "system" ? "system" : "user",
23369
23698
  createdAt: row.time_created ?? row.data.time?.created,
23370
23699
  parts: row.type === "assistant" ? (row.data.content ?? []).map((part) => {
23371
23700
  if (part.type !== "tool")
@@ -23741,6 +24070,17 @@ function resolveCtxExpandMode(args, domain) {
23741
24070
 
23742
24071
  // src/tools/ctx-expand/render.ts
23743
24072
  import { createHash as createHash5 } from "node:crypto";
24073
+ function recoveryParts(sessionId, message, attachments) {
24074
+ return attachments ? [...message.parts, ...attachments(sessionId, message.id)] : message.parts;
24075
+ }
24076
+ function attachmentNotice(part) {
24077
+ const field = (value) => typeof value === "string" ? value.trim().replace(/[\r\n]+/g, " ") : "";
24078
+ const source = isRecord4(part.source) ? part.source : {};
24079
+ const location = [part.url, part.path, source.uri, source.url, source.path].map(field).find((value) => value && !/^(data|blob):/i.test(value));
24080
+ const name = field(part.filename) || field(part.name);
24081
+ const mime = field(part.mime) || field(part.mimeType) || field(part.mediaType);
24082
+ return `[file]${name ? ` ${name}` : ""}${mime ? ` (${mime})` : ""}` + `${location ? ` — stored location: ${location}` : ""}` + "; content not recovered. If needed, ask the user to attach it again.";
24083
+ }
23744
24084
  function isRecord4(value) {
23745
24085
  return value !== null && typeof value === "object" && !Array.isArray(value);
23746
24086
  }
@@ -23847,8 +24187,8 @@ function renderPartPreview(part, expandTools) {
23847
24187
  if (reasoning !== null)
23848
24188
  return ` • [reasoning] ${truncate2(reasoning, 120)}`;
23849
24189
  const type = typeof part.type === "string" ? part.type : "part";
23850
- if (type === "file")
23851
- return " • [file]";
24190
+ if (type === "file" || type === "image")
24191
+ return ` • ${attachmentNotice(part)}`;
23852
24192
  if (type === "step-start" || type === "step-finish")
23853
24193
  return null;
23854
24194
  return ` • [${type}]`;
@@ -23878,13 +24218,11 @@ ${tool.output}`);
23878
24218
  `);
23879
24219
  }
23880
24220
  const type = typeof part.type === "string" ? part.type : "part";
23881
- if (type === "file") {
23882
- const name = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
23883
- return ` [file]${name ? ` ${name}` : ""}`;
23884
- }
24221
+ if (type === "file" || type === "image")
24222
+ return ` ${attachmentNotice(part)}`;
23885
24223
  return null;
23886
24224
  }
23887
- function renderMessageByOrdinal(sessionId, ordinal) {
24225
+ function renderMessageByOrdinal(sessionId, ordinal, attachments) {
23888
24226
  const found = [];
23889
24227
  visitRawSessionMessages2(sessionId, ordinal, ordinal, (m) => {
23890
24228
  if (m.ordinal === ordinal)
@@ -23895,7 +24233,7 @@ function renderMessageByOrdinal(sessionId, ordinal) {
23895
24233
  if (!msg) {
23896
24234
  return `No message at ordinal ${ordinal} is currently in this session's stored history — ` + `it may have been removed by a session prune/revert, or the ordinal may be wrong. ` + `Re-run the tool if you still need the data.`;
23897
24235
  }
23898
- const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
24236
+ const rendered = recoveryParts(sessionId, msg, attachments).map(renderPartFull).filter((l) => l !== null);
23899
24237
  const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
23900
24238
  if (rendered.length === 0) {
23901
24239
  lines.push(" (no recoverable content — message had only structural/reasoning parts)");
@@ -23905,14 +24243,14 @@ function renderMessageByOrdinal(sessionId, ordinal) {
23905
24243
  return lines.join(`
23906
24244
  `);
23907
24245
  }
23908
- function renderVerboseRange(sessionId, start, end, tokenBudget, expandTools) {
24246
+ function renderVerboseRange(sessionId, start, end, tokenBudget, expandTools, attachments) {
23909
24247
  const out = [];
23910
24248
  let usedTokens = 0;
23911
24249
  let lastOrdinal = start - 1;
23912
24250
  let truncated = false;
23913
24251
  visitRawSessionMessages2(sessionId, start, end, (msg) => {
23914
24252
  const header = `[${msg.ordinal}] ${verboseRoleLabel(msg)}`;
23915
- const partLines = msg.parts.map((part) => renderPartPreview(part, expandTools)).filter((l) => l !== null);
24253
+ const partLines = recoveryParts(sessionId, msg, attachments).map((part) => renderPartPreview(part, expandTools)).filter((l) => l !== null);
23916
24254
  const block = partLines.length > 0 ? `${header}
23917
24255
  ${partLines.join(`
23918
24256
  `)}` : header;
@@ -23930,7 +24268,23 @@ ${partLines.join(`
23930
24268
 
23931
24269
  `), lastOrdinal, truncated };
23932
24270
  }
23933
- function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
24271
+ function renderVerboseRangeForTool(sessionId, start, end, tokenBudget, expandTools, attachments) {
24272
+ const v = renderVerboseRange(sessionId, start, end, tokenBudget, expandTools, attachments);
24273
+ if (!v.text) {
24274
+ return `No messages found in range ${start}-${end}. The range may be outside this session's history.`;
24275
+ }
24276
+ const out = [
24277
+ `Messages ${start}-${v.lastOrdinal} (verbose). Recover any one in full with mmc_expand(message=<ordinal>):`,
24278
+ "",
24279
+ v.text
24280
+ ];
24281
+ if (v.truncated) {
24282
+ out.push("", `Truncated at message ${v.lastOrdinal} (budget: ~${tokenBudget} tokens). Call again with start=${v.lastOrdinal + 1} end=${end} verbose=true for more.`);
24283
+ }
24284
+ return out.join(`
24285
+ `);
24286
+ }
24287
+ function renderItemByTag(db, sessionId, number, textIndexDomain = "part", attachments) {
23934
24288
  const tag = getTagById(db, sessionId, number);
23935
24289
  if (!tag)
23936
24290
  return `no tag ${number} in this session; if ${number} came from a <session-history> heading or a ctx_search hit, it is an ordinal: use message=${number}`;
@@ -23955,17 +24309,19 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
23955
24309
  return rendered.length ? rendered.join(`
23956
24310
  `) : `Tag ${number}'s original tool call is no longer in stored history.`;
23957
24311
  }
23958
- const scoped = /^(.*):p(\d+)$/.exec(tag.messageId);
24312
+ const scoped = /^(.*):(?:p|file)(\d+)$/.exec(tag.messageId);
23959
24313
  const derived = /^(.*):mc-text-v1:([a-f0-9]+):([a-f0-9]+):o(\d+)$/.exec(tag.messageId);
23960
24314
  const owner = scoped?.[1] ?? derived?.[1] ?? tag.messageId;
23961
24315
  const message = readRawSessionMessageById2(sessionId, owner);
23962
24316
  if (!message)
23963
24317
  return `Tag ${number}'s original text is no longer in stored history.`;
23964
24318
  const index = scoped ? Number(scoped[2]) : 0;
23965
- const piText = message.parts.filter((part) => isRecord4(part) && part.type === "text");
23966
- const part = message.parts[index];
24319
+ const attached = attachments?.(sessionId, message.id) ?? [];
24320
+ const parts = [...message.parts, ...attached];
24321
+ const piText = parts.filter((part) => isRecord4(part) && part.type === "text");
24322
+ const part = tag.type === "file" ? attached.find((part) => isRecord4(part) && part.partIndex === index) ?? message.parts[index] : parts[index];
23967
24323
  const matching = derived ? piText.filter((part) => isRecord4(part) && typeof part.text === "string" && createHash5("sha256").update(part.text).digest("hex") === derived[3]) : [];
23968
- const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" ? piText[index] : part;
24324
+ const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" && tag.type !== "file" ? piText[index] : part;
23969
24325
  if (!selected)
23970
24326
  return `Tag ${number}'s original text part is no longer in stored history.`;
23971
24327
  const renderedPart = renderPartFull(selected);
@@ -24053,10 +24409,10 @@ function createCtxExpandTool(deps) {
24053
24409
  return mode.message;
24054
24410
  }
24055
24411
  if (mode.kind === "tag") {
24056
- return renderItemByTag(deps.db, sessionId, mode.tag);
24412
+ return renderItemByTag(deps.db, sessionId, mode.tag, "part", deps.recoveryAttachments);
24057
24413
  }
24058
24414
  if (mode.kind === "message") {
24059
- return renderSearchArchive(deps.db, sessionId, mode.message, mode.message, CTX_EXPAND_TOKEN_BUDGET) ?? renderMessageByOrdinal(sessionId, mode.message);
24415
+ return renderSearchArchive(deps.db, sessionId, mode.message, mode.message, CTX_EXPAND_TOKEN_BUDGET) ?? renderMessageByOrdinal(sessionId, mode.message, deps.recoveryAttachments);
24060
24416
  }
24061
24417
  const { start, end, verbose } = mode;
24062
24418
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
@@ -24068,20 +24424,7 @@ function createCtxExpandTool(deps) {
24068
24424
  if (archived)
24069
24425
  return archived;
24070
24426
  if (verbose) {
24071
- const v = renderVerboseRange(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET, deps.expandTools);
24072
- if (!v.text) {
24073
- return `No messages found in range ${start}-${effectiveEnd}. The range may be outside this session's history.`;
24074
- }
24075
- const out = [
24076
- `Messages ${start}-${v.lastOrdinal} (verbose). Recover any one in full with mmc_expand(message=<ordinal>):`,
24077
- "",
24078
- v.text
24079
- ];
24080
- if (v.truncated) {
24081
- out.push("", `Truncated at message ${v.lastOrdinal} (budget: ~${CTX_EXPAND_TOKEN_BUDGET} tokens). Call again with start=${v.lastOrdinal + 1} end=${effectiveEnd} verbose=true for more.`);
24082
- }
24083
- return out.join(`
24084
- `);
24427
+ return renderVerboseRangeForTool(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET, deps.expandTools, deps.recoveryAttachments);
24085
24428
  }
24086
24429
  const chunk = readSessionChunk2(sessionId, CTX_EXPAND_TOKEN_BUDGET, start, effectiveEnd + 1, { expand: false });
24087
24430
  if (!chunk.text || chunk.messageCount === 0) {
@@ -24296,10 +24639,10 @@ function updateMemoryContentInCurrentTransaction(db, memory, content, normalized
24296
24639
  clearMemoryVerifications(db, memory.id);
24297
24640
  }
24298
24641
  var ctxMemoryArgsShape = {
24299
- action: tool2.schema.enum([...CTX_MEMORY_ACTIONS]).optional().describe("write | update | archive | merge | get"),
24300
- content: tool2.schema.string().optional().describe("The memory text — one standalone fact (write, update, merge)."),
24301
- category: tool2.schema.enum([...V2_MEMORY_CATEGORIES]).optional().describe("Kind of fact (required for write; on update/merge optional, omitted keeps the current category)."),
24302
- ids: tool2.schema.array(tool2.schema.number()).optional().describe("Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get."),
24642
+ action: tool2.schema.enum([...CTX_MEMORY_ACTIONS]).describe("write | update | archive | merge | get"),
24643
+ content: tool2.schema.string().describe("The memory text — one standalone fact (write, update, merge)."),
24644
+ category: tool2.schema.enum([...V2_MEMORY_CATEGORIES]).describe("Kind of fact (required for write; on update/merge, pass the memory's current category to keep it)."),
24645
+ ids: tool2.schema.array(tool2.schema.number()).describe("Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get."),
24303
24646
  reason: tool2.schema.string().optional().describe("Why it is being archived (optional).")
24304
24647
  };
24305
24648
  var ctxMemoryArgsSchema = tool2.schema.object({ ...ctxMemoryArgsShape }).passthrough();
@@ -24657,7 +25000,7 @@ function renderGlance(notes, options) {
24657
25000
  const remaining = ordered.length - options.offset - page.length;
24658
25001
  const footer = remaining > 0 ? `
24659
25002
 
24660
- Showing ${page.length} of ${ordered.length} — ${remaining} older: mmc_note(action="read", offset=${options.offset + page.length})` : "";
25003
+ Showing ${page.length} of ${ordered.length} — ${remaining} older: mmc_note(action="read", content="", note_ids=[], offset=${options.offset + page.length})` : "";
24661
25004
  return `## Notes
24662
25005
 
24663
25006
  ${rows}${footer}`;
@@ -24695,7 +25038,7 @@ function captureAnchorOrdinal(db, sessionId) {
24695
25038
  }
24696
25039
  var DISMISS_FOOTER = `
24697
25040
 
24698
- To dismiss a stale note: mmc_note(action="dismiss", note_ids=[N])`;
25041
+ To dismiss a stale note: mmc_note(action="dismiss", note_ids=[N], content="")`;
24699
25042
  var DEFAULT_READ_LIMIT = 25;
24700
25043
  function readGlanceNotes(args) {
24701
25044
  if (args.filter === undefined) {
@@ -24725,12 +25068,12 @@ function noteAuthorityRefusal(action) {
24725
25068
  return renderCapabilityRefusal(isMutation ? "note_change" : "note_access");
24726
25069
  }
24727
25070
  var ctxNoteArgsShape = {
24728
- action: tool3.schema.enum(["write", "read", "dismiss", "update"]).optional().describe("write | read | update | dismiss. Defaults to write when content is given, else read."),
24729
- content: tool3.schema.string().optional().describe("Note text for write/update: first line is the title (under 80 chars), then the detail."),
25071
+ action: tool3.schema.enum(["write", "read", "dismiss", "update"]).describe("write | read | update | dismiss."),
25072
+ content: tool3.schema.string().describe("Note text for write/update: first line is the title (under 80 chars), then the detail."),
24730
25073
  filter: tool3.schema.enum(["all", "active", "dismissed"]).optional().describe("Read filter: active (default), all, dismissed."),
24731
25074
  limit: tool3.schema.number().optional().describe("Rows per read (default 25)."),
24732
25075
  offset: tool3.schema.number().optional().describe("Skip this many newest rows (default 0)."),
24733
- note_ids: tool3.schema.array(tool3.schema.number().int().min(1)).min(1).max(50).optional().describe("Note ids: one for update, 1–50 for dismiss or read (read returns full bodies). Ignored by write.")
25076
+ note_ids: tool3.schema.array(tool3.schema.number().int().min(1)).max(50).describe("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.")
24734
25077
  };
24735
25078
  var ctxNoteArgsSchema = tool3.schema.object(ctxNoteArgsShape).passthrough();
24736
25079
  function formatDismissResults(results) {
@@ -24769,7 +25112,8 @@ function createCtxNoteTool(deps) {
24769
25112
  });
24770
25113
  const sessionId = toolContext.sessionID;
24771
25114
  const action = args.action ?? (args.content?.trim() ? "write" : "read");
24772
- const noteIds = action === "dismiss" || action === "update" || action === "read" && args.note_ids !== undefined ? parseNoteIds(action, args.note_ids) : undefined;
25115
+ const targetedReadIds = action === "read" && Array.isArray(args.note_ids) && args.note_ids.length === 0 ? undefined : args.note_ids;
25116
+ const noteIds = action === "dismiss" || action === "update" || action === "read" && targetedReadIds !== undefined ? parseNoteIds(action, targetedReadIds) : undefined;
24773
25117
  if (typeof noteIds === "string")
24774
25118
  return noteIds;
24775
25119
  const projectIdentity = deps.resolveProjectPath?.(toolContext.directory);
@@ -25511,16 +25855,23 @@ function applyV2PromptSurfaceTools(draft, runtime, config) {
25511
25855
  function recordV2ToolDefinitions(draft) {
25512
25856
  if (!draft.tools)
25513
25857
  return;
25858
+ const tools = [];
25514
25859
  for (const [id, tool] of Object.entries(draft.tools)) {
25515
25860
  if (!tool)
25516
25861
  continue;
25517
25862
  recordToolParameters(id, tool.input);
25518
- recordToolDefinition(draft.model.providerID, draft.model.id, draft.agent, id, typeof tool.description === "string" ? tool.description : "", tool.input);
25863
+ tools.push({
25864
+ id,
25865
+ description: typeof tool.description === "string" ? tool.description : "",
25866
+ parameters: tool.input
25867
+ });
25519
25868
  }
25869
+ recordToolDefinitionSet(draft.model.providerID, draft.model.id, draft.agent, tools, draft.sessionID);
25520
25870
  }
25521
25871
  async function registerContext(context) {
25522
25872
  const directory = context.location.directory;
25523
25873
  const config = loadPluginConfigDetailed(directory).config;
25874
+ log(`[magic-context] OpenCode 2 keep_subagents=${config.keep_subagents === true}`);
25524
25875
  setHomeProjectPermission(config.allow_home_project);
25525
25876
  if (!config.enabled)
25526
25877
  return;
@@ -25642,6 +25993,7 @@ async function registerContext(context) {
25642
25993
  };
25643
25994
  let hiddenAgentsReady;
25644
25995
  const readerPool = new V2StoreReaderPool;
25996
+ const messageTimesCache = new V2MessageTimesCache;
25645
25997
  const openStoreReader = () => readerPool.open(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"));
25646
25998
  const createHiddenExecutor = async (database) => {
25647
25999
  await cleanupLegacyOnce(database);
@@ -25762,17 +26114,11 @@ async function registerContext(context) {
25762
26114
  }
25763
26115
  });
25764
26116
  const pagedRead = createV2RawMessageReader(openStoreReader);
25765
- let orphanSweep;
25766
- let orphanSweepTimer;
25767
26117
  const prepareHistorySession = (sessionID) => {
25768
26118
  if (!rawProviders.has(sessionID))
25769
26119
  rawProviders.set(sessionID, setBoundedRawMessageProvider2(sessionID, createV2RawMessageProvider(pagedRead, sessionID)));
25770
26120
  };
25771
26121
  if (db && isDatabasePersisted(db)) {
25772
- const sweepDb = db;
25773
- orphanSweepTimer = scheduleAfterBootQuiet(() => {
25774
- orphanSweep = sweepOrphanedOpenCodeMessageIndexesCooperatively(sweepDb, () => new Database(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"), { readonly: true, fileMustExist: true })).catch((error) => log("[magic-context] v2 orphan sweep failed:", error));
25775
- });
25776
26122
  const backfillDb = db;
25777
26123
  scheduleAfterBootQuiet(() => {
25778
26124
  runV2SessionProjectBackfill(backfillDb, openStoreReader, config.allow_home_project).catch((error) => log("[session-project-backfill] OpenCode 2 backfill failed:", error));
@@ -25912,6 +26258,12 @@ async function registerContext(context) {
25912
26258
  if (event.type?.startsWith("session.revert.") || event.type === "session.message.content.updated") {
25913
26259
  nativeFoldReplay?.onEvent(event);
25914
26260
  restoredRows.forget(sessionID);
26261
+ messageTimesCache.delete(sessionID);
26262
+ }
26263
+ if (event.type === "session.step.started" || event.type === "session.step.ended" || event.type === "session.step.failed") {
26264
+ const messageID = event.data.assistantMessageID;
26265
+ if (typeof messageID === "string")
26266
+ messageTimesCache.deleteMessage(sessionID, messageID);
25915
26267
  }
25916
26268
  if (event.type === "session.revert.committed") {
25917
26269
  committedSourceRefreshes.set(sessionID, {});
@@ -25951,6 +26303,7 @@ async function registerContext(context) {
25951
26303
  rawProviders.get(sessionID)?.();
25952
26304
  rawProviders.delete(sessionID);
25953
26305
  usage.delete(sessionID);
26306
+ messageTimesCache.delete(sessionID);
25954
26307
  hiddenSessionErrors.delete(sessionID);
25955
26308
  storageNoticeBySession.delete(sessionID);
25956
26309
  liveModels.delete(sessionID);
@@ -26022,7 +26375,7 @@ async function registerContext(context) {
26022
26375
  memoryInjectionBudgetTokens: config.memory.injection_budget_tokens,
26023
26376
  hardSignals: {
26024
26377
  systemHash: foldDigest(JSON.stringify(draft.system)),
26025
- toolSetHash: getCurrentToolSetHash(draft.model.providerID, draft.model.id, draft.agent),
26378
+ toolSetHash: getCurrentToolSetHash(draft.model.providerID, draft.model.id, draft.agent, draft.sessionID),
26026
26379
  modelKey: `${draft.model.providerID}/${draft.model.id}`,
26027
26380
  cacheExpired: false,
26028
26381
  lastResponseTime: state.lastResponseTime
@@ -26110,6 +26463,65 @@ async function registerContext(context) {
26110
26463
  }
26111
26464
  return ids;
26112
26465
  };
26466
+ const initializeTransform = (database) => transform ??= createTransform({
26467
+ cacheTtlConfig: config.cache_ttl,
26468
+ cacheTtlConfigured: config.cacheTtlConfigured,
26469
+ sampleCacheTtlConfig: () => liveConfigReader.poll().effective,
26470
+ db: database,
26471
+ tagger,
26472
+ ...createV2ThresholdDeps(config),
26473
+ contextUsageMap: usage,
26474
+ compactionOff,
26475
+ storeGeneration: "v2",
26476
+ normalizeRestoredAssistantText: (text, providerID) => {
26477
+ const normalized = stripCopiedTagNotation(text);
26478
+ return normalized === "" && !modelAcceptsEmptyContent(providerID) ? text : normalized;
26479
+ },
26480
+ refreshSourceContents: (sessionID) => committedSourceRefreshes.has(sessionID),
26481
+ hostCleanupCompactionMarkers: () => ({
26482
+ verified: true,
26483
+ removedLineages: 0,
26484
+ removedRows: 0,
26485
+ retainedLineages: 0
26486
+ }),
26487
+ protectedTokens: config.protected_tokens,
26488
+ protectedTokenTierOverrides: getProtectedTokensTierOverrides(config),
26489
+ protectedTools: config.protected_tools,
26490
+ liveModelBySession: liveModels,
26491
+ getToolSetHash: (sessionId) => {
26492
+ const model = liveModels.get(sessionId);
26493
+ if (!model)
26494
+ return "";
26495
+ return getCurrentToolSetHash(model.providerID, model.modelID, agents.get(sessionId), sessionId);
26496
+ },
26497
+ channel1StateBySession: channel1,
26498
+ historyRefreshSessions,
26499
+ pendingMaterializationSessions,
26500
+ lastHeuristicsTurnId,
26501
+ variantBySession: variants,
26502
+ keepReasoningTokens: config.keep_reasoning_tokens,
26503
+ directory,
26504
+ sessionDirectoryBySession: sessionDirectories,
26505
+ projectPath: directory,
26506
+ agentBySession: agents,
26507
+ maintenanceConfig: config.maintenance ? () => inheritMaintenanceModelConfig(config) : undefined,
26508
+ hiddenCompletionExecutor,
26509
+ historianRunnable: !compactionOff && hiddenCompletionExecutor !== undefined && config.historian?.disable !== true,
26510
+ historianModel: historianModels.primary,
26511
+ fallbackModels: historianModels.fallbacks,
26512
+ resolveHistorianRun: sampleHistorian,
26513
+ historianTimeoutMs: config.historian_timeout_ms,
26514
+ historianMaxOutputTokens: config.historian?.maxTokens,
26515
+ historianTwoPass: config.historian?.two_pass,
26516
+ historianExpandTools: config.historian?.expand_tools,
26517
+ compactionMarkerStrategy: v2CompactionMarkerStrategy,
26518
+ memoryConfig: {
26519
+ enabled: config.memory.enabled,
26520
+ injectionBudgetTokens: config.memory.injection_budget_tokens,
26521
+ autoPromote: config.memory.auto_promote
26522
+ },
26523
+ ...createHostSeams(context, readAllForConversion, pagedRead, liveModels)
26524
+ });
26113
26525
  const runManagedContext = async (draft) => {
26114
26526
  rememberHostMedia(draft.messages);
26115
26527
  if (hiddenChildHook.apply(draft))
@@ -26139,22 +26551,9 @@ async function registerContext(context) {
26139
26551
  }
26140
26552
  await dropStorageNotices(draft.messages, isMagicContextSynthetic);
26141
26553
  let hostMessagesAtEntry = [...draft.messages];
26142
- const passTimes = new Map;
26554
+ const cachedTimes = messageTimesCache.pass(draft.sessionID);
26143
26555
  const readTimes = (reader, messages) => {
26144
- const ids = timeIds(messages);
26145
- const missing = ids.filter((id) => !passTimes.has(id));
26146
- if (missing.length) {
26147
- const read = reader.messageTimes(draft.sessionID, missing);
26148
- for (const id of missing)
26149
- passTimes.set(id, read.get(id) ?? null);
26150
- }
26151
- const times = new Map;
26152
- for (const id of ids) {
26153
- const time = passTimes.get(id);
26154
- if (time)
26155
- times.set(id, time);
26156
- }
26157
- return times;
26556
+ return cachedTimes(reader, timeIds(messages));
26158
26557
  };
26159
26558
  liveModels.set(draft.sessionID, {
26160
26559
  providerID: draft.model.providerID,
@@ -26405,65 +26804,7 @@ async function registerContext(context) {
26405
26804
  }
26406
26805
  });
26407
26806
  }
26408
- transform ??= createTransform({
26409
- cacheTtlConfig: config.cache_ttl,
26410
- cacheTtlConfigured: config.cacheTtlConfigured,
26411
- sampleCacheTtlConfig: () => liveConfigReader.poll().effective,
26412
- db,
26413
- tagger,
26414
- ...createV2ThresholdDeps(config),
26415
- contextUsageMap: usage,
26416
- compactionOff,
26417
- storeGeneration: "v2",
26418
- normalizeRestoredAssistantText: (text, providerID) => {
26419
- const normalized = stripCopiedTagNotation(text);
26420
- return normalized === "" && !modelAcceptsEmptyContent(providerID) ? text : normalized;
26421
- },
26422
- refreshSourceContents: (sessionID) => committedSourceRefreshes.has(sessionID),
26423
- hostCleanupCompactionMarkers: () => ({
26424
- verified: true,
26425
- removedLineages: 0,
26426
- removedRows: 0,
26427
- retainedLineages: 0
26428
- }),
26429
- protectedTokens: config.protected_tokens,
26430
- protectedTokenTierOverrides: getProtectedTokensTierOverrides(config),
26431
- protectedTools: config.protected_tools,
26432
- liveModelBySession: liveModels,
26433
- getToolSetHash: (sessionId) => {
26434
- const model = liveModels.get(sessionId);
26435
- if (!model)
26436
- return "";
26437
- return getCurrentToolSetHash(model.providerID, model.modelID, agents.get(sessionId));
26438
- },
26439
- channel1StateBySession: channel1,
26440
- historyRefreshSessions,
26441
- pendingMaterializationSessions,
26442
- lastHeuristicsTurnId,
26443
- variantBySession: variants,
26444
- keepReasoningTokens: config.keep_reasoning_tokens,
26445
- directory,
26446
- sessionDirectoryBySession: sessionDirectories,
26447
- projectPath: directory,
26448
- agentBySession: agents,
26449
- maintenanceConfig: config.maintenance ? () => inheritMaintenanceModelConfig(config) : undefined,
26450
- hiddenCompletionExecutor,
26451
- historianRunnable: !compactionOff && hiddenCompletionExecutor !== undefined && config.historian?.disable !== true,
26452
- historianModel: historianModels.primary,
26453
- fallbackModels: historianModels.fallbacks,
26454
- resolveHistorianRun: sampleHistorian,
26455
- historianTimeoutMs: config.historian_timeout_ms,
26456
- historianMaxOutputTokens: config.historian?.maxTokens,
26457
- historianTwoPass: config.historian?.two_pass,
26458
- historianExpandTools: config.historian?.expand_tools,
26459
- compactionMarkerStrategy: v2CompactionMarkerStrategy,
26460
- memoryConfig: {
26461
- enabled: config.memory.enabled,
26462
- injectionBudgetTokens: config.memory.injection_budget_tokens,
26463
- autoPromote: config.memory.auto_promote
26464
- },
26465
- ...createHostSeams(context, readAllForConversion, pagedRead, liveModels)
26466
- });
26807
+ transform = initializeTransform(db);
26467
26808
  const refreshToken = committedSourceRefreshes.get(draft.sessionID);
26468
26809
  if (refreshToken) {
26469
26810
  clearCachedM0(db, draft.sessionID);
@@ -26762,12 +27103,10 @@ async function registerContext(context) {
26762
27103
  }, 0);
26763
27104
  return {
26764
27105
  async dispose() {
27106
+ messageTimesCache.clear();
26765
27107
  usageController.abort();
26766
- if (orphanSweepTimer)
26767
- clearTimeout(orphanSweepTimer);
26768
27108
  await hiddenCompletionExecutor?.dispose();
26769
27109
  await usageDone;
26770
- await orphanSweep;
26771
27110
  rpcStopped = true;
26772
27111
  rpcServer.stop();
26773
27112
  tools?.dispose();