@retaw106/mmc-oc 2.2.1 → 2.3.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.
Files changed (211) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/config/deprecated-config.d.ts +4 -0
  3. package/dist/config/deprecated-config.d.ts.map +1 -0
  4. package/dist/config/index.d.ts.map +1 -1
  5. package/dist/config/project-security.d.ts +2 -2
  6. package/dist/config/schema/magic-context.d.ts +3 -3
  7. package/dist/config/schema/magic-context.d.ts.map +1 -1
  8. package/dist/config-init.js +2 -2
  9. package/dist/{context-mrvm92n8.js → context-wt7g4f8k.js} +1641 -1543
  10. package/dist/{derive-budgets-rjjz5f7y.js → derive-budgets-s1p37t2g.js} +2 -2
  11. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  12. package/dist/features/magic-context/historian-failure-reason.d.ts +7 -0
  13. package/dist/features/magic-context/historian-failure-reason.d.ts.map +1 -0
  14. package/dist/features/magic-context/message-index-async.d.ts +21 -3
  15. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  16. package/dist/features/magic-context/message-index.d.ts +7 -1
  17. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  18. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  19. package/dist/features/magic-context/pi-content-decisions.d.ts +7 -1
  20. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -1
  21. package/dist/features/magic-context/search.d.ts +29 -0
  22. package/dist/features/magic-context/search.d.ts.map +1 -1
  23. package/dist/features/magic-context/storage-clone.d.ts +2 -0
  24. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  25. package/dist/features/magic-context/storage-compartment-history-version.d.ts +4 -0
  26. package/dist/features/magic-context/storage-compartment-history-version.d.ts.map +1 -0
  27. package/dist/features/magic-context/storage-db.d.ts +1 -1
  28. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
  30. package/dist/features/magic-context/storage-meta-persisted.d.ts +23 -5
  31. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  32. package/dist/features/magic-context/storage-meta-session.d.ts +7 -1
  33. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  34. package/dist/features/magic-context/storage-session-tables.d.ts +5 -1
  35. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  36. package/dist/features/magic-context/storage-tag-identity-revision.d.ts +50 -0
  37. package/dist/features/magic-context/storage-tag-identity-revision.d.ts.map +1 -0
  38. package/dist/features/magic-context/storage-tags.d.ts +155 -5
  39. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  40. package/dist/features/magic-context/tagger.d.ts +2 -0
  41. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  42. package/dist/features/magic-context/transform-decision-log.d.ts +3 -0
  43. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/active-anthropic-turn.d.ts +2 -0
  45. package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/apply-operations.d.ts +7 -0
  47. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/auto-search-runner.d.ts +2 -2
  49. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/history-boundary-repair.d.ts +11 -2
  52. package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/lkg-measured-request.d.ts +3 -1
  55. package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/lkg-persist.d.ts +10 -13
  57. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts +62 -0
  59. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts.map +1 -0
  60. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts +38 -0
  61. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts.map +1 -0
  62. package/dist/hooks/magic-context/lkg-replay.d.ts +16 -9
  63. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  64. package/dist/hooks/magic-context/lkg-slot.d.ts +141 -2
  65. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  66. package/dist/hooks/magic-context/opaque-thinking.d.ts +12 -0
  67. package/dist/hooks/magic-context/opaque-thinking.d.ts.map +1 -0
  68. package/dist/hooks/magic-context/pass-outcome.d.ts +1 -0
  69. package/dist/hooks/magic-context/pass-outcome.d.ts.map +1 -1
  70. package/dist/hooks/magic-context/pending-materialization.d.ts +8 -0
  71. package/dist/hooks/magic-context/pending-materialization.d.ts.map +1 -0
  72. package/dist/hooks/magic-context/postprocess-read-cache.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/provider-visible-parts.d.ts +45 -0
  74. package/dist/hooks/magic-context/provider-visible-parts.d.ts.map +1 -0
  75. package/dist/hooks/magic-context/read-session-chunk.d.ts +22 -1
  76. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/read-session-db.d.ts +3 -0
  78. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/read-session-formatting.d.ts +5 -0
  80. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/read-session-raw.d.ts +123 -1
  82. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  83. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/sentinel.d.ts +41 -1
  86. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  87. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts +8 -0
  88. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts.map +1 -0
  89. package/dist/hooks/magic-context/strip-content.d.ts +38 -24
  90. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/system-date-refresh.d.ts +29 -0
  92. package/dist/hooks/magic-context/system-date-refresh.d.ts.map +1 -0
  93. package/dist/hooks/magic-context/system-prompt-dates.d.ts +6 -0
  94. package/dist/hooks/magic-context/system-prompt-dates.d.ts.map +1 -0
  95. package/dist/hooks/magic-context/system-prompt-delivery.d.ts +47 -0
  96. package/dist/hooks/magic-context/system-prompt-delivery.d.ts.map +1 -0
  97. package/dist/hooks/magic-context/system-prompt-hash.d.ts +18 -5
  98. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  99. package/dist/hooks/magic-context/tag-messages.d.ts +19 -0
  100. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  101. package/dist/hooks/magic-context/tag-owner-warmup.d.ts +14 -0
  102. package/dist/hooks/magic-context/tag-owner-warmup.d.ts.map +1 -0
  103. package/dist/hooks/magic-context/token-count-exact.d.ts +35 -0
  104. package/dist/hooks/magic-context/token-count-exact.d.ts.map +1 -0
  105. package/dist/hooks/magic-context/tool-drop-target.d.ts +2 -0
  106. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  107. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +7 -0
  108. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/transform.d.ts +21 -4
  110. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  111. package/dist/index-18yrysyj.js +1477 -0
  112. package/dist/{index-g2z2k5m4.js → index-3gj1ak4c.js} +2 -2
  113. package/dist/{index-5cep2r9n.js → index-5ty9rnbx.js} +60 -258
  114. package/dist/{index-amycb7b1.js → index-a5mw0xp3.js} +3 -3
  115. package/dist/{index-1j34a56j.js → index-czba8tw3.js} +266 -32
  116. package/dist/{index-qbkzdkk0.js → index-k59af64t.js} +6 -2
  117. package/dist/{index-53g38ggt.js → index-kmbme1v1.js} +1 -1
  118. package/dist/{index-5b7x80ec.js → index-kw21z5pe.js} +6055 -3973
  119. package/dist/{index-33gprt9g.js → index-yjv8dpht.js} +1 -1
  120. package/dist/{index-btp5tdc8.js → index-z2w1eq3y.js} +840 -456
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +114 -2
  123. package/dist/{message-time-backfill-phrwp2fz.js → message-time-backfill-sykevdax.js} +3 -3
  124. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  125. package/dist/{read-session-chunk-2xm4sc4y.js → read-session-chunk-863d6c06.js} +6 -4
  126. package/dist/recomp-orchestrator-wrjy3qym.js +21 -0
  127. package/dist/{safe-notification-target-ph3xd1ve.js → safe-notification-target-1jkf0m82.js} +2 -2
  128. package/dist/{send-session-notification-rnc4nrsr.js → send-session-notification-cz6ev088.js} +5 -5
  129. package/dist/shared/auto-search-lifecycle.d.ts +14 -0
  130. package/dist/shared/auto-search-lifecycle.d.ts.map +1 -1
  131. package/dist/shared/logger.d.ts.map +1 -1
  132. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  133. package/dist/shared/models-dev-temperature.d.ts +18 -0
  134. package/dist/shared/models-dev-temperature.d.ts.map +1 -0
  135. package/dist/shared/processed-image-eligibility.d.ts +22 -0
  136. package/dist/shared/processed-image-eligibility.d.ts.map +1 -0
  137. package/dist/shared/project-directory-key.d.ts +16 -0
  138. package/dist/shared/project-directory-key.d.ts.map +1 -1
  139. package/dist/shared/recovery-reasoning-prefix.d.ts +9 -0
  140. package/dist/shared/recovery-reasoning-prefix.d.ts.map +1 -0
  141. package/dist/shared/sqlite.d.ts +33 -1
  142. package/dist/shared/sqlite.d.ts.map +1 -1
  143. package/dist/shared/stable-json.d.ts.map +1 -1
  144. package/dist/shared/storage-file-mutex.d.ts +9 -0
  145. package/dist/shared/storage-file-mutex.d.ts.map +1 -0
  146. package/dist/shared/storage-permissions.d.ts +6 -0
  147. package/dist/shared/storage-permissions.d.ts.map +1 -1
  148. package/dist/shared/subagent-temperature.d.ts +46 -0
  149. package/dist/shared/subagent-temperature.d.ts.map +1 -0
  150. package/dist/shared/tag-owner-id.d.ts +1 -0
  151. package/dist/shared/tag-owner-id.d.ts.map +1 -1
  152. package/dist/shared/tag-transcript.d.ts +9 -0
  153. package/dist/shared/tag-transcript.d.ts.map +1 -1
  154. package/dist/shared/window-geometry.d.ts +9 -66
  155. package/dist/shared/window-geometry.d.ts.map +1 -1
  156. package/dist/standalone-plugin-marker.d.ts +8 -0
  157. package/dist/standalone-plugin-marker.d.ts.map +1 -0
  158. package/dist/tools/ctx-expand/search-archive.d.ts +4 -0
  159. package/dist/tools/ctx-expand/search-archive.d.ts.map +1 -0
  160. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  161. package/dist/tools/ctx-reduce/constants.d.ts +1 -2
  162. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  163. package/dist/tools/ctx-reduce/tools.d.ts +1 -1
  164. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  165. package/dist/v2/hooks/context.d.ts +1 -1
  166. package/dist/v2/hooks/context.d.ts.map +1 -1
  167. package/dist/v2/hooks/copied-tag-notation.d.ts +19 -0
  168. package/dist/v2/hooks/copied-tag-notation.d.ts.map +1 -0
  169. package/dist/v2/hooks/generate.d.ts +9 -2
  170. package/dist/v2/hooks/generate.d.ts.map +1 -1
  171. package/dist/v2/hooks/payload.d.ts.map +1 -1
  172. package/dist/v2/hooks/system-prompt-response.d.ts +5 -0
  173. package/dist/v2/hooks/system-prompt-response.d.ts.map +1 -0
  174. package/dist/v2/server.d.ts.map +1 -1
  175. package/dist/v2/server.js +34550 -30855
  176. package/dist/web/main.js +373 -19
  177. package/dist/{wrapup-orchestrator-yknks3ad.js → wrapup-orchestrator-bmybc5p5.js} +10 -10
  178. package/package.json +1 -1
  179. package/src/config/deprecated-config.ts +18 -0
  180. package/src/config/index.test.ts +13 -37
  181. package/src/config/index.ts +2 -2
  182. package/src/config/project-security.ts +3 -3
  183. package/src/config/schema/magic-context.ts +10 -6
  184. package/src/shared/auto-search-lifecycle.ts +43 -0
  185. package/src/shared/logger.ts +10 -2
  186. package/src/shared/models-dev-cache.ts +1 -7
  187. package/src/shared/models-dev-temperature.ts +107 -0
  188. package/src/shared/processed-image-eligibility.test.ts +68 -0
  189. package/src/shared/processed-image-eligibility.ts +41 -0
  190. package/src/shared/processed-image-position-review.json +20 -0
  191. package/src/shared/processed-image-position-review.test.ts +19 -0
  192. package/src/shared/project-directory-key.ts +20 -0
  193. package/src/shared/recovery-reasoning-prefix.test.ts +34 -0
  194. package/src/shared/recovery-reasoning-prefix.ts +35 -0
  195. package/src/shared/sqlite-acquisition-routing.test.ts +4 -3
  196. package/src/shared/sqlite-foreground-wait.test.ts +68 -0
  197. package/src/shared/sqlite-pass-scope.test.ts +4 -4
  198. package/src/shared/sqlite-writer-diagnostics.test.ts +52 -12
  199. package/src/shared/sqlite.ts +380 -39
  200. package/src/shared/stable-json.ts +37 -7
  201. package/src/shared/storage-file-mutex.test.ts +50 -0
  202. package/src/shared/storage-file-mutex.ts +130 -0
  203. package/src/shared/storage-permissions.ts +46 -0
  204. package/src/shared/subagent-temperature.ts +110 -0
  205. package/src/shared/tag-owner-id.ts +13 -0
  206. package/src/shared/tag-transcript.ts +118 -0
  207. package/src/shared/window-geometry.test.ts +102 -236
  208. package/src/shared/window-geometry.ts +23 -413
  209. package/dist/index-ramz5kkm.js +0 -514
  210. package/dist/recomp-orchestrator-frmwj9hz.js +0 -21
  211. package/src/shared/__fixtures__/window-overlay.v1.json +0 -337
@@ -58,11 +58,23 @@ import {
58
58
  searchMemoriesFTSUnion,
59
59
  clearMemoryVerifications,
60
60
  queueMemoryMutation,
61
+ scheduleAfterBootQuiet,
62
+ BoundedSessionMap,
61
63
  registerOwnRpcServerInstance,
62
64
  rpcPortDir,
63
65
  rpcPortFilePath,
64
66
  parseRpcPortFile,
65
67
  inspectProcessesAsync,
68
+ dirtyLkgRecoveryRevision,
69
+ ownsLkgDirtyFence,
70
+ invalidateCleanLkgRecoveryFence,
71
+ getLastIndexedOrdinal,
72
+ getIndexedMessageIdAtOrdinal,
73
+ getIndexedMessageCorpusSize,
74
+ getMessageIndexReconciliationStartOrdinal,
75
+ isMessageIndexReconciledThrough,
76
+ clearIndexedMessages,
77
+ indexMessagesAfterOrdinal,
66
78
  acquireCompartmentLease,
67
79
  releaseCompartmentLeaseBestEffort,
68
80
  isCompartmentLeaseHeld,
@@ -74,15 +86,6 @@ import {
74
86
  getLastCompartmentEndMessage,
75
87
  getLastCompartmentEndMessageId,
76
88
  getAuthorityManagedMarker,
77
- getLastIndexedOrdinal,
78
- getIndexedMessageIdAtOrdinal,
79
- getIndexedMessageCorpusSize,
80
- getMessageIndexReconciliationStartOrdinal,
81
- isMessageIndexReconciledThrough,
82
- clearIndexedMessages,
83
- indexMessagesAfterOrdinal,
84
- layoutLkgPrefix,
85
- assembleLkgPrefix,
86
89
  getTemporalDecisions,
87
90
  observeTemporalDecisions,
88
91
  freezeTemporalDecisions,
@@ -123,7 +126,7 @@ import {
123
126
  casChannel2NudgeClaim,
124
127
  getAutoSearchHintDecisions,
125
128
  appendAutoSearchHintDecision,
126
- pruneAutoSearchHintDecisions,
129
+ flushAutoSearchHintDecisionRetirement,
127
130
  getPersistedTodoPermissionDenied,
128
131
  setPersistedTodoPermissionDenied,
129
132
  getPersistedTodoSyntheticAnchor,
@@ -152,32 +155,66 @@ import {
152
155
  addTrailingBlankDecisions,
153
156
  demoteTrailingBlankKeepDecisions,
154
157
  addStaleReduceStrippedIds,
158
+ getProcessedImageMarkers,
155
159
  addProcessedImageStrippedIds,
156
160
  getPendingCompactionMarkerState,
157
161
  getCompactionMarkerHealth,
158
162
  loadPostprocessReplaySnapshot,
159
163
  clearPendingCompactionMarkerStateIf,
164
+ getPendingPiCompactionMarkerState,
165
+ clearPendingPiCompactionMarkerStateIf,
166
+ logPiCompactionMarkerClear,
160
167
  setSessionWorkMetrics,
161
168
  getObservedEpochFloor,
162
169
  resolveEpochFloorForPass,
163
- BoundedSessionMap,
170
+ providerResponseFailed,
171
+ isSuccessfulProviderCompletion,
172
+ rememberUnpublishedAutoSearchNoHint,
173
+ getUnpublishedAutoSearchNoHint,
174
+ forgetUnpublishedAutoSearchNoHint,
175
+ registerAutoSearchWork,
176
+ gaDatabasePath,
177
+ RAW_MESSAGE_TYPES,
178
+ V2StoreReaderPool,
179
+ getOrCreateSessionMeta,
180
+ updateSessionMeta,
181
+ advanceToolReclaimWatermark,
182
+ markSessionCleanupPending,
183
+ clearSession,
184
+ requestPendingMaterialization,
185
+ pendingMaterializationOrigin,
186
+ clearPendingMaterialization,
187
+ recordSystemPromptForRequest,
188
+ recordHeldHeadForRequest,
189
+ bindSystemPromptResponse,
190
+ isHeldHeadForRequest,
191
+ deferDeliveredSystemRefresh,
192
+ retirePendingSystemPromptDelivery,
193
+ lkgProviderInputTotal,
194
+ beginV2LkgRequest,
195
+ currentLkgRequestIdentity,
196
+ noteLkgProviderResponse,
197
+ clearLkgMeasuredRequest,
198
+ saveLkgSlotToDb,
199
+ loadPersistedLkgSlot,
200
+ pruneStaleLkgSlots,
201
+ resolveLkgModelKeys,
202
+ createLkgEntryProjector,
203
+ captureLkgSlot,
204
+ replayLkg,
205
+ noteUncertifiedNormalLkg,
206
+ lkgDirtyRevision,
207
+ readLkgRecoveryPrefix,
208
+ noteLkgHealthyPrefix,
164
209
  beginLkgPass,
165
- lkgContentFields,
166
- lkgContentDigestFromFields,
167
- memoizedLkgContentDigestFromFields,
168
- exactReusablePrefix,
169
- lkgContentDigest,
170
- captureSlot,
171
210
  getInMemorySlot,
172
211
  getSlot,
173
212
  dropSlot,
174
- noteEntry,
175
213
  getLkgSlotHeapStats,
176
214
  TOOL_SWEEP_SCOPED_MARKER,
177
215
  useScopedToolSweep,
178
216
  createPreAdoptionToolSweepResolver,
179
217
  copySessionStateForClone,
180
- scheduleAfterBootQuiet,
181
218
  getSchemaFenceRejection,
182
219
  getMigrationOnOpenRefusal,
183
220
  LATEST_SUPPORTED_VERSION,
@@ -186,14 +223,6 @@ import {
186
223
  openDatabaseAsync,
187
224
  isDatabasePersisted,
188
225
  getDatabasePersistenceError,
189
- gaDatabasePath,
190
- RAW_MESSAGE_TYPES,
191
- V2StoreReaderPool,
192
- getOrCreateSessionMeta,
193
- updateSessionMeta,
194
- advanceToolReclaimWatermark,
195
- markSessionCleanupPending,
196
- clearSession,
197
226
  getNoteByIdInScope,
198
227
  getNotes,
199
228
  addNote,
@@ -265,7 +294,7 @@ import {
265
294
  recordHighPressureNoEligibleHead,
266
295
  getActiveCompartmentRun,
267
296
  startCompartmentAgent
268
- } from "./index-5b7x80ec.js";
297
+ } from "./index-kw21z5pe.js";
269
298
  import {
270
299
  getHarness,
271
300
  getDataDir,
@@ -273,14 +302,13 @@ import {
273
302
  log,
274
303
  sessionLog,
275
304
  getLoggerDiagnostics
276
- } from "./index-qbkzdkk0.js";
305
+ } from "./index-k59af64t.js";
277
306
  import {
278
307
  shouldEnforcePrivateStoragePermissions,
279
308
  ensureStorageDirectorySync,
280
309
  writeStorageFileAtomicSync,
281
310
  writeStorageFileAsync,
282
311
  createStorageWriteStream,
283
- setWindowOverlayPath,
284
312
  hasTrustedAbsoluteWall,
285
313
  applyProvenInputFloor,
286
314
  isSaneLimit,
@@ -294,14 +322,11 @@ import {
294
322
  deriveHistorianChunkTokens2,
295
323
  resolveKnownHistorianContextLimit2,
296
324
  resolveHistorianContextLimit2
297
- } from "./index-5cep2r9n.js";
325
+ } from "./index-5ty9rnbx.js";
298
326
  import {
299
327
  normalizeProtectedToolName,
300
328
  mergeProtectedTools,
301
329
  isRecord,
302
- detectLatestTurnThinkingMismatch,
303
- isPrefixBoundThinkingModel,
304
- detectOverflow,
305
330
  protectedToolTagNumbers,
306
331
  protectedToolTokenCount,
307
332
  textMentionsRecentCommit,
@@ -311,6 +336,7 @@ import {
311
336
  estimateTokens,
312
337
  estimateFixedPromptTokens,
313
338
  stripWellFormedLeadingTagPrefix,
339
+ stripPersistedAssistantText,
314
340
  byteSize,
315
341
  stripTagPrefix,
316
342
  peelLeadingMcTagNotation,
@@ -348,6 +374,7 @@ import {
348
374
  getPendingOpsCount,
349
375
  clearPendingOps,
350
376
  removePendingOp,
377
+ tagIdentityRevisionKey,
351
378
  getRecentTagOwnerMessageIds,
352
379
  AGE_RECLAIM_MIN_TOKENS,
353
380
  getOldestActiveUnprotectedToolTags,
@@ -355,12 +382,17 @@ import {
355
382
  getTriggerTagTokenUpperBound,
356
383
  getActiveTagTokenTotalsByMessage,
357
384
  getReasoningTokenEstimatesByMessage,
385
+ tryGetReasoningTokenEstimatesByMessage,
386
+ getMaxTagNumberByOwnerMessage,
387
+ scanTagOwnerSummary,
388
+ isTagOwnerSummaryReady,
358
389
  tagTokenCountIsNull,
359
390
  backfillTagTokenCounts,
360
391
  insertTag,
361
392
  updateTagStatus,
362
393
  markWhitespaceAssistantTagInert,
363
394
  getInertWhitespaceAssistantTags,
395
+ getInertWhitespaceAssistantTagsByNumbers,
364
396
  updateTagDropMode,
365
397
  updateCavemanDepth,
366
398
  updateTagMessageId,
@@ -384,8 +416,6 @@ import {
384
416
  pickNearestPriorOwner,
385
417
  expandToolPart,
386
418
  markHostUnservedRow,
387
- extractInMemoryMessageViews,
388
- buildInMemoryTailRawMessages,
389
419
  estimateTrueRawMessageTokens,
390
420
  stripSystemInjection,
391
421
  isTextPart,
@@ -405,6 +435,9 @@ import {
405
435
  wireContentBytes,
406
436
  createFinalWireUsageTracker,
407
437
  estimateFinalWireInputTokens,
438
+ detectLatestTurnThinkingMismatch,
439
+ isPrefixBoundThinkingModel,
440
+ detectOverflow,
408
441
  modelAcceptsEmptyContent,
409
442
  isAnthropicFamilyRoute,
410
443
  neutralizeDroppedReasoningPart,
@@ -414,6 +447,7 @@ import {
414
447
  variantChangeBustsProviderCache,
415
448
  makeSentinel,
416
449
  makeWholeMessageSentinel,
450
+ neutralizeWholeMessage,
417
451
  isSentinel,
418
452
  replaySentinelByMessageIds,
419
453
  SKELETON_REAL_INPUT_MAX_BYTES,
@@ -425,11 +459,13 @@ import {
425
459
  withRawSessionMessageCache2,
426
460
  readRawSessionMessages2,
427
461
  visitRawSessionMessages2,
462
+ prepareRawSessionOrdinals2,
428
463
  primeTailRawMessageCache2,
429
464
  primeInMemoryTailRawMessageCache2,
465
+ readRawSessionMessageIdOrdinalsForRange2,
430
466
  readRawSessionMessageById2,
431
467
  readSessionChunk2
432
- } from "./index-btp5tdc8.js";
468
+ } from "./index-z2w1eq3y.js";
433
469
  import {
434
470
  getSqliteMemoryStats,
435
471
  withSqliteBackgroundWriter,
@@ -437,8 +473,10 @@ import {
437
473
  withoutSqliteTransformPass,
438
474
  isTransientSqliteError,
439
475
  SqliteAcquisitionBusyError,
440
- withAsyncPrivilegedWriter
441
- } from "./index-1j34a56j.js";
476
+ withAsyncPrivilegedWriter,
477
+ indexCoordinationInput,
478
+ withIndexChunk
479
+ } from "./index-czba8tw3.js";
442
480
  import {
443
481
  canonicalModelIdentity,
444
482
  piModelRefToCanonical,
@@ -448,11 +486,14 @@ import {
448
486
  resolveDecisionCalibration
449
487
  } from "./index-x37vg474.js";
450
488
  import {
489
+ extractInMemoryMessageViews,
490
+ buildInMemoryTailRawMessages,
451
491
  openCodeDbExists,
452
492
  withReadOnlySessionDb,
493
+ latestRealUserTurnIdFromOpenCodeDb,
453
494
  getMessageTimesFromOpenCodeDb,
454
495
  findLastAssistantModelFromOpenCodeDb
455
- } from "./index-ramz5kkm.js";
496
+ } from "./index-18yrysyj.js";
456
497
  import {
457
498
  registerNotificationSink2,
458
499
  pushNotification2,
@@ -460,18 +501,18 @@ import {
460
501
  drainNotifications2,
461
502
  isTuiConnected2
462
503
  } from "./index-jdxxq8cz.js";
463
- import"./index-53g38ggt.js";
504
+ import"./index-kmbme1v1.js";
464
505
  import {
465
506
  getErrorMessage,
466
507
  flushIgnoredMessages2,
467
508
  sendStatusNotification2
468
- } from "./index-amycb7b1.js";
509
+ } from "./index-a5mw0xp3.js";
469
510
  import {
470
511
  resolveHistorianModel2
471
512
  } from "./index-z2g20msd.js";
472
513
  import {
473
514
  logSlowWriteTransaction
474
- } from "./index-33gprt9g.js";
515
+ } from "./index-yjv8dpht.js";
475
516
 
476
517
  // src/config/index.ts
477
518
  import { existsSync as existsSync4 } from "node:fs";
@@ -1578,6 +1619,15 @@ function migrateLegacyAgentEnabledInMemory(rawConfig, warnings) {
1578
1619
  return patched;
1579
1620
  }
1580
1621
 
1622
+ // src/config/deprecated-config.ts
1623
+ var WINDOW_OVERLAY_PATH_DEPRECATION_WARNING = "models.window_overlay_path is deprecated and ignored; Fusiform no longer publishes window-overlay data, so this setting no longer changes model window geometry. Remove this key.";
1624
+ function warnDeprecatedWindowOverlayPath(config, warnings) {
1625
+ const models = config.models;
1626
+ if (typeof models === "object" && models !== null && Object.hasOwn(models, "window_overlay_path") && !warnings.includes(WINDOW_OVERLAY_PATH_DEPRECATION_WARNING)) {
1627
+ warnings.push(WINDOW_OVERLAY_PATH_DEPRECATION_WARNING);
1628
+ }
1629
+ }
1630
+
1581
1631
  // src/config/legacy-harness-blocks.ts
1582
1632
  function isPlainObject(value) {
1583
1633
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1915,7 +1965,7 @@ function stripUnsafeProjectConfigFields(projectRaw) {
1915
1965
  const models = projectRaw.models;
1916
1966
  if (isPlainObject2(models) && "window_overlay_path" in models) {
1917
1967
  delete models.window_overlay_path;
1918
- warnings.push("Ignoring models.window_overlay_path from project config (security: only user-level config may select model geometry metadata).");
1968
+ warnings.push("Ignoring models.window_overlay_path from project config (deprecated; this setting no longer changes model window geometry).");
1919
1969
  }
1920
1970
  if ("language" in projectRaw) {
1921
1971
  delete projectRaw.language;
@@ -2633,6 +2683,7 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
2633
2683
  const configWithoutRemovedAgent = stripRemovedAgentConfig(rawConfig, preMigrationWarnings);
2634
2684
  const migratedExperimental = migrateLegacyExperimental(configWithoutRemovedAgent, preMigrationWarnings);
2635
2685
  const migrated = migrateLegacyAgentEnabledInMemory(migratedExperimental, preMigrationWarnings);
2686
+ warnDeprecatedWindowOverlayPath(migrated, preMigrationWarnings);
2636
2687
  const parsed = MagicContextConfigSchema.safeParse(migrated);
2637
2688
  const disabledHooks = Array.isArray(rawConfig.disabled_hooks) ? rawConfig.disabled_hooks.filter((value) => typeof value === "string") : undefined;
2638
2689
  const command = typeof rawConfig.command === "object" && rawConfig.command !== null ? rawConfig.command : undefined;
@@ -2851,7 +2902,6 @@ function loadPluginConfigDetailed(directory, applyRuntimeGlobals = true) {
2851
2902
  config.profile = profileResolution.activeProfile;
2852
2903
  if (applyRuntimeGlobals) {
2853
2904
  setOutputReserveConfig(config.output_reserve);
2854
- setWindowOverlayPath(config.models?.window_overlay_path);
2855
2905
  }
2856
2906
  const leafValidationWarnings = [...config.configWarnings ?? []];
2857
2907
  if (config.configWarnings?.length) {
@@ -4876,6 +4926,236 @@ function createOc2MaintenanceRunFactory(options) {
4876
4926
  };
4877
4927
  }
4878
4928
 
4929
+ // src/features/magic-context/message-index-async.ts
4930
+ function isDatabaseLockedError(error) {
4931
+ if (!error || typeof error !== "object")
4932
+ return false;
4933
+ const e = error;
4934
+ if (typeof e.code === "string") {
4935
+ if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
4936
+ return true;
4937
+ }
4938
+ if (typeof e.message === "string") {
4939
+ if (/database is locked/i.test(e.message))
4940
+ return true;
4941
+ if (/sqlite_(busy|locked)/i.test(e.message))
4942
+ return true;
4943
+ }
4944
+ return false;
4945
+ }
4946
+ var RECONCILIATION_BATCH_SIZE = 100;
4947
+
4948
+ class MagicContextMessageIndexHeapHolder {
4949
+ reconciledSessions = new Set;
4950
+ reconciliationScheduledSessions = new Set;
4951
+ clearAndReindexScheduledSessions = new Map;
4952
+ sessionLocks = new Map;
4953
+ incrementalTimers = new Map;
4954
+ pendingIncrementalKeys = new Set;
4955
+ completedIncrementalKeys = new Set;
4956
+ activeReconcilerBuffers = new Map;
4957
+ }
4958
+ var heapHolder = new MagicContextMessageIndexHeapHolder;
4959
+ function clearCompletedIncrementalKeys(sessionId) {
4960
+ const prefix = `${sessionId}\x00`;
4961
+ for (const key of heapHolder.completedIncrementalKeys) {
4962
+ if (key.startsWith(prefix))
4963
+ heapHolder.completedIncrementalKeys.delete(key);
4964
+ }
4965
+ }
4966
+ var sessionEpochs = new Map;
4967
+ var PREPARE_RETRY_MS = 60000;
4968
+ async function sourceReady(source, sessionId) {
4969
+ const prepare = source?.prepare;
4970
+ return typeof prepare === "function" ? prepare(sessionId) : true;
4971
+ }
4972
+ function defer(fn) {
4973
+ const immediate = globalThis.setImmediate;
4974
+ if (typeof immediate === "function") {
4975
+ immediate(fn);
4976
+ return;
4977
+ }
4978
+ setTimeout(fn, 0);
4979
+ }
4980
+ function yieldToEventLoop() {
4981
+ return new Promise((resolve) => defer(resolve));
4982
+ }
4983
+ function runWithSessionLock(sessionId, operation) {
4984
+ const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
4985
+ const run = previous.catch(() => {
4986
+ return;
4987
+ }).then(async () => {
4988
+ await operation();
4989
+ });
4990
+ heapHolder.sessionLocks.set(sessionId, run);
4991
+ run.finally(() => {
4992
+ if (heapHolder.sessionLocks.get(sessionId) === run) {
4993
+ heapHolder.sessionLocks.delete(sessionId);
4994
+ }
4995
+ }).catch(() => {
4996
+ return;
4997
+ });
4998
+ return run;
4999
+ }
5000
+ function logIndexingError(sessionId, action, error) {
5001
+ if (isDatabaseLockedError(error)) {
5002
+ sessionLog(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
5003
+ return;
5004
+ }
5005
+ sessionLog(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
5006
+ log(`[message-index-async] ${action} failed for ${sessionId}:`, error);
5007
+ }
5008
+ function serializedMessageBytes(messages) {
5009
+ try {
5010
+ return Buffer.byteLength(JSON.stringify(messages));
5011
+ } catch {
5012
+ return 0;
5013
+ }
5014
+ }
5015
+ function detectIndexMisalignment(db, sessionId, cursor, finalWatermark, readMessages, fallbackSnapshot) {
5016
+ if (finalWatermark <= 0)
5017
+ return null;
5018
+ const indexedWatermark = getLastIndexedOrdinal(db, sessionId);
5019
+ if (indexedWatermark > finalWatermark) {
5020
+ return `indexed watermark ${indexedWatermark} exceeds projection watermark ${finalWatermark}`;
5021
+ }
5022
+ if (cursor <= 0 || cursor > finalWatermark)
5023
+ return null;
5024
+ const indexedId = getIndexedMessageIdAtOrdinal(db, sessionId, cursor);
5025
+ if (indexedId === null)
5026
+ return null;
5027
+ const projectedId = readMessages.readPage ? readMessages.readPage(sessionId, cursor - 1, 1, finalWatermark)[0]?.id ?? null : fallbackSnapshot?.find((message) => message.ordinal === cursor)?.id ?? null;
5028
+ if (projectedId === null || projectedId === indexedId)
5029
+ return null;
5030
+ return `indexed id '${indexedId}' at ordinal ${cursor} but projection has '${projectedId}'`;
5031
+ }
5032
+ async function reconcileSessionIndex(db, sessionId, readMessages) {
5033
+ if (!await sourceReady(readMessages, sessionId))
5034
+ return false;
5035
+ await runWithSessionLock(sessionId, async () => {
5036
+ if (heapHolder.reconciledSessions.has(sessionId))
5037
+ return;
5038
+ let fallbackSnapshot = null;
5039
+ try {
5040
+ const fullReader = typeof readMessages === "function" ? readMessages : null;
5041
+ const finalWatermark = readMessages.getCount ? readMessages.getCount(sessionId) : (fallbackSnapshot = fullReader?.(sessionId) ?? []).length;
5042
+ let cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
5043
+ let after;
5044
+ const misalignment = detectIndexMisalignment(db, sessionId, cursor, finalWatermark, readMessages, fallbackSnapshot);
5045
+ if (misalignment !== null) {
5046
+ sessionLog(sessionId, `message FTS index misaligned (${misalignment}); clearing for full re-index`);
5047
+ withSqliteBackgroundWriter(() => clearIndexedMessages(db, sessionId));
5048
+ cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
5049
+ }
5050
+ while (cursor < finalWatermark) {
5051
+ const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
5052
+ const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
5053
+ const retainedMessages = fallbackSnapshot ?? messages;
5054
+ heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
5055
+ await withIndexChunk(indexCoordinationInput(), () => withSqliteBackgroundWriter(() => indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd)));
5056
+ const nextCursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
5057
+ if (nextCursor <= cursor)
5058
+ break;
5059
+ cursor = nextCursor;
5060
+ const last = messages.at(-1);
5061
+ after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
5062
+ if (cursor < finalWatermark) {
5063
+ await yieldToEventLoop();
5064
+ }
5065
+ }
5066
+ if (isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
5067
+ heapHolder.reconciledSessions.add(sessionId);
5068
+ }
5069
+ } finally {
5070
+ heapHolder.activeReconcilerBuffers.delete(sessionId);
5071
+ }
5072
+ });
5073
+ return true;
5074
+ }
5075
+ function scheduleReconciliation(db, sessionId, readMessages) {
5076
+ if (heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
5077
+ return;
5078
+ }
5079
+ heapHolder.reconciliationScheduledSessions.add(sessionId);
5080
+ scheduleAfterBootQuiet(() => {
5081
+ defer(() => {
5082
+ reconcileSessionIndex(db, sessionId, readMessages).then((ran) => {
5083
+ if (ran)
5084
+ return;
5085
+ setTimeout(() => {
5086
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
5087
+ scheduleReconciliation(db, sessionId, readMessages);
5088
+ }, PREPARE_RETRY_MS).unref?.();
5089
+ }).catch((error) => {
5090
+ logIndexingError(sessionId, "reconciliation", error);
5091
+ if (isDatabaseLockedError(error))
5092
+ setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000).unref();
5093
+ }).finally(() => {
5094
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
5095
+ });
5096
+ });
5097
+ });
5098
+ }
5099
+ function scheduleReconciliationCatchup(db, sessionId, readMessages) {
5100
+ if (typeof readMessages.getCount !== "function") {
5101
+ scheduleReconciliation(db, sessionId, readMessages);
5102
+ return;
5103
+ }
5104
+ let count;
5105
+ try {
5106
+ count = readMessages.getCount(sessionId);
5107
+ } catch {
5108
+ return;
5109
+ }
5110
+ if (count <= getLastIndexedOrdinal(db, sessionId))
5111
+ return;
5112
+ heapHolder.reconciledSessions.delete(sessionId);
5113
+ scheduleReconciliation(db, sessionId, readMessages);
5114
+ }
5115
+ function scheduleClearAndReindex(db, sessionId, readMessages) {
5116
+ heapHolder.reconciledSessions.delete(sessionId);
5117
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
5118
+ clearCompletedIncrementalKeys(sessionId);
5119
+ const existing = heapHolder.clearAndReindexScheduledSessions.get(sessionId);
5120
+ if (existing) {
5121
+ existing.source = readMessages;
5122
+ return;
5123
+ }
5124
+ const pending = { source: readMessages };
5125
+ heapHolder.clearAndReindexScheduledSessions.set(sessionId, pending);
5126
+ scheduleAfterBootQuiet(() => {
5127
+ defer(() => {
5128
+ let cleared = false;
5129
+ runWithSessionLock(sessionId, () => {
5130
+ if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) !== pending)
5131
+ return;
5132
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
5133
+ heapHolder.reconciledSessions.delete(sessionId);
5134
+ clearCompletedIncrementalKeys(sessionId);
5135
+ clearIndexedMessages(db, sessionId);
5136
+ cleared = true;
5137
+ }).then(() => cleared ? reconcileSessionIndex(db, sessionId, pending.source) : undefined).catch((error) => {
5138
+ heapHolder.reconciledSessions.delete(sessionId);
5139
+ if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) === pending)
5140
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
5141
+ logIndexingError(sessionId, "clear and reindex", error);
5142
+ });
5143
+ });
5144
+ });
5145
+ }
5146
+ function getMessageIndexQueueHeapStats() {
5147
+ return {
5148
+ queueLength: heapHolder.reconciliationScheduledSessions.size + heapHolder.incrementalTimers.size + heapHolder.pendingIncrementalKeys.size,
5149
+ reconciliationScheduled: heapHolder.reconciliationScheduledSessions.size,
5150
+ incrementalTimers: heapHolder.incrementalTimers.size,
5151
+ pendingIncremental: heapHolder.pendingIncrementalKeys.size,
5152
+ activeSessionLocks: heapHolder.sessionLocks.size,
5153
+ completedIncrementalKeys: heapHolder.completedIncrementalKeys.size,
5154
+ activeBufferMessages: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + messages.length, 0),
5155
+ activeBufferBytes: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + serializedMessageBytes(messages), 0)
5156
+ };
5157
+ }
5158
+
4879
5159
  // src/shared/model-cache-ttl.ts
4880
5160
  var MODEL_CACHE_LIFETIMES = [
4881
5161
  {
@@ -5240,7 +5520,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
5240
5520
  return `${ownerMsgId}${TOOL_COMPOSITE_KEY_SEP}${callId}`;
5241
5521
  }
5242
5522
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
5243
- var GET_TAGS_VERSION_SQL = `SELECT tags_version FROM session_meta WHERE session_id = ?`;
5523
+ var GET_TAGS_VERSION_SQL = `SELECT
5524
+ (SELECT tags_version FROM session_meta WHERE session_id = ?) AS tags_version,
5525
+ (SELECT value FROM schema_migrations_meta WHERE key = ?) AS identity_revision`;
5244
5526
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
5245
5527
  var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
5246
5528
  var probeTagsVersionStatements = new WeakMap;
@@ -5361,10 +5643,16 @@ function createTagger() {
5361
5643
  const memCounter = heapHolder.counters.get(sessionId) ?? 0;
5362
5644
  const dbMax = getMaxTagNumberBySession(db, sessionId);
5363
5645
  const next = Math.max(memCounter, dbMax) + 1;
5646
+ const loaded = heapHolder.loadSignatures.get(sessionId);
5647
+ let ownedInitialIdentity;
5364
5648
  try {
5365
5649
  db.transaction(() => {
5650
+ const admitted = loaded?.db === db && loaded.identityRevision === null ? probeSignature(sessionId, db) : null;
5651
+ const initializesOwnedEpoch = admitted !== null && loaded !== undefined && admitted.identityRevision === null && admitted.tagsVersion === loaded.tagsVersion;
5366
5652
  getUpsertCounterStatement(db).run(sessionId, next, getHarness());
5367
5653
  insertTag(db, sessionId, messageId, type, byteSize, next, reasoningByteSize, toolName, inputByteSize, toolOwnerMessageId, entryFingerprint, tokenCounts);
5654
+ if (initializesOwnedEpoch)
5655
+ ownedInitialIdentity = probeSignature(sessionId, db).identityRevision;
5368
5656
  }).immediate();
5369
5657
  } catch (error) {
5370
5658
  if (!isUniqueConstraintError(error)) {
@@ -5385,6 +5673,8 @@ function createTagger() {
5385
5673
  const signature = heapHolder.loadSignatures.get(sessionId);
5386
5674
  if (signature?.db === db) {
5387
5675
  signature.tagsVersion += 1;
5676
+ if (signature === loaded && ownedInitialIdentity !== undefined)
5677
+ signature.identityRevision = ownedInitialIdentity;
5388
5678
  }
5389
5679
  if (type === "tool") {
5390
5680
  getSessionToolAccounting(sessionId).set(next, {
@@ -5487,15 +5777,16 @@ function createTagger() {
5487
5777
  return heapHolder.counters.get(sessionId) ?? 0;
5488
5778
  }
5489
5779
  function probeSignature(sessionId, db) {
5490
- const row = getProbeTagsVersionStatement(db).get(sessionId);
5780
+ const row = getProbeTagsVersionStatement(db).get(sessionId, tagIdentityRevisionKey(sessionId));
5491
5781
  return {
5492
- tagsVersion: row?.tags_version ?? 0
5782
+ tagsVersion: row?.tags_version ?? 0,
5783
+ identityRevision: row?.identity_revision ?? null
5493
5784
  };
5494
5785
  }
5495
5786
  function initFromDb(sessionId, db, floor = 0) {
5496
5787
  const probe = probeSignature(sessionId, db);
5497
5788
  const cached = heapHolder.loadSignatures.get(sessionId);
5498
- if (cached !== undefined && cached.db === db && cached.tagsVersion === probe.tagsVersion && cached.floor === floor) {
5789
+ if (cached !== undefined && cached.db === db && cached.tagsVersion === probe.tagsVersion && cached.identityRevision === probe.identityRevision && cached.floor === floor) {
5499
5790
  return;
5500
5791
  }
5501
5792
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
@@ -5528,6 +5819,7 @@ function createTagger() {
5528
5819
  heapHolder.loadSignatures.set(sessionId, {
5529
5820
  db,
5530
5821
  tagsVersion: probe.tagsVersion,
5822
+ identityRevision: probe.identityRevision,
5531
5823
  floor
5532
5824
  });
5533
5825
  }
@@ -5535,6 +5827,10 @@ function createTagger() {
5535
5827
  const signature = heapHolder.loadSignatures.get(sessionId);
5536
5828
  return signature?.db === db ? signature.tagsVersion : undefined;
5537
5829
  }
5830
+ function getLoadedTagIdentityRevision(sessionId, db) {
5831
+ const signature = heapHolder.loadSignatures.get(sessionId);
5832
+ return signature?.db === db ? signature.identityRevision : undefined;
5833
+ }
5538
5834
  function cleanup(sessionId) {
5539
5835
  heapHolder.counters.delete(sessionId);
5540
5836
  heapHolder.assignments.delete(sessionId);
@@ -5577,6 +5873,7 @@ function createTagger() {
5577
5873
  getCounter,
5578
5874
  initFromDb,
5579
5875
  getLoadedTagsVersion,
5876
+ getLoadedTagIdentityRevision,
5580
5877
  cleanup,
5581
5878
  getHeapStats
5582
5879
  };
@@ -5936,25 +6233,35 @@ function forgetCachedPrefix(sessionId) {
5936
6233
  clearInjectionCache(sessionId);
5937
6234
  resetPrefixTrimFallbackState(sessionId);
5938
6235
  }
5939
- function dropCompartmentsAfter(db, sessionId, anchor, holderId) {
6236
+ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = false, onRetire) {
5940
6237
  db.exec("BEGIN IMMEDIATE");
5941
6238
  let finished = false;
6239
+ let clearedMarker = null;
5942
6240
  try {
5943
6241
  if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
5944
6242
  db.exec("ROLLBACK");
5945
6243
  finished = true;
5946
6244
  return null;
5947
6245
  }
5948
- const dropped = db.prepare("SELECT id, sequence FROM compartments WHERE session_id = ? AND sequence > ? ORDER BY sequence").all(sessionId, anchor.sequence);
6246
+ const dropped = db.prepare("SELECT id, sequence FROM compartments WHERE session_id = ? AND sequence > ? ORDER BY sequence").all(sessionId, anchor?.sequence ?? -1);
6247
+ onRetire?.();
5949
6248
  const deleteEmbeddings = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE compartment_id = ?");
5950
6249
  for (const row of dropped)
5951
6250
  deleteEmbeddings.run(row.id);
5952
- db.prepare("DELETE FROM compartments WHERE session_id = ? AND sequence > ?").run(sessionId, anchor.sequence);
5953
- db.prepare("DELETE FROM compression_depth WHERE session_id = ? AND message_ordinal > ?").run(sessionId, anchor.endMessage);
6251
+ db.prepare("DELETE FROM compartments WHERE session_id = ? AND sequence > ?").run(sessionId, anchor?.sequence ?? -1);
6252
+ db.prepare("DELETE FROM compression_depth WHERE session_id = ? AND message_ordinal > ?").run(sessionId, anchor?.endMessage ?? 0);
6253
+ if (activeBranch) {
6254
+ db.prepare("UPDATE session_meta SET prior_boundary_ordinal = ? WHERE session_id = ?").run((anchor?.endMessage ?? 0) + 1, sessionId);
6255
+ const marker = getPendingPiCompactionMarkerState(db, sessionId);
6256
+ if (marker && clearPendingPiCompactionMarkerStateIf(db, sessionId, marker, null))
6257
+ clearedMarker = marker;
6258
+ }
5954
6259
  queueM0Mutation(db, { sessionId, mutationType: "recomp_boundary_change" });
5955
6260
  clearCachedM0M1(db, sessionId);
5956
6261
  db.exec("COMMIT");
5957
6262
  finished = true;
6263
+ if (clearedMarker)
6264
+ logPiCompactionMarkerClear(sessionId, clearedMarker, "active-branch-retired");
5958
6265
  return dropped.map((row) => row.sequence);
5959
6266
  } finally {
5960
6267
  if (!finished) {
@@ -5965,24 +6272,38 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId) {
5965
6272
  }
5966
6273
  }
5967
6274
  function repairMissingHistoryBoundary(args) {
5968
- const { db, sessionId, isInHostStore } = args;
5969
- const compartments = getCompartments(db, sessionId);
6275
+ const { db, sessionId, isInHostStore, scope = "store" } = args;
6276
+ 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);
5970
6277
  let latestIndex = compartments.length - 1;
5971
6278
  while (latestIndex >= 0 && !compartments[latestIndex]?.endMessageId)
5972
6279
  latestIndex -= 1;
5973
6280
  if (latestIndex < 0)
5974
6281
  return { kind: "intact" };
6282
+ if (scope === "active-branch") {
6283
+ for (let index = 0;index <= latestIndex; index += 1) {
6284
+ const endId = compartments[index]?.endMessageId;
6285
+ if (!endId)
6286
+ continue;
6287
+ const present = isInHostStore(endId);
6288
+ if (present === null)
6289
+ return { kind: "unknown" };
6290
+ if (!present) {
6291
+ latestIndex = index;
6292
+ break;
6293
+ }
6294
+ }
6295
+ }
5975
6296
  const latestEnd = compartments[latestIndex].endMessageId;
5976
6297
  const latestPresent = isInHostStore(latestEnd);
5977
6298
  if (latestPresent === null)
5978
6299
  return { kind: "unknown" };
5979
6300
  if (!latestPresent) {
5980
6301
  const missing = latestEnd;
5981
- const problem = `newest compartment end ${latestEnd} is not in the host store`;
6302
+ const problem = `compartment end ${latestEnd} is not in ${scope === "active-branch" ? "the active host branch" : "the host store"}`;
5982
6303
  const anchor = findAnchor(compartments, latestIndex, isInHostStore);
5983
6304
  if (anchor === "unknown")
5984
6305
  return { kind: "unknown" };
5985
- if (anchor === null) {
6306
+ if (anchor === null && scope === "store") {
5986
6307
  sessionLog(sessionId, `history boundary unresolved: ${problem} and no older compartment end is in the host store; nothing to re-anchor on`);
5987
6308
  return { kind: "unresolved", reason: "no-anchor", missingEndMessageId: missing };
5988
6309
  }
@@ -5994,8 +6315,15 @@ function repairMissingHistoryBoundary(args) {
5994
6315
  return { kind: "unresolved", reason: "busy", missingEndMessageId: missing };
5995
6316
  }
5996
6317
  let dropped;
6318
+ let anchorForDrop = anchor;
5997
6319
  try {
5998
- dropped = dropCompartmentsAfter(db, sessionId, anchor, holderId);
6320
+ if (scope === "active-branch" && anchor) {
6321
+ const row = db.prepare("SELECT end_message AS endMessage FROM compartments WHERE session_id = ? AND sequence = ?").get(sessionId, anchor.sequence);
6322
+ if (!row)
6323
+ return { kind: "unresolved", reason: "busy", missingEndMessageId: missing };
6324
+ anchorForDrop = { ...anchor, endMessage: row.endMessage };
6325
+ }
6326
+ dropped = dropCompartmentsAfter(db, sessionId, anchorForDrop, holderId, scope === "active-branch", () => args.onRetire?.(compartments.slice(latestIndex), anchor));
5999
6327
  } finally {
6000
6328
  releaseCompartmentLeaseBestEffort(db, sessionId, holderId);
6001
6329
  }
@@ -6004,14 +6332,16 @@ function repairMissingHistoryBoundary(args) {
6004
6332
  }
6005
6333
  forgetCachedPrefix(sessionId);
6006
6334
  const range = dropped.length === 0 ? "none" : `${dropped[0]}-${dropped[dropped.length - 1]} (${dropped.length})`;
6007
- sessionLog(sessionId, `history boundary repair: ${problem}; re-anchored on compartment ${anchor.sequence} ending at ${anchor.endMessageId} (ordinal ${anchor.endMessage}); removed compartments ${range} for the historian to rebuild; the cached prefix is rebuilt on this pass`);
6335
+ sessionLog(sessionId, `history boundary repair: ${problem}; re-anchored on ${anchorForDrop ? `compartment ${anchorForDrop.sequence} ending at ${anchorForDrop.endMessageId} (ordinal ${anchorForDrop.endMessage})` : "the start of history"}; removed compartments ${range} for the historian to rebuild; the cached prefix is rebuilt on this pass`);
6008
6336
  return {
6009
6337
  kind: "repaired",
6010
6338
  missingEndMessageId: missing,
6011
- anchorEndMessageId: anchor.endMessageId,
6339
+ anchorEndMessageId: anchor?.endMessageId ?? "",
6012
6340
  droppedSequences: dropped
6013
6341
  };
6014
6342
  }
6343
+ if (scope === "active-branch")
6344
+ return { kind: "intact" };
6015
6345
  const baseline = readBaseline(db, sessionId);
6016
6346
  if (!baseline.hasCachedM0 || baseline.boundary === null || baseline.boundary === latestEnd) {
6017
6347
  return { kind: "intact" };
@@ -6028,175 +6358,15 @@ function repairMissingHistoryBoundary(args) {
6028
6358
  };
6029
6359
  }
6030
6360
 
6031
- // src/features/magic-context/message-index-async.ts
6032
- function isDatabaseLockedError(error) {
6033
- if (!error || typeof error !== "object")
6034
- return false;
6035
- const e = error;
6036
- if (typeof e.code === "string") {
6037
- if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
6038
- return true;
6039
- }
6040
- if (typeof e.message === "string") {
6041
- if (/database is locked/i.test(e.message))
6042
- return true;
6043
- if (/sqlite_(busy|locked)/i.test(e.message))
6044
- return true;
6045
- }
6046
- return false;
6047
- }
6048
- var RECONCILIATION_BATCH_SIZE = 100;
6049
-
6050
- class MagicContextMessageIndexHeapHolder {
6051
- reconciledSessions = new Set;
6052
- reconciliationScheduledSessions = new Set;
6053
- clearAndReindexScheduledSessions = new Map;
6054
- sessionLocks = new Map;
6055
- incrementalTimers = new Map;
6056
- pendingIncrementalKeys = new Set;
6057
- completedIncrementalKeys = new Set;
6058
- activeReconcilerBuffers = new Map;
6059
- }
6060
- var heapHolder = new MagicContextMessageIndexHeapHolder;
6061
- function defer(fn) {
6062
- const immediate = globalThis.setImmediate;
6063
- if (typeof immediate === "function") {
6064
- immediate(fn);
6065
- return;
6066
- }
6067
- setTimeout(fn, 0);
6068
- }
6069
- function yieldToEventLoop() {
6070
- return new Promise((resolve) => defer(resolve));
6071
- }
6072
- function runWithSessionLock(sessionId, operation) {
6073
- const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
6074
- const run = previous.catch(() => {
6075
- return;
6076
- }).then(async () => {
6077
- await operation();
6078
- });
6079
- heapHolder.sessionLocks.set(sessionId, run);
6080
- run.finally(() => {
6081
- if (heapHolder.sessionLocks.get(sessionId) === run) {
6082
- heapHolder.sessionLocks.delete(sessionId);
6083
- }
6084
- }).catch(() => {
6085
- return;
6086
- });
6087
- return run;
6088
- }
6089
- function logIndexingError(sessionId, action, error) {
6090
- if (isDatabaseLockedError(error)) {
6091
- sessionLog(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
6092
- return;
6093
- }
6094
- sessionLog(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
6095
- log(`[message-index-async] ${action} failed for ${sessionId}:`, error);
6096
- }
6097
- function serializedMessageBytes(messages) {
6098
- try {
6099
- return Buffer.byteLength(JSON.stringify(messages));
6100
- } catch {
6101
- return 0;
6102
- }
6103
- }
6104
- function detectIndexMisalignment(db, sessionId, cursor, finalWatermark, readMessages, fallbackSnapshot) {
6105
- if (finalWatermark <= 0)
6106
- return null;
6107
- const indexedWatermark = getLastIndexedOrdinal(db, sessionId);
6108
- if (indexedWatermark > finalWatermark) {
6109
- return `indexed watermark ${indexedWatermark} exceeds projection watermark ${finalWatermark}`;
6110
- }
6111
- if (cursor <= 0 || cursor > finalWatermark)
6112
- return null;
6113
- const indexedId = getIndexedMessageIdAtOrdinal(db, sessionId, cursor);
6114
- if (indexedId === null)
6115
- return null;
6116
- const projectedId = readMessages.readPage ? readMessages.readPage(sessionId, cursor - 1, 1, finalWatermark)[0]?.id ?? null : fallbackSnapshot?.find((message) => message.ordinal === cursor)?.id ?? null;
6117
- if (projectedId === null || projectedId === indexedId)
6118
- return null;
6119
- return `indexed id '${indexedId}' at ordinal ${cursor} but projection has '${projectedId}'`;
6120
- }
6121
- async function reconcileSessionIndex(db, sessionId, readMessages) {
6122
- await runWithSessionLock(sessionId, async () => {
6123
- if (heapHolder.reconciledSessions.has(sessionId))
6124
- return;
6125
- let fallbackSnapshot = null;
6126
- try {
6127
- const fullReader = typeof readMessages === "function" ? readMessages : null;
6128
- const finalWatermark = readMessages.getCount ? readMessages.getCount(sessionId) : (fallbackSnapshot = fullReader?.(sessionId) ?? []).length;
6129
- let cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
6130
- let after;
6131
- const misalignment = detectIndexMisalignment(db, sessionId, cursor, finalWatermark, readMessages, fallbackSnapshot);
6132
- if (misalignment !== null) {
6133
- sessionLog(sessionId, `message FTS index misaligned (${misalignment}); clearing for full re-index`);
6134
- withSqliteBackgroundWriter(() => clearIndexedMessages(db, sessionId));
6135
- cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
6136
- }
6137
- while (cursor < finalWatermark) {
6138
- const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
6139
- const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
6140
- const retainedMessages = fallbackSnapshot ?? messages;
6141
- heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
6142
- withSqliteBackgroundWriter(() => indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd));
6143
- const nextCursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
6144
- if (nextCursor <= cursor)
6145
- break;
6146
- cursor = nextCursor;
6147
- const last = messages.at(-1);
6148
- after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
6149
- if (cursor < finalWatermark) {
6150
- await yieldToEventLoop();
6151
- }
6152
- }
6153
- if (isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
6154
- heapHolder.reconciledSessions.add(sessionId);
6155
- }
6156
- } finally {
6157
- heapHolder.activeReconcilerBuffers.delete(sessionId);
6158
- }
6159
- });
6160
- }
6161
- function scheduleReconciliation(db, sessionId, readMessages) {
6162
- if (heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
6163
- return;
6164
- }
6165
- heapHolder.reconciliationScheduledSessions.add(sessionId);
6166
- scheduleAfterBootQuiet(() => {
6167
- defer(() => {
6168
- reconcileSessionIndex(db, sessionId, readMessages).catch((error) => {
6169
- logIndexingError(sessionId, "reconciliation", error);
6170
- if (isDatabaseLockedError(error))
6171
- setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000).unref();
6172
- }).finally(() => {
6173
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
6174
- });
6175
- });
6176
- });
6177
- }
6178
- function getMessageIndexQueueHeapStats() {
6179
- return {
6180
- queueLength: heapHolder.reconciliationScheduledSessions.size + heapHolder.incrementalTimers.size + heapHolder.pendingIncrementalKeys.size,
6181
- reconciliationScheduled: heapHolder.reconciliationScheduledSessions.size,
6182
- incrementalTimers: heapHolder.incrementalTimers.size,
6183
- pendingIncremental: heapHolder.pendingIncrementalKeys.size,
6184
- activeSessionLocks: heapHolder.sessionLocks.size,
6185
- completedIncrementalKeys: heapHolder.completedIncrementalKeys.size,
6186
- activeBufferMessages: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + messages.length, 0),
6187
- activeBufferBytes: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + serializedMessageBytes(messages), 0)
6188
- };
6189
- }
6190
-
6191
- // src/plugin/sidebar-snapshot-cache.ts
6192
- var MAX_CACHED_SESSIONS = 100;
6193
- var STALE_SNAPSHOT_AGE_MS = 5 * 60 * 1000;
6194
- var cache = new BoundedSessionMap(MAX_CACHED_SESSIONS);
6195
- function applyStickySnapshotCache(sessionId, fresh) {
6196
- const now = Date.now();
6197
- if (fresh.inputTokens > 0) {
6198
- cache.set(sessionId, { snapshot: fresh, cachedAt: now });
6199
- return fresh;
6361
+ // src/plugin/sidebar-snapshot-cache.ts
6362
+ var MAX_CACHED_SESSIONS = 100;
6363
+ var STALE_SNAPSHOT_AGE_MS = 5 * 60 * 1000;
6364
+ var cache = new BoundedSessionMap(MAX_CACHED_SESSIONS);
6365
+ function applyStickySnapshotCache(sessionId, fresh) {
6366
+ const now = Date.now();
6367
+ if (fresh.inputTokens > 0) {
6368
+ cache.set(sessionId, { snapshot: fresh, cachedAt: now });
6369
+ return fresh;
6200
6370
  }
6201
6371
  const cached = cache.peek(sessionId);
6202
6372
  if (!cached) {
@@ -6374,6 +6544,24 @@ function createUnifiedSearchDiagnostics() {
6374
6544
  suppressedLiveMessageMatches: 0
6375
6545
  };
6376
6546
  }
6547
+ function searchMessageHistory(db, request) {
6548
+ const diagnostics = request.collectDiagnostics ? createUnifiedSearchDiagnostics() : undefined;
6549
+ const results = searchMessages({
6550
+ db,
6551
+ sessionId: request.sessionId,
6552
+ query: request.query,
6553
+ limit: request.limit,
6554
+ maxOrdinal: request.maxOrdinal,
6555
+ probes: request.probes,
6556
+ relaxedRecall: request.relaxedRecall,
6557
+ diagnostics,
6558
+ dateRange: request.dateRange
6559
+ });
6560
+ return {
6561
+ results,
6562
+ suppressedLiveMessageMatches: diagnostics?.suppressedLiveMessageMatches ?? 0
6563
+ };
6564
+ }
6377
6565
  function normalizeLimit(limit) {
6378
6566
  if (typeof limit !== "number" || !Number.isFinite(limit)) {
6379
6567
  return DEFAULT_UNIFIED_SEARCH_LIMIT;
@@ -6900,15 +7088,18 @@ function searchMessagesInSnapshot(args, sessionFirst) {
6900
7088
  args.diagnostics.suppressedLiveMessageMatches = outcome.suppressedCount;
6901
7089
  }
6902
7090
  const rows = outcome.rows.length > 0 || !args.relaxedRecall ? outcome.rows : runMessageFtsQuery(args.db, args.sessionId, relaxedFtsQuery(args.query), fetchLimit, cutoff, args.dateRange, sessionFirst);
6903
- const filtered = rows.slice(0, args.limit);
6904
- return filtered.map((row, rank) => ({
6905
- source: "message",
6906
- content: previewText(row.content),
6907
- score: linearDecayScore(rank, filtered.length),
6908
- messageOrdinal: row.messageOrdinal,
6909
- messageId: row.messageId,
6910
- role: row.role
6911
- }));
7091
+ const renderRows = (limit) => {
7092
+ const filtered = rows.slice(0, limit);
7093
+ return filtered.map((row, rank) => ({
7094
+ source: "message",
7095
+ content: previewText(row.content),
7096
+ score: linearDecayScore(rank, filtered.length),
7097
+ messageOrdinal: row.messageOrdinal,
7098
+ messageId: row.messageId,
7099
+ role: row.role
7100
+ }));
7101
+ };
7102
+ return renderRows(args.limit);
6912
7103
  }
6913
7104
  const sanitizedProbes = probes.map((probe) => ({ probe, query: sanitizeFtsQuery(probe) })).filter((entry) => entry.query.length > 0);
6914
7105
  const corpusSize = getIndexedMessageCorpusSize(args.db, args.sessionId, cutoff);
@@ -6948,39 +7139,42 @@ function searchMessagesInSnapshot(args, sessionFirst) {
6948
7139
  queryLists.push({ rows: rowsByQuery[queryIndex] ?? [], weight });
6949
7140
  queryIndex += 1;
6950
7141
  });
6951
- const fused = new Map;
6952
- for (const list of queryLists) {
6953
- list.rows.forEach((row, rank) => {
6954
- const rrf = list.weight / (RRF_K + rank);
6955
- const existing = fused.get(row.messageId);
6956
- if (existing) {
6957
- existing.score += rrf;
6958
- } else {
6959
- fused.set(row.messageId, { row, score: rrf });
7142
+ const rankProbeLists = (rowLimit, resultLimit) => {
7143
+ const fused = new Map;
7144
+ for (const list of queryLists) {
7145
+ list.rows.slice(0, rowLimit).forEach((row, rank) => {
7146
+ const rrf = list.weight / (RRF_K + rank);
7147
+ const existing = fused.get(row.messageId);
7148
+ if (existing) {
7149
+ existing.score += rrf;
7150
+ } else {
7151
+ fused.set(row.messageId, { row, score: rrf });
7152
+ }
7153
+ });
7154
+ }
7155
+ for (const entry of fused.values()) {
7156
+ let best = 0;
7157
+ for (const probe of probes) {
7158
+ const weight = probeWeights.get(probe) ?? 0;
7159
+ if (weight > best && containsProbeVerbatim(entry.row.content, [probe])) {
7160
+ best = weight;
7161
+ }
6960
7162
  }
6961
- });
6962
- }
6963
- for (const entry of fused.values()) {
6964
- let best = 0;
6965
- for (const probe of probes) {
6966
- const weight = probeWeights.get(probe) ?? 0;
6967
- if (weight > best && containsProbeVerbatim(entry.row.content, [probe])) {
6968
- best = weight;
7163
+ if (best > 0) {
7164
+ entry.score += best * VERBATIM_RANK_BONUS;
6969
7165
  }
6970
7166
  }
6971
- if (best > 0) {
6972
- entry.score += best * VERBATIM_RANK_BONUS;
6973
- }
6974
- }
6975
- const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
6976
- return ranked.map((entry, rank) => ({
6977
- source: "message",
6978
- content: previewText(entry.row.content),
6979
- score: linearDecayScore(rank, ranked.length),
6980
- messageOrdinal: entry.row.messageOrdinal,
6981
- messageId: entry.row.messageId,
6982
- role: entry.row.role
6983
- }));
7167
+ const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, resultLimit);
7168
+ return ranked.map((entry, rank) => ({
7169
+ source: "message",
7170
+ content: previewText(entry.row.content),
7171
+ score: linearDecayScore(rank, ranked.length),
7172
+ messageOrdinal: entry.row.messageOrdinal,
7173
+ messageId: entry.row.messageId,
7174
+ role: entry.row.role
7175
+ }));
7176
+ };
7177
+ return rankProbeLists(fetchLimit, args.limit);
6984
7178
  }
6985
7179
  var NOTE_SEARCHABLE_STATUSES = ["active"];
6986
7180
  var MAX_NOTE_KEYWORD_SCORE = 3.5;
@@ -7270,17 +7464,28 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
7270
7464
  const runPrimers = activeSources.has("primer") && memoryFeatureEnabled;
7271
7465
  const runNotes = activeSources.has("note");
7272
7466
  const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
7273
- const messageResults = runMessages ? searchMessages({
7274
- db,
7275
- sessionId,
7276
- query: trimmedQuery,
7277
- limit: tierLimit,
7278
- maxOrdinal: options.maxMessageOrdinal,
7279
- probes: messageProbes,
7280
- relaxedRecall: options.explicitSearch,
7281
- diagnostics: options.diagnostics,
7282
- dateRange
7283
- }) : [];
7467
+ let messageResults = [];
7468
+ if (runMessages) {
7469
+ const messageRequest = {
7470
+ sessionId,
7471
+ query: trimmedQuery,
7472
+ limit: tierLimit,
7473
+ maxOrdinal: options.maxMessageOrdinal,
7474
+ probes: messageProbes,
7475
+ relaxedRecall: options.explicitSearch,
7476
+ collectDiagnostics: options.diagnostics !== undefined,
7477
+ dateRange
7478
+ };
7479
+ const delegated = options.searchMessageHistory ? await options.searchMessageHistory(messageRequest).catch((error) => {
7480
+ log(`[search] off-thread message search failed: ${error instanceof Error ? error.message : String(error)}`);
7481
+ return null;
7482
+ }) : null;
7483
+ const outcome = delegated ?? searchMessageHistory(db, messageRequest);
7484
+ if (options.diagnostics) {
7485
+ options.diagnostics.suppressedLiveMessageMatches = outcome.suppressedLiveMessageMatches;
7486
+ }
7487
+ messageResults = outcome.results;
7488
+ }
7284
7489
  const workspace = resolveSearchWorkspaceContext(db, projectPath);
7285
7490
  const [memoryOutcome, primerResults, noteResults] = await Promise.all([
7286
7491
  runMemory ? searchMemories({
@@ -7425,7 +7630,7 @@ ${diagnosticLines.join(`
7425
7630
  bodyParts.push(diagnosticLines.join(`
7426
7631
  `));
7427
7632
  if (results.some((result) => result.source === "message")) {
7428
- bodyParts.push("Use mmc_expand(start, end) with the range from any message result above to read the full conversation context.");
7633
+ bodyParts.push("Use mmc_expand(start, end) with the range from any message result above to read the conversation context. If the host no longer holds those messages, mmc_expand returns indexed text from the search archive only, with no surrounding messages or tool output.");
7429
7634
  }
7430
7635
  if (results.some((result) => result.source === "note" && result.anchorOrdinal !== null && result.sourceSessionId === currentSessionId)) {
7431
7636
  bodyParts.push(SEARCH_NOTE_EXPAND_HINT);
@@ -7437,23 +7642,6 @@ ${bodyParts.join(`
7437
7642
  `)}`;
7438
7643
  }
7439
7644
 
7440
- // src/shared/auto-search-lifecycle.ts
7441
- var pending = new Map;
7442
- function registerAutoSearchWork(sessionId) {
7443
- const controller = new AbortController;
7444
- const work = pending.get(sessionId) ?? new Set;
7445
- pending.set(sessionId, work);
7446
- work.add(controller);
7447
- return {
7448
- signal: controller.signal,
7449
- done: () => {
7450
- work.delete(controller);
7451
- if (pending.get(sessionId) === work && work.size === 0)
7452
- pending.delete(sessionId);
7453
- }
7454
- };
7455
- }
7456
-
7457
7645
  // src/hooks/magic-context/auto-search-deadline.ts
7458
7646
  var AUTO_SEARCH_TIMEOUT_MS = 3000;
7459
7647
  var operationDeadlines = new WeakMap;
@@ -8227,6 +8415,8 @@ async function runAutoSearchHint(args) {
8227
8415
  return AUTO_SEARCH_OK;
8228
8416
  }
8229
8417
  const replayServedDecision = () => {
8418
+ if (getUnpublishedAutoSearchNoHint(sessionId, userMsgId) !== undefined)
8419
+ return;
8230
8420
  const decisions = getAutoSearchHintDecisions(db, sessionId);
8231
8421
  const decision = decisions.find((entry) => entry.messageId === userMsgId);
8232
8422
  if (decision?.decision === "hint") {
@@ -8239,12 +8429,34 @@ async function runAutoSearchHint(args) {
8239
8429
  if (messages.length === 0 || messages[messages.length - 1].info.id !== userMsgId) {
8240
8430
  return AUTO_SEARCH_OK;
8241
8431
  }
8432
+ const unpublishedReason = getUnpublishedAutoSearchNoHint(sessionId, userMsgId);
8433
+ if (unpublishedReason !== undefined) {
8434
+ try {
8435
+ const outcome = appendAutoSearchHintDecision(db, sessionId, {
8436
+ messageId: userMsgId,
8437
+ decision: "no-hint",
8438
+ reason: unpublishedReason
8439
+ });
8440
+ if (outcome.ok)
8441
+ forgetUnpublishedAutoSearchNoHint(sessionId, userMsgId);
8442
+ } catch {}
8443
+ return AUTO_SEARCH_OK;
8444
+ }
8242
8445
  const writeNoHintAndReconcile = (reason) => {
8243
- const outcome = appendAutoSearchHintDecision(db, sessionId, {
8244
- messageId: userMsgId,
8245
- decision: "no-hint",
8246
- reason
8247
- });
8446
+ let outcome;
8447
+ try {
8448
+ outcome = appendAutoSearchHintDecision(db, sessionId, {
8449
+ messageId: userMsgId,
8450
+ decision: "no-hint",
8451
+ reason
8452
+ });
8453
+ } catch (error) {
8454
+ if (isTransientSqliteError(error)) {
8455
+ rememberUnpublishedAutoSearchNoHint(sessionId, userMsgId, reason);
8456
+ return { ok: false, kind: "storage-busy" };
8457
+ }
8458
+ throw error;
8459
+ }
8248
8460
  if (!outcome.ok)
8249
8461
  return { ok: false, kind: "cas-exhaustion" };
8250
8462
  if (outcome.kind === "already-present" && outcome.decision.decision === "hint") {
@@ -8281,8 +8493,11 @@ async function runAutoSearchHint(args) {
8281
8493
  });
8282
8494
  }, startedAt);
8283
8495
  } catch (error) {
8284
- log(`[auto-search] unified search failed for session ${sessionId} (will retry next pass): ${error instanceof Error ? error.message : String(error)}`);
8285
- return { ok: false, kind: "search-failure" };
8496
+ if (lifecycleSignal.aborted)
8497
+ return AUTO_SEARCH_OK;
8498
+ log(`[auto-search] unified search failed for session ${sessionId} (no-hint frozen for this turn): ${error instanceof Error ? error.message : String(error)}`);
8499
+ const outcome = writeNoHintAndReconcile("error");
8500
+ return outcome.ok ? { ok: false, kind: "search-failure" } : outcome;
8286
8501
  }
8287
8502
  if (results === null) {
8288
8503
  persistAutoSearchSkip(db, sessionId, userMsgId, "timeout");
@@ -8510,7 +8725,7 @@ function createChatMessageHook(args) {
8510
8725
  sessionLog(sessionId, `variant changed (${previousVariant} -> ${input.variant}), triggering flush`);
8511
8726
  args.historyRefreshSessions.add(sessionId);
8512
8727
  args.systemPromptRefreshSessions.add(sessionId);
8513
- args.pendingMaterializationSessions.add(sessionId);
8728
+ requestPendingMaterialization(args.pendingMaterializationSessions, sessionId, "variant_change");
8514
8729
  args.lastHeuristicsTurnId.delete(sessionId);
8515
8730
  } else {
8516
8731
  sessionLog(sessionId, `variant changed (${previousVariant} -> ${input.variant}) on ${providerID ?? "unknown"}/${modelID ?? "unknown"} without a proven natural cache bust; deferring flush to next natural bust`);
@@ -8635,8 +8850,12 @@ function createToolExecuteAfterHook(args) {
8635
8850
 
8636
8851
  // src/hooks/magic-context/active-anthropic-turn.ts
8637
8852
  function isInActiveAnthropicTurn(messages, index, anthropic) {
8853
+ const start = activeAnthropicTurnStart(messages, anthropic);
8854
+ return start !== undefined && index >= start;
8855
+ }
8856
+ function activeAnthropicTurnStart(messages, anthropic) {
8638
8857
  if (!anthropic)
8639
- return false;
8858
+ return;
8640
8859
  for (let user = messages.length - 1;user >= 0; user--) {
8641
8860
  const message = messages[user];
8642
8861
  if (!isRecord(message))
@@ -8650,9 +8869,9 @@ function isInActiveAnthropicTurn(messages, index, anthropic) {
8650
8869
  const parts = Array.isArray(message.parts) ? message.parts : Array.isArray(message.content) ? message.content : [];
8651
8870
  if (parts.length > 0 && parts.every((part) => isRecord(part) && (part.synthetic === true || part.ignored === true || ["tool_result", "toolResult", "tool-result"].includes(String(part.type)))))
8652
8871
  continue;
8653
- return index > user;
8872
+ return user + 1;
8654
8873
  }
8655
- return false;
8874
+ return;
8656
8875
  }
8657
8876
  function hasAnthropicReasoning(messages) {
8658
8877
  return messages.some((message) => {
@@ -8666,6 +8885,27 @@ function hasAnthropicReasoning(messages) {
8666
8885
  });
8667
8886
  }
8668
8887
 
8888
+ // src/shared/processed-image-eligibility.ts
8889
+ function createProcessedImageEligibility(messages, messageTagNumbers, options) {
8890
+ const ageTags = new Map;
8891
+ let nextTag;
8892
+ for (let index = messages.length - 1;index >= 0; index--) {
8893
+ const message = messages[index];
8894
+ const ownTag = messageTagNumbers.get(message);
8895
+ if (ownTag !== undefined && Number.isFinite(ownTag) && ownTag > 0)
8896
+ nextTag = ownTag;
8897
+ if (nextTag !== undefined)
8898
+ ageTags.set(message, nextTag);
8899
+ }
8900
+ return {
8901
+ ageTags,
8902
+ mayStrip: (message) => {
8903
+ const age = ageTags.get(message);
8904
+ return options.watermark > 0 && age !== undefined && age <= options.watermark && ((options.contextPercentage ?? 0) >= 95 || options.protectedCutoff == null || age < options.protectedCutoff);
8905
+ }
8906
+ };
8907
+ }
8908
+
8669
8909
  // src/hooks/magic-context/strip-content.ts
8670
8910
  var MARKER_ONLY_PATTERN = /^(?:(?:§\d+§|\[dropped(?: §\d+§)?\]|\[cleared\])\s*)+$/;
8671
8911
  function isMarkerOnlyText(text) {
@@ -8728,8 +8968,7 @@ function stripSystemInjectedMessages(messages, protectedTailStart, providerID, o
8728
8968
  }
8729
8969
  if (hasContentPart && allContentIsSystemInjection) {
8730
8970
  onFirstApplication?.(msg, 0);
8731
- msg.parts.length = 0;
8732
- msg.parts.push(makeWholeMessageSentinel(providerID));
8971
+ neutralizeWholeMessage(msg, providerID);
8733
8972
  stripped++;
8734
8973
  if (typeof msg.info.id === "string")
8735
8974
  sentineledIds.push(msg.info.id);
@@ -8787,8 +9026,7 @@ function stripDroppedPlaceholderMessages(messages, providerID, onFirstApplicatio
8787
9026
  }
8788
9027
  if (hasContentPart && !hasNonDroppedContent) {
8789
9028
  onFirstApplication?.(msg, 0);
8790
- msg.parts.length = 0;
8791
- msg.parts.push(makeWholeMessageSentinel(providerID));
9029
+ neutralizeWholeMessage(msg, providerID);
8792
9030
  stripped++;
8793
9031
  if (typeof msg.info.id === "string")
8794
9032
  sentineledIds.push(msg.info.id);
@@ -9187,7 +9425,7 @@ function stripReasoningFromMergedAssistants(messages, providerID, options) {
9187
9425
  if (providerID !== "anthropic")
9188
9426
  return 0;
9189
9427
  let stripped = 0;
9190
- const frozenParts = readFrozenMergedReasoningParts(options?.frozenMessageIds ?? new Set);
9428
+ const frozenParts = options?.frozenParts ?? readFrozenMergedReasoningParts(options?.frozenMessageIds ?? new Set);
9191
9429
  for (const message of messages) {
9192
9430
  if (message.info.role !== "assistant" || options?.restoreMessages?.has(message))
9193
9431
  continue;
@@ -9204,7 +9442,7 @@ function stripReasoningFromMergedAssistants(messages, providerID, options) {
9204
9442
  stripped++;
9205
9443
  }
9206
9444
  }
9207
- for (const entry of planMergedAssistantReasoningStrip(messages, options?.mutationExemptMessage, new Set(frozenParts.keys()), options?.frozenMessageIds ? options.restoreMessages : options?.protectedMessages)) {
9445
+ for (const entry of planMergedAssistantReasoningStrip(messages, options?.mutationExemptMessage, options?.frozenPartMessageIds ?? new Set(frozenParts.keys()), options?.frozenMessageIds ? options.restoreMessages : options?.protectedMessages)) {
9208
9446
  if (options?.frozenMessageIds) {
9209
9447
  const id = entry.message.info.id;
9210
9448
  if (typeof id !== "string" || !options.frozenMessageIds.has(id))
@@ -9217,50 +9455,142 @@ function stripReasoningFromMergedAssistants(messages, providerID, options) {
9217
9455
  }
9218
9456
  return stripped;
9219
9457
  }
9458
+ var emptyImageParts = new WeakMap;
9459
+ function getEmptyImageParts(messages) {
9460
+ return emptyImageParts.get(messages) ?? new Map;
9461
+ }
9462
+ function imageOmissionBreaksStructure(messages, index, omitted) {
9463
+ let before = index - 1;
9464
+ let after = index + 1;
9465
+ while (omitted.has(before))
9466
+ before--;
9467
+ while (omitted.has(after))
9468
+ after++;
9469
+ const previous = messages[before];
9470
+ const next = messages[after];
9471
+ return previous !== undefined && next !== undefined && previous.info.role === next.info.role;
9472
+ }
9220
9473
  function stripProcessedImages(messages, frozenIds, options) {
9221
- const { detect, watermark, messageTagNumbers } = options;
9474
+ const omissions = new Map;
9475
+ emptyImageParts.set(messages, omissions);
9476
+ const { detect, messageTagNumbers } = options;
9477
+ const eligibility = createProcessedImageEligibility(messages, messageTagNumbers, options);
9222
9478
  let stripped = 0;
9223
9479
  const newlyStrippedIds = [];
9480
+ const planned = [];
9481
+ const newMarkers = new Map;
9224
9482
  let hasAssistantResponse = false;
9225
9483
  for (let i = messages.length - 1;i >= 0; i--) {
9226
9484
  const msg = messages[i];
9485
+ const answered = hasAssistantResponse;
9227
9486
  if (msg.info.role === "assistant") {
9228
9487
  hasAssistantResponse = true;
9229
- continue;
9230
9488
  }
9231
- if (msg.info.role !== "user") {
9489
+ if (msg.info.role !== "user" && msg.info.role !== "assistant") {
9232
9490
  continue;
9233
9491
  }
9234
9492
  const id = typeof msg.info.id === "string" ? msg.info.id : undefined;
9235
9493
  const inFrozen = id !== undefined && frozenIds.has(id);
9236
- const maxTag = messageTagNumbers.get(msg) ?? 0;
9237
- const isNewDetection = !inFrozen && detect && hasAssistantResponse && id !== undefined && maxTag <= watermark;
9494
+ const isNewDetection = !inFrozen && detect && answered && id !== undefined && eligibility.mayStrip(msg) && !options.protectedMessageIds?.has(id);
9238
9495
  if (!inFrozen && !isNewDetection) {
9239
9496
  continue;
9240
9497
  }
9241
9498
  let touchedThisMsg = false;
9242
9499
  for (let j = 0;j < msg.parts.length; j++) {
9243
9500
  const part = msg.parts[j];
9244
- if (!isRecord(part) || part.type !== "file") {
9245
- continue;
9246
- }
9247
- if (typeof part.mime !== "string" || !part.mime.startsWith("image/")) {
9248
- continue;
9249
- }
9250
- if (typeof part.url === "string" && part.url.startsWith("data:") && part.url.length > 200) {
9251
- if (isNewDetection)
9252
- options.onFirstApplication?.(msg, j);
9253
- msg.parts[j] = makeSentinel(part);
9254
- stripped++;
9501
+ const tool = msg.info.role === "assistant" && isRecord(part) && part.type === "tool" && isRecord(part.state) && part.state.status === "completed";
9502
+ const images = tool && isRecord(part) && isRecord(part.state) && Array.isArray(part.state.attachments) ? part.state.attachments : msg.info.role === "user" ? [part] : [];
9503
+ if (images.some(isProcessedImagePart)) {
9504
+ planned.push({
9505
+ message: msg,
9506
+ partIndex: j,
9507
+ firstApplication: isNewDetection,
9508
+ tool
9509
+ });
9255
9510
  touchedThisMsg = true;
9256
9511
  }
9257
9512
  }
9258
9513
  if (touchedThisMsg && isNewDetection && id !== undefined) {
9259
9514
  newlyStrippedIds.push(id);
9515
+ newMarkers.set(id, msg.info.role === "assistant" ? "[image stripped]" : options.replacementText ?? "");
9516
+ }
9517
+ }
9518
+ const imageIndices = new Map;
9519
+ for (const { message, partIndex, tool } of planned) {
9520
+ if (tool)
9521
+ continue;
9522
+ const indices = imageIndices.get(message) ?? new Set;
9523
+ indices.add(partIndex);
9524
+ imageIndices.set(message, indices);
9525
+ }
9526
+ const emptiedRows = new Set;
9527
+ const newEmptyRows = [];
9528
+ for (let index = 0;index < messages.length; index++) {
9529
+ const message = messages[index];
9530
+ if (message.parts.every((part) => isRecord(part) && part.type === "text" && part.text === ""))
9531
+ emptiedRows.add(index);
9532
+ const id = message.info.id;
9533
+ const indices = imageIndices.get(message);
9534
+ if (!id || !indices)
9535
+ continue;
9536
+ const marker = options.frozenMarkers?.get(id) ?? newMarkers.get(id) ?? "";
9537
+ if (marker !== "" || !message.parts.every((part, partIndex) => indices.has(partIndex) || isRecord(part) && part.type === "text" && part.text === ""))
9538
+ continue;
9539
+ if (newMarkers.has(id))
9540
+ newEmptyRows.push({ index, id });
9541
+ else
9542
+ emptiedRows.add(index);
9543
+ }
9544
+ for (const { index, id } of newEmptyRows) {
9545
+ if (imageOmissionBreaksStructure(messages, index, emptiedRows))
9546
+ newMarkers.set(id, "[image stripped]");
9547
+ else
9548
+ emptiedRows.add(index);
9549
+ }
9550
+ const persisted = newlyStrippedIds.length === 0 || options.persistNewIds?.(newlyStrippedIds, newMarkers) !== false;
9551
+ if (!persisted)
9552
+ newlyStrippedIds.length = 0;
9553
+ for (const { message, partIndex, firstApplication, tool } of planned) {
9554
+ if (firstApplication && !persisted)
9555
+ continue;
9556
+ if (firstApplication)
9557
+ options.onFirstApplication?.(message, partIndex);
9558
+ const marker = tool ? "[image stripped]" : options.frozenMarkers?.get(message.info.id ?? "") ?? (firstApplication ? newMarkers.get(message.info.id ?? "") : "") ?? "";
9559
+ const original = message.parts[partIndex];
9560
+ if (tool && isRecord(original) && isRecord(original.state) && Array.isArray(original.state.attachments)) {
9561
+ const attachments = original.state.attachments;
9562
+ const count = attachments.filter(isProcessedImagePart).length;
9563
+ const output = typeof original.state.output === "string" ? original.state.output : "";
9564
+ message.parts[partIndex] = {
9565
+ ...original,
9566
+ state: {
9567
+ ...original.state,
9568
+ output: [output, ...Array(count).fill(marker)].filter(Boolean).join(`
9569
+ `),
9570
+ attachments: attachments.filter((part) => !isProcessedImagePart(part))
9571
+ }
9572
+ };
9573
+ stripped += count;
9574
+ } else {
9575
+ message.parts[partIndex] = { ...makeSentinel(original), text: marker };
9576
+ if (marker === "" && message.info.id) {
9577
+ const indices = omissions.get(message.info.id) ?? new Set;
9578
+ indices.add(partIndex);
9579
+ omissions.set(message.info.id, indices);
9580
+ }
9581
+ stripped++;
9260
9582
  }
9261
9583
  }
9262
9584
  return { stripped, newlyStrippedIds };
9263
9585
  }
9586
+ function isProcessedImagePart(part) {
9587
+ if (isRecord(part) && part.type === "media" && isRecord(part.media)) {
9588
+ const { source, mediaType } = part.media;
9589
+ const inlineLength = isRecord(source) ? source.type === "base64" && typeof source.data === "string" ? source.data.length : source.type === "bytes" && source.data instanceof Uint8Array ? Math.ceil(source.data.byteLength / 3) * 4 : 0 : 0;
9590
+ return typeof mediaType === "string" && mediaType.startsWith("image/") && inlineLength > 0 && inlineLength + `data:${mediaType};base64,`.length > 200;
9591
+ }
9592
+ return isRecord(part) && part.type === "file" && typeof part.mime === "string" && part.mime.startsWith("image/") && typeof part.url === "string" && part.url.startsWith("data:") && part.url.length > 200;
9593
+ }
9264
9594
 
9265
9595
  // src/hooks/magic-context/reasoning-removal.ts
9266
9596
  var REMOVABLE_REASONING_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
@@ -9510,7 +9840,7 @@ function armLatestThinkingRecovery(db, sessionId) {
9510
9840
  ensureSessionMetaRow(db, sessionId);
9511
9841
  const current = getThinkingBindingRecoveryTarget(db, sessionId);
9512
9842
  const anchor = lastPassTurnAnchor.get(sessionId);
9513
- const next = current?.startsWith(ACTIVE) ? UNRECOVERABLE + ":" + current.slice(ACTIVE.length) : current?.startsWith(UNRECOVERABLE) || current?.startsWith(ARMED) ? current : anchor !== undefined ? ARMED + anchor : LATEST_THINKING_RESTORE;
9843
+ const next = current?.startsWith(ACTIVE) ? `${UNRECOVERABLE}:${current.slice(ACTIVE.length)}` : current?.startsWith(UNRECOVERABLE) || current?.startsWith(ARMED) ? current : anchor !== undefined ? `${ARMED}${anchor}` : LATEST_THINKING_RESTORE;
9514
9844
  db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = ? WHERE session_id = ?").run(next, sessionId);
9515
9845
  }
9516
9846
  function clone(value) {
@@ -9543,12 +9873,12 @@ function prepareLatestThinkingRecovery(args) {
9543
9873
  let target;
9544
9874
  try {
9545
9875
  target = getThinkingBindingRecoveryTarget(args.db, args.sessionId);
9546
- } catch (error) {
9876
+ } catch {
9547
9877
  if (knownRejectedSessions.has(args.sessionId))
9548
9878
  throw contextRefusalError(LATEST_THINKING_UNSAFE);
9549
9879
  return { restore: false, ended: false };
9550
9880
  }
9551
- if (!target || !target.startsWith(LATEST_THINKING_RESTORE)) {
9881
+ if (!target?.startsWith(LATEST_THINKING_RESTORE)) {
9552
9882
  knownRejectedSessions.delete(args.sessionId);
9553
9883
  return { restore: false, ended: false };
9554
9884
  }
@@ -9559,7 +9889,7 @@ function prepareLatestThinkingRecovery(args) {
9559
9889
  return { restore: false, ended: false };
9560
9890
  }
9561
9891
  knownRejectedSessions.add(args.sessionId);
9562
- const savedAnchor = target.startsWith(ACTIVE) ? target.slice(ACTIVE.length) : target.startsWith(UNRECOVERABLE + ":") ? target.slice(UNRECOVERABLE.length + 1) : undefined;
9892
+ const savedAnchor = target.startsWith(ACTIVE) ? target.slice(ACTIVE.length) : target.startsWith(`${UNRECOVERABLE}:`) ? target.slice(UNRECOVERABLE.length + 1) : undefined;
9563
9893
  if (savedAnchor !== undefined && savedAnchor !== anchor) {
9564
9894
  args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = '' WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(args.sessionId, target);
9565
9895
  knownRejectedSessions.delete(args.sessionId);
@@ -9572,7 +9902,7 @@ function prepareLatestThinkingRecovery(args) {
9572
9902
  }
9573
9903
  const active = args.messages.slice(start);
9574
9904
  if (!active.some((message) => args.parts(message).some(replayableOriginal)))
9575
- throw contextRefusalError(LATEST_THINKING_UNSAFE + " [missing-thinking-original]");
9905
+ throw contextRefusalError(`${LATEST_THINKING_UNSAFE} [missing-thinking-original]`);
9576
9906
  const ids = new Map(args.messages.map((message, index) => [args.id(message, index), index]));
9577
9907
  for (const decision of getMergedReasoningStrippedIds(args.db, args.sessionId)) {
9578
9908
  if (decision === "binding_mismatch_order:end" || decision === TOOL_SWEEP_SCOPED_MARKER)
@@ -9580,7 +9910,7 @@ function prepareLatestThinkingRecovery(args) {
9580
9910
  const id = decodeMergedReasoningParts(decision)?.[0] ?? (decision.startsWith("binding_mismatch:") ? decision.slice("binding_mismatch:".length) : decision);
9581
9911
  const index = ids.get(id);
9582
9912
  if (index === undefined || index >= start && !args.parts(args.messages[index]).some(replayableOriginal))
9583
- throw contextRefusalError(LATEST_THINKING_UNSAFE + ` [missing-original:${id}]`);
9913
+ throw contextRefusalError(`${LATEST_THINKING_UNSAFE} [missing-original:${id}]`);
9584
9914
  }
9585
9915
  return { restore: true, ended: false };
9586
9916
  }
@@ -9613,964 +9943,6 @@ function armLatestThinkingRecoveryFromError(args) {
9613
9943
  return true;
9614
9944
  }
9615
9945
 
9616
- // src/hooks/magic-context/lkg-measured-request.ts
9617
- import { createHash as createHash3 } from "node:crypto";
9618
-
9619
- // src/shared/provider-response-completion.ts
9620
- function providerResponseFailed(input) {
9621
- if (input.error !== undefined && input.error !== null && input.error !== "")
9622
- return true;
9623
- const finish = typeof input.finish === "string" ? input.finish.trim().toLowerCase() : "";
9624
- return ["error", "failed", "abort", "aborted", "interrupted", "cancelled", "canceled"].includes(finish);
9625
- }
9626
- function isSuccessfulProviderCompletion(input) {
9627
- if (providerResponseFailed(input))
9628
- return false;
9629
- return typeof input.completedAt === "number" && Number.isFinite(input.completedAt) && input.completedAt > 0 || typeof input.finish === "string" && input.finish.trim().length > 0;
9630
- }
9631
-
9632
- // src/hooks/magic-context/lkg-measured-request.ts
9633
- var requests = new BoundedSessionMap(1000);
9634
- var claimedRequests = new WeakSet;
9635
- var captures = new BoundedSessionMap(1000);
9636
- function lkgProviderInputTotal(tokens) {
9637
- const counts = [tokens?.input ?? 0, tokens?.cache?.read ?? 0, tokens?.cache?.write ?? 0];
9638
- return counts.every((count) => Number.isSafeInteger(count) && count >= 0) ? counts.reduce((sum, count) => sum + count, 0) : Number.NaN;
9639
- }
9640
- function identity(slot) {
9641
- return createHash3("sha256").update(JSON.stringify([
9642
- slot.capturedAt,
9643
- slot.captureSequence,
9644
- slot.rowVersion,
9645
- slot.modelKey,
9646
- slot.providerKey,
9647
- slot.inputIdSeq,
9648
- slot.inputContentDigests
9649
- ])).update(slot.jsonPrefix).digest("hex");
9650
- }
9651
- function envelope(modelKey, systemPromptTokens, agentName) {
9652
- const slash = modelKey.indexOf("/");
9653
- return JSON.stringify([
9654
- modelKey,
9655
- systemPromptTokens,
9656
- agentName,
9657
- getMeasuredToolDefinitionTokens(modelKey.slice(0, slash), modelKey.slice(slash + 1), agentName)
9658
- ]);
9659
- }
9660
- function beginV2LkgRequest(sessionId, modelKey, previousResponseId) {
9661
- requests.set(sessionId, { kind: "v2", previousResponseId, startedAt: Date.now(), modelKey });
9662
- }
9663
- function claimLkgRequestIdentity(sessionId) {
9664
- const request = requests.get(sessionId);
9665
- if (!request || claimedRequests.has(request))
9666
- return;
9667
- claimedRequests.add(request);
9668
- return request;
9669
- }
9670
- function noteCapturedLkgRequest(args) {
9671
- const request = args.request;
9672
- if (!request || request.modelKey !== args.slot.modelKey) {
9673
- captures.delete(args.sessionId);
9674
- return;
9675
- }
9676
- captures.set(args.sessionId, {
9677
- request,
9678
- slot: identity(args.slot),
9679
- prefixCount: JSON.parse(args.slot.jsonPrefix).length,
9680
- envelope: envelope(request.modelKey, args.systemPromptTokens, args.agentName)
9681
- });
9682
- }
9683
- function noteLkgProviderResponse(args) {
9684
- if (!args.responseId || !args.modelKey)
9685
- return;
9686
- if (!args.v2 && args.inputTokens === 0 && !args.completedAt && !args.finish && !args.error) {
9687
- const previous = requests.get(args.sessionId);
9688
- if (previous?.kind === "v1" && previous.responseId === args.responseId && previous.modelKey === args.modelKey)
9689
- return;
9690
- requests.set(args.sessionId, {
9691
- kind: "v1",
9692
- responseId: args.responseId,
9693
- modelKey: args.modelKey
9694
- });
9695
- return;
9696
- }
9697
- const capture = captures.get(args.sessionId);
9698
- if (!capture || capture.usage || capture.request.modelKey !== args.modelKey || !Number.isSafeInteger(args.inputTokens) || args.inputTokens <= 0 || providerResponseFailed(args) || !args.completedAt && !args.finish)
9699
- return;
9700
- const request = capture.request;
9701
- if (request.kind === "v1") {
9702
- if (args.v2 || request.responseId !== args.responseId)
9703
- return;
9704
- } else if (!args.v2 || requests.get(args.sessionId) !== request || request.previousResponseId === args.responseId || args.createdAt === undefined || args.createdAt < request.startedAt)
9705
- return;
9706
- capture.usage = { responseId: args.responseId, inputTokens: args.inputTokens };
9707
- }
9708
-
9709
- // src/hooks/magic-context/lkg-persist.ts
9710
- import { createHash as createHash4 } from "node:crypto";
9711
- function parseStringArray(value) {
9712
- if (typeof value !== "string")
9713
- return null;
9714
- let parsed;
9715
- try {
9716
- parsed = JSON.parse(value);
9717
- } catch {
9718
- return null;
9719
- }
9720
- if (!Array.isArray(parsed))
9721
- return null;
9722
- const result = [];
9723
- for (const entry of parsed) {
9724
- if (typeof entry !== "string" || entry.length === 0)
9725
- return null;
9726
- result.push(entry);
9727
- }
9728
- return result;
9729
- }
9730
- function parseNullableString(value) {
9731
- if (value === null || value === undefined)
9732
- return null;
9733
- return typeof value === "string" ? value : undefined;
9734
- }
9735
- function parseNullableInteger(value) {
9736
- if (value === null || value === undefined)
9737
- return;
9738
- return typeof value === "number" && Number.isSafeInteger(value) ? value : undefined;
9739
- }
9740
- function parsePersistedLkgSlot(row) {
9741
- if (!row || typeof row !== "object")
9742
- return;
9743
- const record = row;
9744
- const jsonPrefix = record.json_prefix;
9745
- const lastInputMessageId = record.last_input_message_id;
9746
- const capturedAt = record.captured_at;
9747
- let inputIdsRaw = record.input_id_seq;
9748
- let piOutputEntryIds;
9749
- if (typeof inputIdsRaw === "string") {
9750
- try {
9751
- const metadata = JSON.parse(inputIdsRaw);
9752
- if (metadata && !Array.isArray(metadata) && metadata.version === 1 && Array.isArray(metadata.piOutputEntryIds)) {
9753
- if (!metadata.piOutputEntryIds.every((id) => id === null || typeof id === "string" && id.length > 0))
9754
- return;
9755
- piOutputEntryIds = metadata.piOutputEntryIds;
9756
- inputIdsRaw = JSON.stringify(metadata.inputIds);
9757
- }
9758
- } catch {
9759
- return;
9760
- }
9761
- }
9762
- const inputIdSeq = parseStringArray(inputIdsRaw);
9763
- const inputContentDigests = parseStringArray(record.input_content_digests);
9764
- const modelKey = parseNullableString(record.model_key);
9765
- const providerKey = parseNullableString(record.provider_key);
9766
- if (typeof jsonPrefix !== "string" || typeof lastInputMessageId !== "string" || lastInputMessageId.length === 0 || typeof capturedAt !== "number" || !Number.isFinite(capturedAt) || inputIdSeq === null || inputContentDigests === null || inputContentDigests.length !== inputIdSeq.length || modelKey === undefined || providerKey === undefined) {
9767
- return;
9768
- }
9769
- if (piOutputEntryIds) {
9770
- try {
9771
- const output = JSON.parse(jsonPrefix);
9772
- const inputs = new Set(inputIdSeq);
9773
- if (!Array.isArray(output) || output.length !== piOutputEntryIds.length || piOutputEntryIds.some((id) => id !== null && !inputs.has(id)))
9774
- return;
9775
- } catch {
9776
- return;
9777
- }
9778
- }
9779
- let inputContentSignatures;
9780
- if (record.input_content_signatures !== null && record.input_content_signatures !== undefined) {
9781
- const parsed = parseStringArray(record.input_content_signatures);
9782
- if (parsed === null || parsed.length !== inputIdSeq.length)
9783
- return;
9784
- inputContentSignatures = parsed;
9785
- }
9786
- const slot = {
9787
- jsonPrefix,
9788
- inputIdSeq,
9789
- inputContentDigests,
9790
- lastInputMessageId,
9791
- modelKey,
9792
- providerKey,
9793
- capturedAt
9794
- };
9795
- if (inputContentSignatures)
9796
- slot.inputContentSignatures = inputContentSignatures;
9797
- if (piOutputEntryIds)
9798
- slot.piOutputEntryIds = piOutputEntryIds;
9799
- const rowVersion = parseNullableInteger(record.row_version);
9800
- if (rowVersion !== undefined)
9801
- slot.rowVersion = rowVersion;
9802
- const captureSequence = parseNullableInteger(record.capture_sequence);
9803
- if (captureSequence !== undefined)
9804
- slot.captureSequence = captureSequence;
9805
- return slot;
9806
- }
9807
- var persistedFingerprints = new WeakMap;
9808
- var PERSISTED_FINGERPRINT_MAX_SESSIONS = 1000;
9809
- function rememberFingerprint(db, sessionId, fingerprint) {
9810
- let saved = persistedFingerprints.get(db);
9811
- if (!saved) {
9812
- saved = new Map;
9813
- persistedFingerprints.set(db, saved);
9814
- }
9815
- saved.delete(sessionId);
9816
- if (saved.size >= PERSISTED_FINGERPRINT_MAX_SESSIONS) {
9817
- const oldest = saved.keys().next().value;
9818
- if (oldest !== undefined)
9819
- saved.delete(oldest);
9820
- }
9821
- saved.set(sessionId, fingerprint);
9822
- }
9823
- function slotFingerprint(slot) {
9824
- return createHash4("sha256").update(JSON.stringify([
9825
- slot.jsonPrefix,
9826
- slot.inputIdSeq,
9827
- slot.inputContentDigests,
9828
- slot.inputContentSignatures,
9829
- slot.piOutputEntryIds,
9830
- slot.lastInputMessageId,
9831
- slot.modelKey,
9832
- slot.providerKey,
9833
- slot.rowVersion,
9834
- slot.captureSequence
9835
- ])).digest("hex");
9836
- }
9837
- function saveLkgSlotToDb(db, sessionId, slot) {
9838
- const fingerprint = slotFingerprint(slot);
9839
- if (persistedFingerprints.get(db)?.get(sessionId) === fingerprint)
9840
- return true;
9841
- const layout = layoutLkgPrefix(slot.jsonPrefix);
9842
- try {
9843
- db.transaction(() => {
9844
- const storedHashes = new Map;
9845
- for (const row of db.prepare("SELECT chunk, hash FROM lkg_slot_chunks WHERE session_id = ?").all(sessionId)) {
9846
- storedHashes.set(row.chunk, row.hash);
9847
- }
9848
- const upsertChunk = db.prepare(`INSERT INTO lkg_slot_chunks (session_id, chunk, hash, body) VALUES (?, ?, ?, ?)
9849
- ON CONFLICT(session_id, chunk) DO UPDATE SET hash = excluded.hash, body = excluded.body`);
9850
- for (let index = 0;index < layout.chunks.length; index += 1) {
9851
- if (storedHashes.get(index) === layout.chunkHashes[index])
9852
- continue;
9853
- upsertChunk.run(sessionId, index, layout.chunkHashes[index], layout.chunks[index]);
9854
- }
9855
- db.prepare("DELETE FROM lkg_slot_chunks WHERE session_id = ? AND chunk >= ?").run(sessionId, layout.chunks.length);
9856
- db.prepare(`INSERT INTO lkg_slots (
9857
- session_id, json_prefix_chars, json_prefix_chunks, json_prefix_hash,
9858
- input_id_seq, input_content_digests, input_content_signatures,
9859
- last_input_message_id, model_key, provider_key,
9860
- captured_at, row_version, capture_sequence
9861
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9862
- ON CONFLICT(session_id) DO UPDATE SET
9863
- json_prefix_chars = excluded.json_prefix_chars,
9864
- json_prefix_chunks = excluded.json_prefix_chunks,
9865
- json_prefix_hash = excluded.json_prefix_hash,
9866
- input_id_seq = excluded.input_id_seq,
9867
- input_content_digests = excluded.input_content_digests,
9868
- input_content_signatures = excluded.input_content_signatures,
9869
- last_input_message_id = excluded.last_input_message_id,
9870
- model_key = excluded.model_key,
9871
- provider_key = excluded.provider_key,
9872
- captured_at = excluded.captured_at,
9873
- row_version = excluded.row_version,
9874
- capture_sequence = excluded.capture_sequence`).run(sessionId, layout.chars, layout.chunks.length, layout.hash, JSON.stringify(slot.piOutputEntryIds ? {
9875
- version: 1,
9876
- inputIds: slot.inputIdSeq,
9877
- piOutputEntryIds: slot.piOutputEntryIds
9878
- } : slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
9879
- }).immediate();
9880
- rememberFingerprint(db, sessionId, fingerprint);
9881
- return true;
9882
- } catch (error) {
9883
- sessionLog(sessionId, "LKG snapshot persistence failed (in-memory slot retained):", error);
9884
- return false;
9885
- }
9886
- }
9887
- function clearPersistedLkgSlot(db, sessionId) {
9888
- try {
9889
- db.transaction(() => {
9890
- db.prepare("DELETE FROM lkg_slot_chunks WHERE session_id = ?").run(sessionId);
9891
- db.prepare("DELETE FROM lkg_slots WHERE session_id = ?").run(sessionId);
9892
- }).immediate();
9893
- persistedFingerprints.get(db)?.delete(sessionId);
9894
- } catch (error) {
9895
- sessionLog(sessionId, "LKG snapshot durable clear failed:", error);
9896
- }
9897
- }
9898
- function loadPersistedLkgSlot(db, sessionId) {
9899
- let row;
9900
- let chunkRows;
9901
- try {
9902
- ({ row, chunkRows } = db.transaction(() => {
9903
- const slotRow = db.prepare("SELECT * FROM lkg_slots WHERE session_id = ?").get(sessionId);
9904
- if (!slotRow)
9905
- return { row: undefined, chunkRows: [] };
9906
- const slices = db.prepare("SELECT chunk, hash, body FROM lkg_slot_chunks WHERE session_id = ? ORDER BY chunk").all(sessionId);
9907
- return { row: slotRow, chunkRows: slices };
9908
- }).deferred());
9909
- } catch (error) {
9910
- sessionLog(sessionId, "LKG snapshot durable load failed:", error);
9911
- return;
9912
- }
9913
- if (!row)
9914
- return;
9915
- const prefix = assembleLkgPrefix({
9916
- chars: row.json_prefix_chars,
9917
- chunks: row.json_prefix_chunks,
9918
- hash: row.json_prefix_hash
9919
- }, chunkRows);
9920
- const slot = prefix ? parsePersistedLkgSlot({ ...row, json_prefix: prefix.jsonPrefix }) : undefined;
9921
- if (!prefix || !slot) {
9922
- if (!prefix)
9923
- sessionLog(sessionId, "LKG snapshot slices failed verification; slot cleared");
9924
- clearPersistedLkgSlot(db, sessionId);
9925
- return;
9926
- }
9927
- return slot;
9928
- }
9929
- function pruneStaleLkgSlotsBatch(db, now) {
9930
- const previousTimeout = db.prepare("PRAGMA busy_timeout").get();
9931
- const cutoff = now - 7 * 24 * 60 * 60 * 1000;
9932
- const stale = `captured_at < ? AND NOT EXISTS (
9933
- SELECT 1 FROM session_projects sp
9934
- WHERE sp.session_id = lkg_slots.session_id AND sp.updated_at >= ?
9935
- )`;
9936
- const slot = db.prepare(`SELECT session_id FROM lkg_slots WHERE ${stale} LIMIT 1`).get(cutoff, cutoff);
9937
- const chunks = db.prepare(`SELECT rowid FROM lkg_slot_chunks c WHERE
9938
- session_id = ? OR NOT EXISTS (SELECT 1 FROM lkg_slots s WHERE s.session_id = c.session_id)
9939
- LIMIT 25`).all(slot?.session_id ?? "");
9940
- if (!slot && chunks.length === 0)
9941
- return { slots: 0, chunks: 0 };
9942
- try {
9943
- db.exec("PRAGMA busy_timeout = 0");
9944
- let transactionStartedAt = 0;
9945
- const changes = db.transaction(() => {
9946
- transactionStartedAt = performance.now();
9947
- const removed = slot ? db.prepare(`DELETE FROM lkg_slots WHERE session_id = ? AND ${stale}`).run(slot.session_id, cutoff, cutoff).changes : 0;
9948
- const removeChunk = db.prepare(`DELETE FROM lkg_slot_chunks WHERE rowid = ? AND NOT EXISTS (
9949
- SELECT 1 FROM lkg_slots s WHERE s.session_id = lkg_slot_chunks.session_id
9950
- )`);
9951
- let removedChunks = 0;
9952
- for (const { rowid } of chunks)
9953
- removedChunks += removeChunk.run(rowid).changes;
9954
- return { slots: removed, chunks: removedChunks };
9955
- }).immediate();
9956
- logSlowWriteTransaction("lkg_stale_gc", transactionStartedAt);
9957
- if (changes.slots && slot)
9958
- persistedFingerprints.get(db)?.delete(slot.session_id);
9959
- return changes;
9960
- } finally {
9961
- db.exec(`PRAGMA busy_timeout = ${Number(previousTimeout.timeout) || 0}`);
9962
- }
9963
- }
9964
- function pruneStaleLkgSlots(db, now = Date.now()) {
9965
- return pruneStaleLkgSlotsBatch(db, now).slots;
9966
- }
9967
-
9968
- // src/hooks/magic-context/openai-compat-adjacency.ts
9969
- function assertOpenAiCompatAdjacency(messages) {
9970
- const violations = [];
9971
- for (let i = 0;i < messages.length; i++) {
9972
- const msg = messages[i];
9973
- if (msg.role !== "assistant" || !msg.tool_calls || msg.tool_calls.length === 0) {
9974
- continue;
9975
- }
9976
- const expectedIds = msg.tool_calls.map((tc) => tc.id);
9977
- const expectedSet = new Set(expectedIds);
9978
- const collected = new Map;
9979
- let j = i + 1;
9980
- while (j < messages.length && messages[j].role === "tool") {
9981
- const toolMsg = messages[j];
9982
- const id = toolMsg.tool_call_id;
9983
- if (!id) {
9984
- violations.push({
9985
- index: i,
9986
- kind: "unmatched_tool_call_id",
9987
- detail: `tool message at index ${j} missing tool_call_id`
9988
- });
9989
- } else if (!expectedSet.has(id)) {
9990
- violations.push({
9991
- index: i,
9992
- kind: "unmatched_tool_call_id",
9993
- toolCallId: id,
9994
- assistantToolCallIds: expectedIds,
9995
- detail: `tool message at index ${j} references unexpected id ${id}`
9996
- });
9997
- } else {
9998
- collected.set(id, j);
9999
- }
10000
- j++;
10001
- }
10002
- const followingRoles = messages.slice(i + 1, Math.min(messages.length, i + 4)).map((m) => m.role);
10003
- const missing = expectedIds.filter((id) => !collected.has(id));
10004
- if (missing.length > 0) {
10005
- violations.push({
10006
- index: i,
10007
- kind: "missing_tool_messages",
10008
- assistantToolCallIds: expectedIds,
10009
- followingRoles,
10010
- detail: missing.length === expectedIds.length ? `assistant at index ${i} has tool_calls but next messages are not contiguous tool role (following: ${followingRoles.join(", ") || "none"})` : `assistant at index ${i} missing tool results for: ${missing.join(", ")}`
10011
- });
10012
- if (j < messages.length && messages[j].role !== "tool") {
10013
- const last = violations[violations.length - 1];
10014
- last.detail += `; blocked by ${messages.slice(i + 1, j + 1).map((m, off) => `${m.role}[${i + 1 + off}]`).join(", ")}`;
10015
- }
10016
- }
10017
- }
10018
- for (let i = 0;i < messages.length; i++) {
10019
- if (messages[i].role !== "tool")
10020
- continue;
10021
- const id = messages[i].tool_call_id ?? "";
10022
- if (!id)
10023
- continue;
10024
- let found = false;
10025
- for (let k = i - 1;k >= 0; k--) {
10026
- const prev = messages[k];
10027
- if (prev.role === "assistant" && prev.tool_calls?.some((tc) => tc.id === id)) {
10028
- const between = messages.slice(k + 1, i);
10029
- if (between.every((m) => m.role === "tool")) {
10030
- found = true;
10031
- }
10032
- break;
10033
- }
10034
- if (prev.role === "assistant" || prev.role === "user")
10035
- break;
10036
- }
10037
- if (!found) {
10038
- violations.push({
10039
- index: i,
10040
- kind: "orphan_tool_message",
10041
- toolCallId: id,
10042
- detail: `tool message at index ${i} is not immediately after its assistant tool_calls`
10043
- });
10044
- }
10045
- }
10046
- return { ok: violations.length === 0, violations };
10047
- }
10048
-
10049
- // src/hooks/magic-context/lkg-replay.ts
10050
- function canonicalLkgModelKeys(modelKey, providerKey) {
10051
- const canonicalModelKey = modelKey ? piModelRefToCanonical(modelKey) : null;
10052
- const slash = canonicalModelKey?.indexOf("/") ?? -1;
10053
- return {
10054
- modelKey: canonicalModelKey,
10055
- providerKey: slash > 0 && canonicalModelKey ? canonicalModelKey.slice(0, slash) : providerKey
10056
- };
10057
- }
10058
- function resolveLkgModelKeys(messages) {
10059
- for (let index = messages.length - 1;index >= 0; index -= 1) {
10060
- const info = messages[index]?.info;
10061
- const nested = info?.model;
10062
- if (nested && typeof nested === "object") {
10063
- const provider = nested.providerID;
10064
- const model = nested.modelID;
10065
- if (typeof provider === "string" && typeof model === "string") {
10066
- return canonicalLkgModelKeys(`${provider}/${model}`, provider);
10067
- }
10068
- }
10069
- const provider = info?.providerID;
10070
- const model = info?.modelID;
10071
- if (typeof provider === "string" && typeof model === "string") {
10072
- return canonicalLkgModelKeys(`${provider}/${model}`, provider);
10073
- }
10074
- }
10075
- return { modelKey: null, providerKey: null };
10076
- }
10077
- function projectLkgEntry(messages) {
10078
- return projectEntryWithDigests(messages, messages.map(lkgContentDigest));
10079
- }
10080
- function createLkgEntryProjector(options = {}) {
10081
- const priors = new Map;
10082
- const maxBytes = options.maxBytes ?? 64 * 1024 * 1024;
10083
- let bytes = 0;
10084
- return (sessionId, messages) => {
10085
- const prior = priors.get(sessionId);
10086
- const snapshots = messages.map((message) => ({
10087
- id: typeof message.info?.id === "string" ? message.info.id : "",
10088
- fields: lkgContentFields(message)
10089
- }));
10090
- let reused = 0;
10091
- const digests = snapshots.map((snapshot) => {
10092
- const cached = prior?.entries.get(snapshot.id);
10093
- if (snapshot.fields && cached && exactReusablePrefix([snapshot], [cached.snapshot]) === 1) {
10094
- reused += 1;
10095
- return cached.digest;
10096
- }
10097
- if (!snapshot.fields)
10098
- return null;
10099
- return options.maxBytes === undefined ? memoizedLkgContentDigestFromFields(snapshot.id, snapshot.fields) : lkgContentDigestFromFields(snapshot.fields);
10100
- });
10101
- if (prior) {
10102
- bytes -= prior.bytes;
10103
- priors.delete(sessionId);
10104
- }
10105
- let size = 0;
10106
- let retainedCount = 0;
10107
- const retained = new Map;
10108
- snapshots.forEach((snapshot, index) => {
10109
- const entrySize = snapshot.id.length * 2 + (snapshot.fields?.reduce((sum, field) => sum + 16 + (typeof field === "string" ? field.length * 2 : 0), 0) ?? 0) + 80 + 86;
10110
- if (!snapshot.fields || retained.has(snapshot.id) || size + entrySize > maxBytes)
10111
- return;
10112
- size += entrySize;
10113
- retainedCount += 1;
10114
- retained.set(snapshot.id, {
10115
- snapshot,
10116
- digest: digests[index] ?? null
10117
- });
10118
- });
10119
- if (size <= maxBytes && retainedCount > 0) {
10120
- while (priors.size >= 16 || bytes + size > maxBytes) {
10121
- const oldest = priors.entries().next().value;
10122
- if (!oldest)
10123
- break;
10124
- bytes -= oldest[1].bytes;
10125
- priors.delete(oldest[0]);
10126
- }
10127
- priors.set(sessionId, {
10128
- entries: retained,
10129
- bytes: size
10130
- });
10131
- bytes += size;
10132
- }
10133
- options.onReuse?.({ reused, retained: retainedCount, retainedBytes: size });
10134
- return projectEntryWithDigests(messages, digests);
10135
- };
10136
- }
10137
- function projectEntryWithDigests(messages, digests) {
10138
- return messages.map((message, index) => {
10139
- const info = messageInfo(message);
10140
- const time = info.time;
10141
- const timeRecord = time && typeof time === "object" ? time : null;
10142
- const timeCandidates = [
10143
- timeRecord?.created,
10144
- info.timeCreated,
10145
- info.time_created,
10146
- info.createdAt,
10147
- info.created_at
10148
- ];
10149
- let timeCreated = null;
10150
- for (const value of timeCandidates) {
10151
- if (typeof value === "number" && Number.isFinite(value)) {
10152
- timeCreated = value;
10153
- break;
10154
- }
10155
- }
10156
- let hasIncompleteTool = false;
10157
- for (const rawPart of messageParts(message)) {
10158
- if (!rawPart || typeof rawPart !== "object")
10159
- continue;
10160
- const part = rawPart;
10161
- if (part.type !== "tool" || part.providerExecuted === true)
10162
- continue;
10163
- const state = part.state;
10164
- const status = state && typeof state === "object" ? state.status : undefined;
10165
- if (status !== "completed") {
10166
- hasIncompleteTool = true;
10167
- break;
10168
- }
10169
- }
10170
- const id = info.id;
10171
- const projection = {
10172
- id: typeof id === "string" && id.length > 0 ? id : null,
10173
- role: typeof info.role === "string" ? info.role : undefined,
10174
- synthetic: info.synthetic === true,
10175
- timeCreated,
10176
- finish: info.finish,
10177
- hasIncompleteTool
10178
- };
10179
- const contentDigest = digests[index] ?? null;
10180
- Object.defineProperty(projection, "contentDigest", {
10181
- value: () => contentDigest,
10182
- enumerable: false
10183
- });
10184
- return projection;
10185
- });
10186
- }
10187
- function recordValue(info, key) {
10188
- return info && typeof info === "object" ? info[key] : undefined;
10189
- }
10190
- function messageInfo(message) {
10191
- if (message.info && typeof message.info === "object")
10192
- return message.info;
10193
- return message;
10194
- }
10195
- function messageParts(message) {
10196
- return Array.isArray(message.parts) ? message.parts : [];
10197
- }
10198
- function isSynthetic(message) {
10199
- return recordValue(messageInfo(message), "synthetic") === true;
10200
- }
10201
- function hasSyntheticParts(message) {
10202
- const parts = messageParts(message);
10203
- return parts.length > 0 && parts.every((part) => {
10204
- return Boolean(part && typeof part === "object" && part.synthetic === true);
10205
- });
10206
- }
10207
- function isSyntheticOutput(message) {
10208
- return isSynthetic(message) || hasSyntheticParts(message);
10209
- }
10210
- function messageRole(message) {
10211
- const infoRole = recordValue(messageInfo(message), "role");
10212
- if (typeof infoRole === "string")
10213
- return infoRole;
10214
- const role = recordValue(message, "role");
10215
- return typeof role === "string" ? role : undefined;
10216
- }
10217
- function messageId(message) {
10218
- const id = recordValue(messageInfo(message), "id") ?? recordValue(message, "id");
10219
- return typeof id === "string" && id.length > 0 ? id : null;
10220
- }
10221
- function latestAssistant(messages) {
10222
- for (let index = messages.length - 1;index >= 0; index -= 1) {
10223
- if (messages[index].role === "assistant")
10224
- return messages[index];
10225
- }
10226
- return null;
10227
- }
10228
- function isRealUser(message) {
10229
- return message.role === "user" && !message.synthetic && message.id !== null;
10230
- }
10231
- function assistantIsActive(message) {
10232
- return message.finish === "tool-calls" || message.hasIncompleteTool;
10233
- }
10234
- function findLkgAnchor(messages) {
10235
- const assistant = latestAssistant(messages);
10236
- const assistantTime = assistant?.timeCreated ?? null;
10237
- if (assistant && assistantTime === null)
10238
- return null;
10239
- let anchor = -1;
10240
- for (let index = messages.length - 1;index >= 0; index -= 1) {
10241
- const message = messages[index];
10242
- if (!isRealUser(message))
10243
- continue;
10244
- if (assistant && assistantIsActive(assistant)) {
10245
- if (assistantTime === null || message.timeCreated === null || message.timeCreated <= assistantTime) {
10246
- continue;
10247
- }
10248
- }
10249
- anchor = index;
10250
- break;
10251
- }
10252
- return anchor >= 0 ? anchor : null;
10253
- }
10254
- function asEntryProjection(input) {
10255
- if (input.length === 0 || "hasIncompleteTool" in input[0]) {
10256
- return input;
10257
- }
10258
- return projectLkgEntry(input);
10259
- }
10260
- function outputMessageIsPostAnchor(message, inputIndexById, anchorIndex) {
10261
- const id = messageId(message);
10262
- if (id !== null) {
10263
- const inputIndex = inputIndexById.get(id);
10264
- if (inputIndex !== undefined)
10265
- return inputIndex > anchorIndex;
10266
- if (!isSyntheticOutput(message))
10267
- return null;
10268
- const linked = ["sourceMessageId", "ownerMessageId", "anchorMessageId", "messageId"].map((key) => recordValue(messageInfo(message), key)).find((value) => typeof value === "string");
10269
- if (typeof linked === "string") {
10270
- const linkedIndex = inputIndexById.get(linked);
10271
- if (linkedIndex === undefined)
10272
- return null;
10273
- return linkedIndex > anchorIndex;
10274
- }
10275
- return false;
10276
- }
10277
- if (!isSyntheticOutput(message))
10278
- return null;
10279
- const linked = ["sourceMessageId", "ownerMessageId", "anchorMessageId"].map((key) => recordValue(message.info, key)).find((value) => typeof value === "string");
10280
- if (typeof linked !== "string")
10281
- return false;
10282
- const linkedIndex = inputIndexById.get(linked);
10283
- return linkedIndex === undefined ? null : linkedIndex > anchorIndex;
10284
- }
10285
- function buildLkgPrefix(input, output) {
10286
- const projected = asEntryProjection(input);
10287
- const anchorIndex = findLkgAnchor(projected);
10288
- if (anchorIndex === null)
10289
- return null;
10290
- const ids = projected.map((message) => message.id);
10291
- if (ids.some((id) => id === null))
10292
- return null;
10293
- const validIds = ids;
10294
- if (new Set(validIds).size !== validIds.length)
10295
- return null;
10296
- const anchorMessageId = validIds[anchorIndex];
10297
- const inputContentDigests = projected.slice(0, anchorIndex + 1).map((message) => message.contentDigest?.() ?? null);
10298
- if (inputContentDigests.some((digest) => digest === null))
10299
- return null;
10300
- const inputIndexById = new Map(validIds.map((id, index) => [id, index]));
10301
- const prefix = [];
10302
- for (const message of output) {
10303
- const postAnchor = outputMessageIsPostAnchor(message, inputIndexById, anchorIndex);
10304
- if (postAnchor === null)
10305
- return null;
10306
- if (!postAnchor)
10307
- prefix.push(message);
10308
- }
10309
- let jsonPrefix;
10310
- try {
10311
- jsonPrefix = JSON.stringify(prefix);
10312
- if (typeof jsonPrefix !== "string")
10313
- return null;
10314
- } catch {
10315
- return null;
10316
- }
10317
- return {
10318
- anchorIndex,
10319
- anchorMessageId,
10320
- inputIdSeq: validIds.slice(0, anchorIndex + 1),
10321
- inputContentDigests,
10322
- jsonPrefix
10323
- };
10324
- }
10325
- function captureLkgSlot(args) {
10326
- const built = buildLkgPrefix(args.input, args.output);
10327
- if (!built)
10328
- return false;
10329
- const modelKeys = canonicalLkgModelKeys(args.modelKey, args.providerKey);
10330
- const slot = {
10331
- jsonPrefix: built.jsonPrefix,
10332
- inputIdSeq: built.inputIdSeq,
10333
- inputContentDigests: built.inputContentDigests,
10334
- lastInputMessageId: built.anchorMessageId,
10335
- modelKey: modelKeys.modelKey,
10336
- providerKey: modelKeys.providerKey,
10337
- capturedAt: args.capturedAt ?? Date.now()
10338
- };
10339
- const captured = captureSlot(args.sessionId, slot);
10340
- if (captured)
10341
- noteCapturedLkgRequest({
10342
- sessionId: args.sessionId,
10343
- slot,
10344
- request: built.anchorIndex === args.input.length - 1 ? claimLkgRequestIdentity(args.sessionId) : undefined,
10345
- systemPromptTokens: args.systemPromptTokens ?? 0,
10346
- agentName: args.agentName
10347
- });
10348
- return captured;
10349
- }
10350
- function entryIdsAreValid(slot, entryIds) {
10351
- if (slot.inputIdSeq.length === 0 || entryIds.length < slot.inputIdSeq.length)
10352
- return false;
10353
- if (slot.inputIdSeq[slot.inputIdSeq.length - 1] !== slot.lastInputMessageId)
10354
- return false;
10355
- const seen = new Set;
10356
- for (const id of entryIds) {
10357
- if (!id || seen.has(id))
10358
- return false;
10359
- seen.add(id);
10360
- }
10361
- if (entryIds.indexOf(slot.lastInputMessageId) !== slot.inputIdSeq.length - 1)
10362
- return false;
10363
- for (let index = 0;index < slot.inputIdSeq.length; index += 1) {
10364
- if (entryIds[index] !== slot.inputIdSeq[index])
10365
- return false;
10366
- }
10367
- return true;
10368
- }
10369
- function entryContentIsValid(slot, entryDigests) {
10370
- return entryDigests.length >= slot.inputContentDigests.length && slot.inputContentDigests.every((digest, index) => entryDigests[index] === digest);
10371
- }
10372
- function partCallIds(message) {
10373
- const ids = [];
10374
- for (const rawPart of messageParts(message)) {
10375
- if (!rawPart || typeof rawPart !== "object")
10376
- continue;
10377
- const part = rawPart;
10378
- if (part.type !== "tool" && part.type !== "tool_use")
10379
- continue;
10380
- const callId = part.callID ?? part.callId ?? part.id;
10381
- if (typeof callId === "string" && callId.length > 0)
10382
- ids.push(callId);
10383
- }
10384
- return ids;
10385
- }
10386
- function partResultIds(message) {
10387
- const ids = [];
10388
- for (const rawPart of messageParts(message)) {
10389
- if (!rawPart || typeof rawPart !== "object")
10390
- continue;
10391
- const part = rawPart;
10392
- if (part.type !== "tool_result" && part.type !== "tool-result")
10393
- continue;
10394
- const callId = part.tool_call_id ?? part.tool_use_id ?? part.callID ?? part.callId;
10395
- if (typeof callId === "string" && callId.length > 0)
10396
- ids.push(callId);
10397
- }
10398
- return ids;
10399
- }
10400
- function partIsReasoning(part) {
10401
- return Boolean(part && typeof part === "object" && part.type === "reasoning");
10402
- }
10403
- function partIsAnthropicThinking(part) {
10404
- if (!part || typeof part !== "object")
10405
- return false;
10406
- const type = part.type;
10407
- return type === "thinking" || type === "reasoning" || type === "redacted_thinking";
10408
- }
10409
- function partEndsAnthropicAssistantRun(part) {
10410
- if (!part || typeof part !== "object")
10411
- return false;
10412
- const value = part;
10413
- if (value.type !== "tool" || value.providerExecuted === true || !value.state || typeof value.state !== "object") {
10414
- return false;
10415
- }
10416
- const status = value.state.status;
10417
- return status === "completed" || status === "error";
10418
- }
10419
- function partIsOpenCodeStepMetadata(part) {
10420
- if (!part || typeof part !== "object")
10421
- return false;
10422
- const type = part.type;
10423
- return type === "step-start" || type === "step-finish";
10424
- }
10425
- function validateAnthropicReasoningRuns(messages) {
10426
- let index = 0;
10427
- while (index < messages.length) {
10428
- if (messageRole(messages[index]) !== "assistant") {
10429
- index += 1;
10430
- continue;
10431
- }
10432
- let firstMessageInRun = null;
10433
- let sawOtherContent = false;
10434
- while (index < messages.length && messageRole(messages[index]) === "assistant") {
10435
- for (const part of messageParts(messages[index])) {
10436
- if (partEndsAnthropicAssistantRun(part)) {
10437
- firstMessageInRun = null;
10438
- sawOtherContent = false;
10439
- } else if (!partIsOpenCodeStepMetadata(part)) {
10440
- if (firstMessageInRun === null)
10441
- firstMessageInRun = index;
10442
- if (partIsAnthropicThinking(part)) {
10443
- if (sawOtherContent || index !== firstMessageInRun)
10444
- return false;
10445
- } else {
10446
- sawOtherContent = true;
10447
- }
10448
- }
10449
- }
10450
- index += 1;
10451
- }
10452
- }
10453
- return true;
10454
- }
10455
- function validateLkgSeamBoundary(prefix, tail) {
10456
- const last = prefix[prefix.length - 1];
10457
- const first = tail[0];
10458
- if (!last || !first)
10459
- return true;
10460
- const lastCalls = partCallIds(last);
10461
- if (lastCalls.length === 0)
10462
- return true;
10463
- const firstCalls = new Set([...partCallIds(first), ...partResultIds(first)]);
10464
- if (messageRole(first) === "tool" || lastCalls.some((callId) => firstCalls.has(callId)))
10465
- return false;
10466
- return !messageParts(last).some((part) => {
10467
- if (!part || typeof part !== "object")
10468
- return false;
10469
- const value = part;
10470
- if (value.type !== "tool")
10471
- return false;
10472
- const state = value.state;
10473
- return !state || typeof state !== "object" || state.status !== "completed";
10474
- });
10475
- }
10476
- function validateLkgSeam(prefix, tail, providerKey) {
10477
- const all = [...prefix, ...tail];
10478
- const ids = new Set;
10479
- const calls = new Set;
10480
- const results = new Set;
10481
- for (const message of all) {
10482
- const id = messageId(message);
10483
- if (id !== null) {
10484
- if (ids.has(id))
10485
- return false;
10486
- ids.add(id);
10487
- }
10488
- for (const callId of partCallIds(message)) {
10489
- if (calls.has(callId))
10490
- return false;
10491
- calls.add(callId);
10492
- }
10493
- for (const callId of partResultIds(message)) {
10494
- if (results.has(callId))
10495
- return false;
10496
- results.add(callId);
10497
- }
10498
- if (messageRole(message) !== "assistant" && messageParts(message).some(partIsReasoning))
10499
- return false;
10500
- if (providerKey !== "anthropic" && messageParts(message).some((part) => {
10501
- if (!part || typeof part !== "object")
10502
- return false;
10503
- const value = part;
10504
- return (value.type === "text" || value.type === "reasoning") && value.text === "";
10505
- }))
10506
- return false;
10507
- }
10508
- if (!validateLkgSeamBoundary(prefix, tail))
10509
- return false;
10510
- const wireCandidates = all.map((message) => message);
10511
- if (wireCandidates.every((message) => typeof message.role === "string")) {
10512
- const adjacency = assertOpenAiCompatAdjacency(wireCandidates);
10513
- if (!adjacency.ok)
10514
- return false;
10515
- const wireCallIds = new Set;
10516
- for (const wireMessage of wireCandidates) {
10517
- for (const call of wireMessage.tool_calls ?? []) {
10518
- if (wireCallIds.has(call.id))
10519
- return false;
10520
- wireCallIds.add(call.id);
10521
- }
10522
- }
10523
- }
10524
- return true;
10525
- }
10526
- function replayLkg(args) {
10527
- const slot = getSlot(args.sessionId);
10528
- if (!slot)
10529
- return { ok: false, reason: "lkg_invalidated_reshape" };
10530
- const requestedModelKeys = canonicalLkgModelKeys(args.modelKey, args.providerKey);
10531
- const capturedModelKeys = canonicalLkgModelKeys(slot.modelKey, slot.providerKey);
10532
- if (capturedModelKeys.modelKey !== requestedModelKeys.modelKey || capturedModelKeys.providerKey !== requestedModelKeys.providerKey) {
10533
- dropSlot(args.sessionId, "lkg_model_mismatch");
10534
- return { ok: false, reason: "lkg_model_mismatch" };
10535
- }
10536
- const entry = args.entry ?? noteEntry(args.sessionId, args.messages);
10537
- if (!entry || entry.anchorIndex !== slot.inputIdSeq.length - 1 || !entryIdsAreValid(slot, entry.entryInputIds)) {
10538
- dropSlot(args.sessionId, "lkg_invalidated_reshape");
10539
- return { ok: false, reason: "lkg_invalidated_reshape" };
10540
- }
10541
- if (!entryContentIsValid(slot, entry.entryContentDigests)) {
10542
- dropSlot(args.sessionId, "lkg_content_mismatch");
10543
- return { ok: false, reason: "lkg_content_mismatch" };
10544
- }
10545
- let prefix;
10546
- try {
10547
- const parsed = JSON.parse(slot.jsonPrefix);
10548
- if (!Array.isArray(parsed))
10549
- throw new Error("prefix is not an array");
10550
- prefix = parsed;
10551
- } catch {
10552
- dropSlot(args.sessionId, "lkg_seam_invalid");
10553
- return { ok: false, reason: "lkg_seam_invalid" };
10554
- }
10555
- const replayed = [...prefix, ...entry.pristineTail];
10556
- args.prepareReplay?.(replayed);
10557
- if (!args.skipSeamValidation) {
10558
- if (!validateLkgSeamBoundary(prefix, entry.pristineTail)) {
10559
- dropSlot(args.sessionId, "lkg_unsafe_seam");
10560
- return { ok: false, reason: "lkg_unsafe_seam" };
10561
- }
10562
- if (!validateLkgSeam(prefix, entry.pristineTail, requestedModelKeys.providerKey)) {
10563
- dropSlot(args.sessionId, "lkg_seam_invalid");
10564
- return { ok: false, reason: "lkg_seam_invalid" };
10565
- }
10566
- }
10567
- if (requestedModelKeys.providerKey === "anthropic" && !validateAnthropicReasoningRuns(replayed)) {
10568
- dropSlot(args.sessionId, "lkg_anthropic_reasoning_run_invalid");
10569
- return { ok: false, reason: "lkg_anthropic_reasoning_run_invalid" };
10570
- }
10571
- return { ok: true, messages: replayed };
10572
- }
10573
-
10574
9946
  // src/hooks/magic-context/lkg-replay-fit.ts
10575
9947
  var RAW_FALLBACK_BYTES_PER_CONTEXT_TOKEN = 4;
10576
9948
  function lkgReplayLimit(args) {
@@ -10637,7 +10009,7 @@ class StorageBusyRefusalError extends Error {
10637
10009
  }
10638
10010
 
10639
10011
  // src/hooks/magic-context/system-prompt-hash.ts
10640
- import { createHash as createHash5 } from "node:crypto";
10012
+ import { createHash as createHash3 } from "node:crypto";
10641
10013
 
10642
10014
  // src/agents/magic-context-prompt.ts
10643
10015
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -10780,8 +10152,160 @@ ${BASE_INTRO(memoryEnabled, temporalAwarenessEnabled)}${cavemanWarning}
10780
10152
  ${SELF_TAG_GUIDANCE}${languageGuidance}`;
10781
10153
  }
10782
10154
 
10155
+ // src/hooks/magic-context/system-prompt-dates.ts
10156
+ function replaceHostDates(prompt, replace) {
10157
+ const fields = /<(plugin-env|environment|env)>[\s\S]*?<\/\1>|Today's date: [^\r\n<]+/g;
10158
+ return prompt.replace(fields, (block) => block.replace(/(^[\t ]*(?:Date|Today's date):[\t ]*)([^\r\n<]+)/gm, (_line, label, date) => `${label}${replace(date)}`));
10159
+ }
10160
+ function systemPromptDates(prompt, sticky) {
10161
+ const values = [];
10162
+ replaceHostDates(prompt, (date) => {
10163
+ values.push(date);
10164
+ return date;
10165
+ });
10166
+ const prior = sticky ? JSON.parse(sticky) : [];
10167
+ let index = 0;
10168
+ const stable = replaceHostDates(prompt, (date) => prior[index++] ?? date);
10169
+ return { live: values.length ? JSON.stringify(values) : undefined, stable };
10170
+ }
10171
+
10172
+ // src/hooks/magic-context/system-date-refresh.ts
10173
+ var channels = new WeakMap;
10174
+ var outputUpdates = new WeakMap;
10175
+ var outputIdentities = new WeakMap;
10176
+ var outputRequests = new WeakMap;
10177
+ function systemDateTurnId(messages) {
10178
+ const start = activeAnthropicTurnStart(messages, true);
10179
+ if (start === undefined)
10180
+ return;
10181
+ const message = messages[start - 1];
10182
+ const id = message.info?.id ?? message.id;
10183
+ return typeof id === "string" && id.length > 0 ? id : undefined;
10184
+ }
10185
+ function bindSystemDateRequestIdentity(output, identity) {
10186
+ outputIdentities.set(output, identity);
10187
+ }
10188
+ function systemDateRequestIdentity(output) {
10189
+ return outputIdentities.get(output);
10190
+ }
10191
+ function bindSystemDateRequest(output, request) {
10192
+ if (request)
10193
+ outputRequests.set(output, request);
10194
+ }
10195
+ function systemDateRequestForOutput(history, output) {
10196
+ const request = outputRequests.get(output);
10197
+ return state(history, request);
10198
+ }
10199
+ function bindSystemDateOutput(system, update) {
10200
+ outputUpdates.set(system, update);
10201
+ }
10202
+ function publishSystemDateOutput(system) {
10203
+ outputUpdates.get(system)?.();
10204
+ outputUpdates.delete(system);
10205
+ }
10206
+ function registerSystemDateRefresh(history) {
10207
+ if (!channels.has(history))
10208
+ channels.set(history, new Map);
10209
+ }
10210
+ function hasSystemDateRefresh(history) {
10211
+ return channels.has(history);
10212
+ }
10213
+ function beginSystemDateRequest(history, sessionId, stage, turnId, identity) {
10214
+ const channel = channels.get(history);
10215
+ if (!channel)
10216
+ return;
10217
+ let requests = channel.get(sessionId);
10218
+ if (!requests)
10219
+ channel.set(sessionId, requests = new Map);
10220
+ let request = identity === undefined ? undefined : requests.get(identity);
10221
+ if (identity === undefined) {
10222
+ const candidates = [...requests.values()].filter((candidate) => !candidate.explicit && !(candidate.systemSeen && candidate.messagesSeen) && (stage === "system-after-messages" || candidate.turnId === turnId));
10223
+ const matching = candidates.filter((candidate) => !candidate.ambiguous && (stage === "messages" ? candidate.systemSeen && !candidate.messagesSeen : candidate.messagesSeen && !candidate.systemSeen));
10224
+ if (turnId !== undefined && candidates.length === 1 && matching.length === 1) {
10225
+ request = matching[0];
10226
+ } else if (candidates.length > 0) {
10227
+ for (const candidate of candidates)
10228
+ candidate.ambiguous = true;
10229
+ }
10230
+ }
10231
+ if (!request) {
10232
+ const sameTurn = [...requests.values()].some((candidate) => !candidate.explicit && !(candidate.systemSeen && candidate.messagesSeen) && (stage === "system-after-messages" || candidate.turnId === turnId));
10233
+ request = {
10234
+ sessionId,
10235
+ identity: identity ?? {},
10236
+ turnId,
10237
+ explicit: identity !== undefined,
10238
+ systemSeen: false,
10239
+ systemDone: false,
10240
+ messagesSeen: false,
10241
+ permitted: undefined,
10242
+ ambiguous: identity === undefined && (turnId === undefined || sameTurn)
10243
+ };
10244
+ const blocked = [...requests.values()].filter((candidate) => !candidate.explicit && candidate.ambiguous && !(candidate.systemSeen && candidate.messagesSeen) && candidate.turnId === turnId).length;
10245
+ if (request.explicit || !request.ambiguous || blocked < 2)
10246
+ requests.set(request.identity, request);
10247
+ }
10248
+ if (stage === "messages")
10249
+ request.messagesSeen = true;
10250
+ else
10251
+ request.systemSeen = true;
10252
+ return request;
10253
+ }
10254
+ function state(history, request) {
10255
+ if (!request)
10256
+ return;
10257
+ const current = channels.get(history)?.get(request.sessionId)?.get(request.identity);
10258
+ return current === request ? current : undefined;
10259
+ }
10260
+ function systemDateRefreshPermission(history, request) {
10261
+ const current = state(history, request);
10262
+ return current?.ambiguous ? undefined : current?.permitted;
10263
+ }
10264
+ function waitForSystemDateRefresh(history, request, apply) {
10265
+ const current = state(history, request);
10266
+ if (!current)
10267
+ return false;
10268
+ current.waiting = apply;
10269
+ return true;
10270
+ }
10271
+ function retire(history, current) {
10272
+ const unpairable = !current.explicit && current.turnId === undefined;
10273
+ if (!unpairable && (!current.systemDone || current.permitted === undefined))
10274
+ return;
10275
+ if (unpairable) {
10276
+ const apply = current.waiting;
10277
+ current.waiting = undefined;
10278
+ apply?.(false);
10279
+ }
10280
+ const channel = channels.get(history);
10281
+ const requests = channel?.get(current.sessionId);
10282
+ requests?.delete(current.identity);
10283
+ if (requests?.size === 0)
10284
+ channel?.delete(current.sessionId);
10285
+ }
10286
+ function finishSystemDateRefresh(history, request, permitted) {
10287
+ const current = state(history, request);
10288
+ if (!current)
10289
+ return;
10290
+ current.permitted = current.ambiguous ? false : permitted;
10291
+ const apply = current.waiting;
10292
+ current.waiting = undefined;
10293
+ apply?.(current.permitted);
10294
+ retire(history, current);
10295
+ }
10296
+ function finishSystemDateHook(history, request) {
10297
+ const current = state(history, request);
10298
+ if (!current)
10299
+ return;
10300
+ current.systemDone = true;
10301
+ retire(history, current);
10302
+ }
10303
+ function clearSystemDateRefresh(history, sessionId) {
10304
+ channels.get(history)?.delete(sessionId);
10305
+ }
10306
+
10783
10307
  // src/hooks/magic-context/system-prompt-hash.ts
10784
- var MMC_MARKER = "## Magic Context";
10308
+ var MAGIC_CONTEXT_MARKER = "## Magic Context";
10785
10309
  var SYSTEM_PROMPT_GUIDANCE_SEPARATOR = `
10786
10310
 
10787
10311
  `;
@@ -10793,16 +10317,16 @@ function isInternalOpenCodeAgent(systemPromptContent) {
10793
10317
  return systemPromptContent.includes("You are a title generator. You output ONLY a thread title.") || systemPromptContent.includes("Summarize what was done in this conversation. Write like a pull request description.") || systemPromptContent.includes("You are an anchored context summarization assistant for coding sessions.") || systemPromptContent.includes("You are a context summarization agent. You are given a conversation between a user and an agent.");
10794
10318
  }
10795
10319
  function isMagicContextInternalAgent(systemPromptContent) {
10796
- return systemPromptContent.includes("You are Historian — the hippocampus of a long-running coding agent.");
10320
+ return systemPromptContent.includes("You are Historian — the hippocampus of a long-running coding agent.") || systemPromptContent.includes("for the magic-context system");
10797
10321
  }
10798
10322
  function createSystemPromptHashHandler(deps) {
10799
10323
  const promptSurfaceRuntime = deps.promptSurfaceRuntime ?? createPromptSurfaceRuntime({
10800
10324
  userConfigDirectory: process.cwd(),
10801
- warn: (message) => console.warn(`[magic-context] config warning: ${message}`)
10325
+ warn: (message) => log(`[magic-context] config warning: ${message}`)
10802
10326
  });
10803
10327
  const guidanceEpochs = createPromptSurfaceGuidanceEpochCache(promptSurfaceRuntime);
10804
10328
  const stickyDateBySession = new Map;
10805
- const handler = async (input, output) => {
10329
+ const processSystemPrompt = async (input, output) => {
10806
10330
  const sessionId = input.sessionID;
10807
10331
  if (!sessionId)
10808
10332
  return;
@@ -10826,6 +10350,10 @@ function createSystemPromptHashHandler(deps) {
10826
10350
  sessionLog(sessionId, "system-prompt-hash skipped (matched system_prompt_injection.skip_signatures)");
10827
10351
  return;
10828
10352
  }
10353
+ const messagesPrepared = deps.consumeMessagesPrepared?.(sessionId) ?? false;
10354
+ const dateRequest = beginSystemDateRequest(deps.historyRefreshSessions, sessionId, messagesPrepared ? "system-after-messages" : "system", input.dateRefreshRequest === undefined && hasSystemDateRefresh(deps.historyRefreshSessions) ? (deps.resolveDateTurnId ?? latestRealUserTurnIdFromOpenCodeDb)(sessionId) : undefined, input.dateRefreshRequest);
10355
+ bindSystemDateRequest(output, dateRequest);
10356
+ const requestMessagesFinal = messagesPrepared || systemDateRefreshPermission(deps.historyRefreshSessions, dateRequest) !== undefined;
10829
10357
  let sessionMetaEarly;
10830
10358
  try {
10831
10359
  sessionMetaEarly = getOrCreateSessionMeta(deps.db, sessionId);
@@ -10850,15 +10378,14 @@ function createSystemPromptHashHandler(deps) {
10850
10378
  const promptSurface = guidanceEpochs.resolve(sessionId, deps.promptSurface, modelKey);
10851
10379
  const fullPrompt = output.system.join(`
10852
10380
  `);
10853
- if (fullPrompt.length > 0 && !fullPrompt.includes(MMC_MARKER) && !skipGuidanceForDisabledSubagent) {
10381
+ if (fullPrompt.length > 0 && !fullPrompt.includes(MAGIC_CONTEXT_MARKER) && !skipGuidanceForDisabledSubagent) {
10854
10382
  const guidance = buildMagicContextSection(null, 0, effectiveCtxReduceEnabled, deps.experimentalTemporalAwareness, deps.experimentalCavemanTextCompression, subagentReduceMode, deps.language, deps.memoryEnabled !== false, promptSurface.preset, promptSurface.primaryOverride);
10855
10383
  output.system[0] = `${output.system[0]}${SYSTEM_PROMPT_GUIDANCE_SEPARATOR}${guidance}`;
10856
10384
  sessionLog(sessionId, `injected generic guidance into system prompt (ctxReduce=${effectiveCtxReduceEnabled}, subagent=${isSubagentSession}, subagentReduceMode=${subagentReduceMode})`);
10857
10385
  }
10858
10386
  const isCacheBusting = deps.systemPromptRefreshSessions.has(sessionId);
10859
10387
  const idleCacheExpired = sessionMetaEarly !== undefined && computeHardCacheExpired(sessionMetaEarly.cacheTtl, sessionMetaEarly.lastResponseTime, Date.now());
10860
- const DATE_PATTERN = /Today's date: .+/;
10861
- const DATE_PATTERN_ALL = /Today's date: .+/g;
10388
+ const adoptSystemChange = idleCacheExpired || requestMessagesFinal && !isHeldHeadForRequest(sessionId, currentLkgRequestIdentity(sessionId));
10862
10389
  const liveSystemContent = output.system.join(`
10863
10390
  `);
10864
10391
  if (liveSystemContent.length === 0)
@@ -10867,29 +10394,56 @@ function createSystemPromptHashHandler(deps) {
10867
10394
  const hasPersistedHash = previousHash !== "" && previousHash !== "0";
10868
10395
  if (!hasPersistedHash)
10869
10396
  stickyDateBySession.delete(sessionId);
10870
- const dateElementIndexes = [];
10871
- let currentDate;
10872
- for (let i = 0;i < output.system.length; i++) {
10873
- const match = output.system[i].match(DATE_PATTERN);
10874
- if (!match)
10875
- continue;
10876
- dateElementIndexes.push(i);
10877
- currentDate ??= match[0];
10878
- }
10879
10397
  const stickyDate = stickyDateBySession.get(sessionId);
10880
- const stableCandidate = currentDate && stickyDate && currentDate !== stickyDate ? liveSystemContent.replace(DATE_PATTERN_ALL, stickyDate) : liveSystemContent;
10881
- const stableCandidateHash = createHash5("md5").update(promptSurfaceHashMaterial(stableCandidate, promptSurface.preset)).digest("hex");
10398
+ const dates = systemPromptDates(liveSystemContent, stickyDate);
10399
+ const currentDate = dates.live;
10400
+ const stableCandidate = dates.stable;
10401
+ const stableCandidateHash = createHash3("md5").update(promptSurfaceHashMaterial(stableCandidate, promptSurface.preset)).digest("hex");
10882
10402
  const contentOrPresetChanged = hasPersistedHash && stableCandidateHash !== previousHash;
10883
- const dateMayAdvance = isCacheBusting || idleCacheExpired || contentOrPresetChanged;
10403
+ const rewritePermission = systemDateRefreshPermission(deps.historyRefreshSessions, dateRequest);
10404
+ let deferredDateRefresh = false;
10405
+ if (currentDate && stickyDate && currentDate !== stickyDate && isCacheBusting && !idleCacheExpired && !contentOrPresetChanged && !requestMessagesFinal) {
10406
+ const liveElements = [...output.system];
10407
+ deferredDateRefresh = waitForSystemDateRefresh(deps.historyRefreshSessions, dateRequest, (permitted) => {
10408
+ if (!permitted) {
10409
+ deps.systemPromptRefreshSessions.add(sessionId);
10410
+ return;
10411
+ }
10412
+ try {
10413
+ const content = liveElements.join(`
10414
+ `);
10415
+ const hash = createHash3("md5").update(promptSurfaceHashMaterial(content, promptSurface.preset)).digest("hex");
10416
+ updateSessionMeta(deps.db, sessionId, {
10417
+ systemPromptHash: hash,
10418
+ cachedM0SystemHash: hash,
10419
+ systemPromptTokens: estimateTokens(content)
10420
+ });
10421
+ stickyDateBySession.set(sessionId, currentDate);
10422
+ output.system.splice(0, output.system.length, ...liveElements);
10423
+ publishSystemDateOutput(output.system);
10424
+ deps.systemPromptRefreshSessions.delete(sessionId);
10425
+ sessionLog(sessionId, `system prompt date updated after prefix rewrite: ${stickyDate} → ${currentDate}`);
10426
+ } catch (error) {
10427
+ deps.systemPromptRefreshSessions.add(sessionId);
10428
+ sessionLog(sessionId, "system prompt date refresh failed (keeping frozen date):", error);
10429
+ }
10430
+ });
10431
+ }
10432
+ const dateMayAdvance = idleCacheExpired || contentOrPresetChanged || isCacheBusting && !deferredDateRefresh && (dateRequest ? rewritePermission === true : rewritePermission !== false);
10884
10433
  if (currentDate && !stickyDate) {
10885
10434
  stickyDateBySession.set(sessionId, currentDate);
10886
10435
  } else if (currentDate && stickyDate && currentDate !== stickyDate) {
10887
10436
  if (dateMayAdvance) {
10888
10437
  stickyDateBySession.set(sessionId, currentDate);
10889
10438
  sessionLog(sessionId, `system prompt date updated: ${stickyDate} → ${currentDate} (cache-busting pass)`);
10890
- } else if (dateElementIndexes.length > 0) {
10891
- for (const index of dateElementIndexes) {
10892
- output.system[index] = output.system[index].replace(DATE_PATTERN_ALL, stickyDate);
10439
+ } else {
10440
+ let offset = 0;
10441
+ const prior = JSON.parse(stickyDate);
10442
+ for (let index = 0;index < output.system.length; index++) {
10443
+ const element = systemPromptDates(output.system[index]);
10444
+ const count = element.live ? JSON.parse(element.live).length : 0;
10445
+ output.system[index] = systemPromptDates(output.system[index], JSON.stringify(prior.slice(offset, offset + count))).stable;
10446
+ offset += count;
10893
10447
  }
10894
10448
  sessionLog(sessionId, `system prompt date frozen: real=${currentDate}, using=${stickyDate} (defer pass)`);
10895
10449
  }
@@ -10898,17 +10452,20 @@ function createSystemPromptHashHandler(deps) {
10898
10452
  `);
10899
10453
  if (!availability.frozen || !modelKey)
10900
10454
  return;
10901
- const currentHash = createHash5("md5").update(promptSurfaceHashMaterial(systemContent, promptSurface.preset)).digest("hex");
10455
+ const currentHash = createHash3("md5").update(promptSurfaceHashMaterial(systemContent, promptSurface.preset)).digest("hex");
10456
+ recordSystemPromptForRequest(sessionId, currentLkgRequestIdentity(sessionId), currentHash, deps.db, previousHash !== currentHash ? !deps.historyRefreshSessions.has(sessionId) : undefined, deps.historyRefreshSessions, deps.pendingMaterializationSessions);
10902
10457
  if (!sessionMetaEarly) {
10903
10458
  return;
10904
10459
  }
10905
10460
  const sessionMeta = sessionMetaEarly;
10906
10461
  if (previousHash !== "" && previousHash !== "0" && previousHash !== currentHash) {
10907
- sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${systemContent.length}), ${idleCacheExpired ? "adopting on idle-expired request" : "triggering flush"}`);
10908
- if (!idleCacheExpired) {
10462
+ sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${systemContent.length}), ${idleCacheExpired ? "adopting on idle-expired request" : requestMessagesFinal ? "adopting on the request whose messages are already final" : "triggering flush"}`);
10463
+ if (!adoptSystemChange) {
10909
10464
  deps.historyRefreshSessions.add(sessionId);
10910
10465
  deps.systemPromptRefreshSessions.add(sessionId);
10911
- deps.pendingMaterializationSessions.add(sessionId);
10466
+ if (!deps.pendingMaterializationSessions.has(sessionId)) {
10467
+ requestPendingMaterialization(deps.pendingMaterializationSessions, sessionId, "system_hash");
10468
+ }
10912
10469
  deps.lastHeuristicsTurnId.delete(sessionId);
10913
10470
  }
10914
10471
  } else if (previousHash === "" || previousHash === "0") {
@@ -10925,19 +10482,27 @@ function createSystemPromptHashHandler(deps) {
10925
10482
  updateSessionMeta(deps.db, sessionId, {
10926
10483
  systemPromptHash: currentHash,
10927
10484
  systemPromptTokens,
10928
- cachedM0SystemHash: !hasPersistedHash || idleCacheExpired ? currentHash : undefined
10485
+ cachedM0SystemHash: !hasPersistedHash || adoptSystemChange ? currentHash : undefined
10929
10486
  });
10930
10487
  } catch (error) {
10931
10488
  sessionLog(sessionId, "system prompt meta persist failed (fail-open):", error);
10932
10489
  }
10933
10490
  }
10934
- if (isCacheBusting) {
10491
+ if (isCacheBusting && !deferredDateRefresh && (contentOrPresetChanged || !currentDate || currentDate === stickyDate || !dateRequest || rewritePermission === true)) {
10935
10492
  deps.systemPromptRefreshSessions.delete(sessionId);
10936
10493
  }
10937
10494
  };
10495
+ const handler = async (input, output) => {
10496
+ try {
10497
+ await processSystemPrompt(input, output);
10498
+ } finally {
10499
+ finishSystemDateHook(deps.historyRefreshSessions, systemDateRequestForOutput(deps.historyRefreshSessions, output));
10500
+ }
10501
+ };
10938
10502
  return {
10939
10503
  handler,
10940
10504
  clearSession: (sessionId) => {
10505
+ clearSystemDateRefresh(deps.historyRefreshSessions, sessionId);
10941
10506
  guidanceEpochs.clear(sessionId);
10942
10507
  clearSystemPromptHashSession(sessionId, {
10943
10508
  stickyDateBySession,
@@ -11657,12 +11222,8 @@ function markDropLeavesReasoning(db, sessionId) {
11657
11222
 
11658
11223
  // src/hooks/magic-context/reasoning-budget.ts
11659
11224
  function projectOpencodeReasoningBudgetCutoff(db, sessionId, messages, budget, watermark, proseRatio) {
11660
- const maxTags = new Map;
11661
- for (const tag of getTagsBySession(db, sessionId)) {
11662
- const id = tag.type === "tool" ? tag.toolOwnerMessageId : contentTagOwnerMessageId(tag.messageId);
11663
- if (id)
11664
- maxTags.set(id, Math.max(maxTags.get(id) ?? 0, tag.tagNumber));
11665
- }
11225
+ const textEstimateByMessageId = getReasoningTokenEstimatesByMessage(db, sessionId, proseRatio);
11226
+ const maxTags = getMaxTagNumberByOwnerMessage(db, sessionId);
11666
11227
  const messageTags = new Map;
11667
11228
  const gone = new Set(getReasoningRemovalState(db, sessionId).messageIds);
11668
11229
  for (const message of messages) {
@@ -11673,15 +11234,16 @@ function projectOpencodeReasoningBudgetCutoff(db, sessionId, messages, budget, w
11673
11234
  if (tag > 0 && tag <= watermark)
11674
11235
  gone.add(message.info.id);
11675
11236
  }
11237
+ const frozenMergedIds = getMergedReasoningStrippedIds(db, sessionId);
11676
11238
  return opencodeReasoningBudgetCutoff({
11677
11239
  messages,
11678
11240
  messageTagNumbers: messageTags,
11679
11241
  budget,
11680
11242
  alreadyRemoved: gone,
11681
- textEstimateByMessageId: getReasoningTokenEstimatesByMessage(db, sessionId, proseRatio),
11243
+ textEstimateByMessageId,
11682
11244
  proseRatio,
11683
- frozenMergedIds: getMergedReasoningStrippedIds(db, sessionId),
11684
- alsoGone: new Set([...getMergedReasoningStrippedIds(db, sessionId)].filter((id) => id.startsWith(THINKING_BINDING_RECOVERY_FROZEN_PREFIX)).map((id) => id.slice(THINKING_BINDING_RECOVERY_FROZEN_PREFIX.length)))
11245
+ frozenMergedIds,
11246
+ alsoGone: new Set([...frozenMergedIds].filter((id) => id.startsWith(THINKING_BINDING_RECOVERY_FROZEN_PREFIX)).map((id) => id.slice(THINKING_BINDING_RECOVERY_FROZEN_PREFIX.length)))
11685
11247
  });
11686
11248
  }
11687
11249
  var DEFAULT_KEEP_REASONING_TOKENS = 1e4;
@@ -11769,14 +11331,25 @@ function opencodeReasoningBudgetCutoff(args) {
11769
11331
  const assistants = args.messages.filter((message) => message.info.role === "assistant");
11770
11332
  const newest = assistants.at(-1);
11771
11333
  const exempt = findLatestAssistantReasoningMutationExemptMessage(args.messages);
11334
+ const anthropic = args.anthropic ?? hasAnthropicReasoning(args.messages);
11335
+ const indexOfMessage = new Map;
11336
+ args.messages.forEach((message, index) => {
11337
+ if (!indexOfMessage.has(message))
11338
+ indexOfMessage.set(message, index);
11339
+ });
11340
+ const frozenParts = args.frozenMergedIds ? readFrozenMergedReasoningParts(args.frozenMergedIds) : undefined;
11341
+ const frozenPartMessageIds = frozenParts ? new Set(frozenParts.keys()) : undefined;
11772
11342
  return reasoningBudgetCutoff(assistants.map((message) => ({
11773
11343
  tag: args.messageTagNumbers.get(message) ?? 0,
11774
- exempt: message === newest || message === exempt || isInActiveAnthropicTurn(args.messages, args.messages.indexOf(message), args.anthropic ?? hasAnthropicReasoning(args.messages)),
11344
+ exempt: message === newest || message === exempt || isInActiveAnthropicTurn(args.messages, indexOfMessage.get(message) ?? -1, anthropic),
11775
11345
  cost: () => {
11776
11346
  const costMessage = { ...message, parts: [...message.parts] };
11777
- if (args.frozenMergedIds)
11347
+ const id = message.info.id ?? "";
11348
+ if (args.frozenMergedIds && (frozenParts?.has(id) || args.frozenMergedIds.has(id)))
11778
11349
  stripReasoningFromMergedAssistants([costMessage], "anthropic", {
11779
- frozenMessageIds: args.frozenMergedIds
11350
+ frozenMessageIds: args.frozenMergedIds,
11351
+ frozenParts,
11352
+ frozenPartMessageIds
11780
11353
  });
11781
11354
  const visible = reasoningTextAndOpaque(args.countNeutralized ? costMessage.parts.map(neutralizedReasoningSource) : costMessage.parts);
11782
11355
  if (!visible.hasReasoning && !visible.inlineText)
@@ -12151,6 +11724,7 @@ var PASS_DEGRADATION_EFFECTS = {
12151
11724
  "auto-search-timeout": "served",
12152
11725
  "auto-search-search-failure": "served",
12153
11726
  "auto-search-cas-exhaustion": "served",
11727
+ "auto-search-storage-busy": "served",
12154
11728
  "auto-search-internal-failure": "served",
12155
11729
  "thinking-binding-recovery-persistence-failure": "changes-request",
12156
11730
  "merged-reasoning-strip-persistence-failure": "changes-request",
@@ -12609,15 +12183,22 @@ function buildReplacementContent(tagId) {
12609
12183
  return `[dropped §${tagId}§]`;
12610
12184
  }
12611
12185
  function applyNewToolDrop(target, options) {
12612
- if (!target)
12186
+ if (options.persist && (target?.prepareDrop?.() ?? target?.canDrop?.()) === false) {
12187
+ return { result: "incomplete", mode: "full" };
12188
+ }
12189
+ if (!target) {
12190
+ options.persist?.("full");
12613
12191
  return { result: "absent", mode: "full" };
12614
- if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true) {
12192
+ }
12193
+ if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true || target.wouldStrandConversationEnd?.() === true) {
12615
12194
  const strip = target.hasAttachments?.() === true;
12195
+ options.persist?.(strip ? "skeleton_stripped" : "skeleton_real");
12616
12196
  return {
12617
12197
  result: (strip ? target.skeletonStripped?.() : target.skeletonReal?.()) ?? "absent",
12618
12198
  mode: strip ? "skeleton_stripped" : "skeleton_real"
12619
12199
  };
12620
12200
  }
12201
+ options.persist?.("full");
12621
12202
  const result = target.drop?.() ?? "absent";
12622
12203
  if (result === "truncated" && target.hasAttachments?.() === true) {
12623
12204
  return { result: target.skeletonStripped?.() ?? "absent", mode: "skeleton_stripped" };
@@ -12628,6 +12209,18 @@ function hasSmallToolInput(target) {
12628
12209
  const bytes = target?.inputStringBytes?.() ?? null;
12629
12210
  return bytes !== null && bytes <= SKELETON_REAL_INPUT_MAX_BYTES;
12630
12211
  }
12212
+ function applyPersistedToolDrop(db, sessionId, tagNumber, target, options, afterWrite) {
12213
+ return applyNewToolDrop(target, {
12214
+ ...options,
12215
+ persist: (mode) => {
12216
+ db.transaction(() => {
12217
+ updateTagDropMode(db, sessionId, tagNumber, mode);
12218
+ updateTagStatus(db, sessionId, tagNumber, "dropped");
12219
+ afterWrite?.();
12220
+ }).immediate();
12221
+ }
12222
+ });
12223
+ }
12631
12224
  var CACHE_LOSING_FOLD_REASONS = new Set([
12632
12225
  "model_change",
12633
12226
  "system_hash",
@@ -12701,11 +12294,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12701
12294
  diagnostics.reasons[reason] = (diagnostics.reasons[reason] ?? 0) + 1;
12702
12295
  };
12703
12296
  let admitted = false;
12704
- let startedAt = 0;
12705
12297
  try {
12706
12298
  db.transaction(() => {
12707
12299
  admitted = true;
12708
- startedAt = performance.now();
12300
+ }).immediate();
12301
+ {
12709
12302
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
12710
12303
  const neededTagNumbers = new Set(pendingOps.map((op) => op.tagId));
12711
12304
  for (const op of syntheticPendingOps)
@@ -12764,9 +12357,30 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12764
12357
  }
12765
12358
  }
12766
12359
  let shouldPersistDrop = false;
12360
+ const persistDrop = (mode) => {
12361
+ let startedAt = 0;
12362
+ try {
12363
+ db.transaction(() => {
12364
+ startedAt = performance.now();
12365
+ if (mode)
12366
+ updateTagDropMode(db, sessionId, pendingOp.tagId, mode);
12367
+ updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
12368
+ if (!synthetic)
12369
+ removePendingOp(db, sessionId, pendingOp.tagId);
12370
+ }).immediate();
12371
+ } finally {
12372
+ if (startedAt)
12373
+ logSlowWriteTransaction("apply_pending_operation", startedAt);
12374
+ }
12375
+ };
12767
12376
  if (isToolTag) {
12768
12377
  if (editMarkerTagIds.has(pendingOp.tagId)) {
12769
12378
  const strip = target?.hasAttachments?.() === true;
12379
+ if (target?.canDrop?.() === false || target?.prepareDrop?.() === false || !target) {
12380
+ reject("edit_marker_incomplete");
12381
+ continue;
12382
+ }
12383
+ persistDrop(strip ? "edit_marker_stripped" : "edit_marker");
12770
12384
  const markResult = (strip ? target?.editMarkerStripped?.() : target?.editMarker?.()) ?? "absent";
12771
12385
  if (markResult === "incomplete" || markResult === "absent") {
12772
12386
  reject(`edit_marker_${markResult}`);
@@ -12775,11 +12389,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12775
12389
  didMutateMessage = true;
12776
12390
  operationMutated = true;
12777
12391
  onTagReduced?.({ tagNumber: pendingOp.tagId, mode: "edit_marker" });
12778
- updateTagDropMode(db, sessionId, pendingOp.tagId, strip ? "edit_marker_stripped" : "edit_marker");
12779
12392
  shouldPersistDrop = true;
12780
12393
  } else {
12781
12394
  const { result: dropResult, mode: appliedMode } = applyNewToolDrop(target, {
12782
- inWindow: skeletonWindow.has(pendingOp.tagId)
12395
+ inWindow: skeletonWindow.has(pendingOp.tagId),
12396
+ persist: persistDrop
12783
12397
  });
12784
12398
  if (dropResult === "incomplete" || synthetic && dropResult !== "removed" && dropResult !== "truncated") {
12785
12399
  reject(`drop_${dropResult}`);
@@ -12795,12 +12409,12 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12795
12409
  } else {
12796
12410
  reject(`drop_${dropResult}`);
12797
12411
  }
12798
- updateTagDropMode(db, sessionId, pendingOp.tagId, appliedMode);
12799
12412
  shouldPersistDrop = true;
12800
12413
  }
12801
12414
  } else if (target) {
12802
12415
  const replacement = buildReplacementContent(pendingOp.tagId);
12803
12416
  const priorContent = target.getContent?.();
12417
+ persistDrop();
12804
12418
  const changed = target.setContent(replacement);
12805
12419
  if (changed) {
12806
12420
  didMutateMessage = true;
@@ -12817,30 +12431,25 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
12817
12431
  shouldPersistDrop = true;
12818
12432
  } else if (!synthetic) {
12819
12433
  reject("target_absent");
12434
+ persistDrop();
12820
12435
  shouldPersistDrop = true;
12821
12436
  }
12822
12437
  if (!shouldPersistDrop) {
12823
12438
  reject("not_persisted");
12824
12439
  continue;
12825
12440
  }
12826
- updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
12827
- if (!synthetic)
12828
- removePendingOp(db, sessionId, pendingOp.tagId);
12829
12441
  if (operationMutated)
12830
12442
  diagnostics.mutated += 1;
12831
12443
  else
12832
12444
  diagnostics.persistedWithoutMutation += 1;
12833
12445
  }
12834
- }).immediate();
12446
+ }
12835
12447
  } catch (error) {
12836
12448
  if (!admitted && /database (?:table )?is locked|sqlite_(busy|locked)/i.test(String(error))) {
12837
12449
  sessionLog(sessionId, "pending operations write admission busy; retaining prior bytes for retry");
12838
12450
  return false;
12839
12451
  }
12840
12452
  throw error;
12841
- } finally {
12842
- if (admitted)
12843
- logSlowWriteTransaction("apply_pending_operations", startedAt);
12844
12453
  }
12845
12454
  if (diagnostics) {
12846
12455
  onBatchComplete?.(diagnostics);
@@ -13088,6 +12697,7 @@ function getCachedInertWhitespaceAssistantTags(db, sessionId, tagger, messages)
13088
12697
  const messageIds = messages.flatMap((message) => typeof message.info.id === "string" ? [message.info.id] : []);
13089
12698
  const ownersKey = JSON.stringify(messageIds);
13090
12699
  const tagsVersion = tagger.getLoadedTagsVersion?.(sessionId, db);
12700
+ const identityRevision = tagger.getLoadedTagIdentityRevision?.(sessionId, db);
13091
12701
  if (tagsVersion === undefined)
13092
12702
  return getInertWhitespaceAssistantTags(db, sessionId, messageIds);
13093
12703
  let bySession = inertWhitespaceCache.get(db);
@@ -13096,10 +12706,10 @@ function getCachedInertWhitespaceAssistantTags(db, sessionId, tagger, messages)
13096
12706
  inertWhitespaceCache.set(db, bySession);
13097
12707
  }
13098
12708
  const cached = bySession.get(sessionId);
13099
- if (cached?.tagsVersion === tagsVersion && cached.ownersKey === ownersKey)
12709
+ if (cached?.tagsVersion === tagsVersion && cached.identityRevision === identityRevision && cached.ownersKey === ownersKey)
13100
12710
  return cached.tags;
13101
12711
  const tags = getInertWhitespaceAssistantTags(db, sessionId, messageIds);
13102
- bySession.set(sessionId, { tagsVersion, ownersKey, tags });
12712
+ bySession.set(sessionId, { tagsVersion, identityRevision, ownersKey, tags });
13103
12713
  return tags;
13104
12714
  }
13105
12715
  function makeToolOwnerCacheKey(sessionId, callId) {
@@ -13256,8 +12866,19 @@ function hasRecentAssistantCommit(messages) {
13256
12866
  }
13257
12867
  return false;
13258
12868
  }
13259
- function isVolatileDeepworkTextPart(part) {
13260
- return isTextPart(part) && isRecord(part) && part.synthetic === true && isRecord(part.metadata) && part.metadata["oh-my-opencode-slim.deepworkGoal"] === true;
12869
+ function isVolatileOmoSyntheticTextPart(part) {
12870
+ if (!isTextPart(part) || !isRecord(part))
12871
+ return false;
12872
+ if (part.synthetic !== true)
12873
+ return false;
12874
+ const metadata = part.metadata;
12875
+ if (!isRecord(metadata))
12876
+ return false;
12877
+ for (const [key, value] of Object.entries(metadata)) {
12878
+ if (key.startsWith("oh-my-opencode-slim.") && value === true)
12879
+ return true;
12880
+ }
12881
+ return false;
13261
12882
  }
13262
12883
  function tagMessages(sessionId, messages, tagger, db, options = {}) {
13263
12884
  const protectedThinkingParts = new Set([...options.protectedThinkingMessages ?? []].flatMap((message) => message.parts));
@@ -13315,9 +12936,16 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
13315
12936
  if (message.info.role === "user") {
13316
12937
  precedingThinkingParts = [];
13317
12938
  }
13318
- if (message.info.role === "user" && message.parts.length === 1 && isVolatileDeepworkTextPart(message.parts[0])) {
12939
+ if (message.info.role === "user" && message.parts.length === 1 && isVolatileOmoSyntheticTextPart(message.parts[0])) {
13319
12940
  continue;
13320
12941
  }
12942
+ if (message.info.role === "user" && messageId !== null) {
12943
+ for (let index = 0;index < message.parts.length; index += 1) {
12944
+ if (isVolatileOmoSyntheticTextPart(message.parts[index])) {
12945
+ tagger.unbindTag(sessionId, `${messageId}:p${index}`);
12946
+ }
12947
+ }
12948
+ }
13321
12949
  const messageThinkingParts = message.parts.filter(isThinkingPart);
13322
12950
  if (messageThinkingParts.length > 0) {
13323
12951
  reasoningByMessage.set(message, messageThinkingParts);
@@ -13379,6 +13007,10 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
13379
13007
  ownerByPartKey.set(part, { ownerMsgId, callId: toolObservation.callId });
13380
13008
  }
13381
13009
  if (messageId && isTextPart(part)) {
13010
+ if (message.info.role === "user" && isVolatileOmoSyntheticTextPart(part)) {
13011
+ textOrdinal += 1;
13012
+ continue;
13013
+ }
13382
13014
  const textPart = part;
13383
13015
  const thinkingParts = messageThinkingParts;
13384
13016
  const contentId = `${messageId}:p${partIndex}`;
@@ -13434,7 +13066,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
13434
13066
  accAssignTag += performance.now() - _tAssignText;
13435
13067
  const persistedSource = sourceContents.get(tagId);
13436
13068
  if (persistedSource !== undefined) {
13437
- textPart.text = persistedSource;
13069
+ textPart.text = options.normalizeRestoredAssistantText && message.info.role === "assistant" ? options.normalizeRestoredAssistantText(persistedSource) : persistedSource;
13438
13070
  } else {
13439
13071
  const sourceContent = stripTagPrefix(textPart.text);
13440
13072
  if (sourceContent.trim().length > 0) {
@@ -13724,31 +13356,27 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
13724
13356
  if (plan.shouldDrop) {
13725
13357
  const toDrop = new Set(plan.tagNumbers);
13726
13358
  const newestEmergencyTags = recentTags;
13727
- db.transaction(() => {
13728
- for (const tag of tags) {
13729
- if (!toDrop.has(tag.tagNumber))
13730
- continue;
13731
- if (tag.status !== "active" || tag.type !== "tool")
13732
- continue;
13733
- const target = targets.get(tag.tagNumber);
13734
- const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
13735
- const { result, mode } = applyNewToolDrop(target, {
13736
- inWindow: recent,
13737
- keepSkeleton: target?.requiresToolArcSkeleton === true
13359
+ for (const tag of tags) {
13360
+ if (!toDrop.has(tag.tagNumber))
13361
+ continue;
13362
+ if (tag.status !== "active" || tag.type !== "tool")
13363
+ continue;
13364
+ const target = targets.get(tag.tagNumber);
13365
+ const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
13366
+ const { result } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
13367
+ inWindow: recent,
13368
+ keepSkeleton: target?.requiresToolArcSkeleton === true
13369
+ });
13370
+ if (result === "removed" || result === "truncated") {
13371
+ droppedTools++;
13372
+ emergencyDroppedTools++;
13373
+ droppedTokenReductions.push({
13374
+ tagNumber: tag.tagNumber,
13375
+ mode: result === "removed" ? "full" : "truncated"
13738
13376
  });
13739
- if (result === "removed" || result === "truncated") {
13740
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
13741
- updateTagDropMode(db, sessionId, tag.tagNumber, mode);
13742
- droppedTools++;
13743
- emergencyDroppedTools++;
13744
- droppedTokenReductions.push({
13745
- tagNumber: tag.tagNumber,
13746
- mode: result === "removed" ? "full" : "truncated"
13747
- });
13748
- emergencyReclaimedTokens += estimateEmergencyDropReclaimTokens(measuredByTag.get(tag.tagNumber) ?? tag);
13749
- }
13377
+ emergencyReclaimedTokens += estimateEmergencyDropReclaimTokens(measuredByTag.get(tag.tagNumber) ?? tag);
13750
13378
  }
13751
- }).immediate();
13379
+ }
13752
13380
  sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
13753
13381
  } else {
13754
13382
  sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
@@ -13829,35 +13457,33 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
13829
13457
  group.push(tag);
13830
13458
  fingerprintGroups.set(fingerprint, group);
13831
13459
  }
13832
- db.transaction(() => {
13833
- for (const [, group] of fingerprintGroups) {
13834
- if (group.length <= 1)
13460
+ for (const [, group] of fingerprintGroups) {
13461
+ if (group.length <= 1)
13462
+ continue;
13463
+ group.sort((a, b) => a.tagNumber - b.tagNumber);
13464
+ for (let i = 0;i < group.length - 1; i++) {
13465
+ const tag = group[i];
13466
+ if (config.protectedTagNumbers.has(tag.tagNumber))
13835
13467
  continue;
13836
- group.sort((a, b) => a.tagNumber - b.tagNumber);
13837
- for (let i = 0;i < group.length - 1; i++) {
13838
- const tag = group[i];
13839
- if (config.protectedTagNumbers.has(tag.tagNumber))
13840
- continue;
13841
- const target = targets.get(tag.tagNumber);
13842
- if (protectedTools.has(tag.tagNumber))
13843
- continue;
13844
- if (target?.canDrop?.() === false)
13845
- continue;
13846
- const { result, mode } = applyNewToolDrop(target, { inWindow: false });
13847
- if (result === "incomplete")
13848
- continue;
13849
- updateTagDropMode(db, sessionId, tag.tagNumber, mode);
13850
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
13851
- if (result === "removed" || result === "truncated") {
13852
- deduplicatedTools++;
13853
- droppedTokenReductions.push({
13854
- tagNumber: tag.tagNumber,
13855
- mode: result === "removed" ? "full" : "truncated"
13856
- });
13857
- }
13468
+ const target = targets.get(tag.tagNumber);
13469
+ if (protectedTools.has(tag.tagNumber))
13470
+ continue;
13471
+ if (target?.canDrop?.() === false)
13472
+ continue;
13473
+ const { result } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
13474
+ inWindow: false
13475
+ });
13476
+ if (result === "incomplete")
13477
+ continue;
13478
+ if (result === "removed" || result === "truncated") {
13479
+ deduplicatedTools++;
13480
+ droppedTokenReductions.push({
13481
+ tagNumber: tag.tagNumber,
13482
+ mode: result === "removed" ? "full" : "truncated"
13483
+ });
13858
13484
  }
13859
13485
  }
13860
- }).immediate();
13486
+ }
13861
13487
  }
13862
13488
  if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0) {
13863
13489
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
@@ -13988,6 +13614,7 @@ function postprocessReplaySnapshot(db, sessionId) {
13988
13614
  ...replay,
13989
13615
  staleReduceStrippedIds: new Set(replay.staleReduceStrippedIds),
13990
13616
  processedImageStrippedIds: new Set(replay.processedImageStrippedIds),
13617
+ processedImageMarkers: new Map(replay.processedImageMarkers),
13991
13618
  strippedPlaceholderIds: new Set(replay.strippedPlaceholderIds),
13992
13619
  hiddenSeamPlaceholderIds: new Set(replay.hiddenSeamPlaceholderIds),
13993
13620
  mergedReasoningStrippedIds: new Set(replay.mergedReasoningStrippedIds),
@@ -14044,6 +13671,39 @@ function captureOpencodeReasoningBudgetStatus(sessionId, messages, budget, prefi
14044
13671
  sources.delete(sources.keys().next().value);
14045
13672
  }
14046
13673
 
13674
+ // src/hooks/magic-context/strict-thinking-prefix.ts
13675
+ function servedPrefixMatches(args) {
13676
+ const heads = args.messages?.slice(0, 2);
13677
+ if (heads?.length !== 2 || args.m0Text == null || args.m1Text == null)
13678
+ return false;
13679
+ const text = (message) => {
13680
+ if (!isRecord(message))
13681
+ return;
13682
+ if (typeof message.content === "string")
13683
+ return message.content;
13684
+ const parts = Array.isArray(message.parts) ? message.parts : message.content;
13685
+ if (!Array.isArray(parts))
13686
+ return;
13687
+ return parts.filter(isRecord).filter((part) => part.type === "text").map((part) => String(part.text ?? "")).join("");
13688
+ };
13689
+ if (text(heads[0]) !== args.m0Text || text(heads[1]) !== args.m1Text)
13690
+ return false;
13691
+ const head = heads[0];
13692
+ if (!isRecord(head))
13693
+ return false;
13694
+ const parts = Array.isArray(head.parts) ? head.parts : head.content;
13695
+ if (!Array.isArray(parts))
13696
+ return !args.muralDataUrl;
13697
+ const images = parts.filter(isRecord).filter((part) => part.type === "image" || part.type === "file");
13698
+ if (!args.muralDataUrl)
13699
+ return images.length === 0;
13700
+ if (images.length !== 1)
13701
+ return false;
13702
+ const image = images[0];
13703
+ const url = typeof image.url === "string" ? image.url : typeof image.data === "string" && typeof image.mimeType === "string" ? `data:${image.mimeType};base64,${image.data}` : undefined;
13704
+ return url === args.muralDataUrl;
13705
+ }
13706
+
14047
13707
  // src/hooks/magic-context/supersession-reclaim.ts
14048
13708
  var SUPERSESSION_RECENT_MESSAGE_WINDOW = 20;
14049
13709
  function recentSupersessionOwnerMessageIds(db, sessionId) {
@@ -14147,6 +13807,21 @@ function readFilePath(target) {
14147
13807
  return null;
14148
13808
  }
14149
13809
 
13810
+ // src/hooks/magic-context/tag-owner-warmup.ts
13811
+ function prewarmTagOwners(db, sessionId, ratio) {
13812
+ if (isTagOwnerSummaryReady(db, sessionId, ratio))
13813
+ return Promise.resolve(true);
13814
+ try {
13815
+ scanTagOwnerSummary(db, sessionId, ratio);
13816
+ return Promise.resolve(true);
13817
+ } catch {
13818
+ return Promise.resolve(false);
13819
+ }
13820
+ }
13821
+ async function waitForTagOwners(db, sessionId, ratio, _waitMs = 250) {
13822
+ return prewarmTagOwners(db, sessionId, ratio);
13823
+ }
13824
+
14150
13825
  // src/hooks/magic-context/tool-reclaim.ts
14151
13826
  function buildSyntheticToolReclaimOps(input) {
14152
13827
  const watermark = Math.max(0, input.watermark);
@@ -14515,6 +14190,9 @@ function finalizeMessageRepresentation(messages, resolvedProviderID, options) {
14515
14190
  async function runPostTransformPhase(args) {
14516
14191
  const tPostprocessSetup = performance.now();
14517
14192
  const compactionOff = args.compactionOff === true;
14193
+ if (!compactionOff && !tryGetReasoningTokenEstimatesByMessage(args.db, args.sessionId, sessionDecisionCalibration(args.db, args.sessionId).proseRatio)) {
14194
+ await waitForTagOwners(args.db, args.sessionId, sessionDecisionCalibration(args.db, args.sessionId).proseRatio);
14195
+ }
14518
14196
  const failPass = (site, error = new Error(`Could not complete ${site}`), kind) => {
14519
14197
  args.passOutcome?.record(site, kind);
14520
14198
  if (!compactionOff) {
@@ -14533,8 +14211,9 @@ async function runPostTransformPhase(args) {
14533
14211
  }
14534
14212
  const reasoningMutationExemptMessage = findLatestAssistantReasoningMutationExemptMessage(args.messages);
14535
14213
  const pendingMaterializationAtPassStart = args.pendingMaterializationSessions.has(args.sessionId);
14214
+ const pendingOrigin = pendingMaterializationOrigin(args.pendingMaterializationSessions, args.sessionId);
14536
14215
  const deferredMaterializationAtPassStart = args.deferredMaterializationSessions.has(args.sessionId);
14537
- const isExplicitFlush = pendingMaterializationAtPassStart;
14216
+ let isExplicitFlush = pendingMaterializationAtPassStart;
14538
14217
  const deferredMaterializationWasPending = deferredMaterializationAtPassStart;
14539
14218
  const alreadyRanThisTurn = args.currentTurnId !== null && args.lastHeuristicsTurnId.get(args.sessionId) === args.currentTurnId;
14540
14219
  const forceMaterialization = args.fullFeatureMode && !compactionOff && args.contextUsage.percentage >= args.forceMaterializationPercentage;
@@ -14551,8 +14230,8 @@ async function runPostTransformPhase(args) {
14551
14230
  }
14552
14231
  const activeCompartmentRun = args.canRunCompartments ? getActiveCompartmentRun(args.sessionId) : undefined;
14553
14232
  const compartmentRunning = args.canRunCompartments && !args.awaitedCompartmentRun && activeCompartmentRun !== undefined;
14554
- const deferredMaterialize = args.canConsumeDeferredLate && deferredMaterializationWasPending;
14555
- const materializationRequested = isExplicitFlush || deferredMaterialize;
14233
+ let deferredMaterialize = args.canConsumeDeferredLate && deferredMaterializationWasPending;
14234
+ let materializationRequested = isExplicitFlush || deferredMaterialize;
14556
14235
  const m0M1EnabledForFold = args.m0M1 !== undefined && (!!args.m0M1.projectPath || !!args.m0M1.projectDirectory) && (args.fullFeatureMode || compactionOff);
14557
14236
  const frozenThinking = new Set(args.activeThinkingTurn ? getMergedReasoningStrippedIds(args.db, args.sessionId) : []);
14558
14237
  const bindingThinking = new Set([...frozenThinking].filter((id) => id.startsWith(THINKING_BINDING_RECOVERY_FROZEN_PREFIX)).map((id) => id.slice(THINKING_BINDING_RECOVERY_FROZEN_PREFIX.length)));
@@ -14564,9 +14243,9 @@ async function runPostTransformPhase(args) {
14564
14243
  bindingIds: bindingThinking,
14565
14244
  removedIds: getReasoningRemovalState(args.db, args.sessionId).messageIds
14566
14245
  }) : new Set;
14567
- const activeThinkingTurn = retainedThinking.size > 0;
14246
+ let activeThinkingTurn = retainedThinking.size > 0;
14568
14247
  const protectedThinkingMessages = new Set(args.messages.filter((message) => message.parts.some((part) => retainedThinking.has(part))));
14569
- const newTargets = protectNewTagMutations(args.messages, args.targets, retainedThinking, args.thinkingBindingRecoveryEnabledForModel === true);
14248
+ let newTargets = protectNewTagMutations(args.messages, args.targets, retainedThinking, args.thinkingBindingRecoveryEnabledForModel === true);
14570
14249
  let maxReasoningTag = 0;
14571
14250
  for (const tag of args.messageTagNumbers.values())
14572
14251
  maxReasoningTag = Math.max(maxReasoningTag, tag);
@@ -14576,7 +14255,28 @@ async function runPostTransformPhase(args) {
14576
14255
  if (tag && tag > 0)
14577
14256
  safeReasoningCutoff = Math.min(safeReasoningCutoff, tag - 1);
14578
14257
  }
14579
- const freezeM0M1 = args.freezeM0M1 === true || activeThinkingTurn && args.thinkingBindingRecoveryEnabledForModel === true;
14258
+ let activeThinkingPrefixHold = args.activeThinkingPrefixHold === true || activeThinkingTurn && args.thinkingBindingRecoveryEnabledForModel === true;
14259
+ let freezeM0M1 = args.freezeM0M1 === true || activeThinkingPrefixHold;
14260
+ let rewritingSignedPrefix = false;
14261
+ let thinkingReleasedForFit = false;
14262
+ const releaseThinkingForRewrite = (forFit = false) => {
14263
+ const stripped = freezeReasoningOnBustingPass({
14264
+ db: args.db,
14265
+ sessionId: args.sessionId,
14266
+ messages: args.messages,
14267
+ alreadyFrozen: bindingThinking
14268
+ });
14269
+ if (stripped.persistenceFailed)
14270
+ throw new Error("Cannot persist signed thinking omission before prefix rewrite");
14271
+ retainedThinking.clear();
14272
+ protectedThinkingMessages.clear();
14273
+ activeThinkingTurn = false;
14274
+ activeThinkingPrefixHold = false;
14275
+ freezeM0M1 = args.directoryPrefixHold === true;
14276
+ newTargets = args.targets;
14277
+ rewritingSignedPrefix = true;
14278
+ thinkingReleasedForFit ||= forFit;
14279
+ };
14580
14280
  const foldDueDecision = m0M1EnabledForFold && args.m0M1 && !freezeM0M1 ? mustMaterialize({
14581
14281
  db: args.db,
14582
14282
  sessionId: args.sessionId,
@@ -14594,6 +14294,7 @@ async function runPostTransformPhase(args) {
14594
14294
  const cachedPrefixBeforePreflight = shouldCaptureCachedPrefix ? prepareCachedM0M1Replay(args.db, args.sessionId) : undefined;
14595
14295
  const completeCachedPrefixAvailable = args.sessionMeta.cachedM0Bytes != null && args.sessionMeta.cachedM1Bytes != null || cachedPrefixBeforePreflight !== undefined || m0M1EnabledForFold && !shouldCaptureCachedPrefix && hasCompleteCachedM0M1(args.db, args.sessionId);
14596
14296
  const firstRenderBust = m0M1EnabledForFold && !completeCachedPrefixAvailable;
14297
+ const servedBeforeFirstRender = firstRenderBust ? args.readLastServedMessages?.() : null;
14597
14298
  let foldExecutedThisPass = false;
14598
14299
  let foldBustsServedPrefixThisPass = false;
14599
14300
  let publishedM1RefreshedThisPass = false;
@@ -14609,7 +14310,7 @@ async function runPostTransformPhase(args) {
14609
14310
  m0Bytes: args.sessionMeta.cachedM0Bytes ?? null,
14610
14311
  m1Bytes: args.sessionMeta.cachedM1Bytes ?? null
14611
14312
  };
14612
- if ((foldDueDecision.value || softRefreshOpportunity) && m0M1EnabledForFold && args.m0M1) {
14313
+ if ((foldDueDecision.value || softRefreshOpportunity || firstRenderBust && activeThinkingPrefixHold) && m0M1EnabledForFold && args.m0M1) {
14613
14314
  let committedFoldBustsServedPrefix;
14614
14315
  try {
14615
14316
  const previousM1 = args.sessionMeta.cachedM1Bytes?.toString("utf8");
@@ -14632,7 +14333,11 @@ async function runPostTransformPhase(args) {
14632
14333
  onFoldPrepare: () => {
14633
14334
  const conversions = prepareLegacyToolSkeletonConversions(args.db, args.sessionId, args.targets);
14634
14335
  return (db, rendered) => {
14635
- const bustsPrefix = foldBustsServedPrefix(foldDueDecision.reason, servedPrefixBeforeFold, rendered);
14336
+ const bustsPrefix = activeThinkingPrefixHold ? !servedPrefixMatches({
14337
+ messages: servedBeforeFirstRender,
14338
+ m0Text: rendered.m0Bytes.toString(),
14339
+ m1Text: rendered.m1Bytes.toString()
14340
+ }) : foldBustsServedPrefix(foldDueDecision.reason, servedPrefixBeforeFold, rendered);
14636
14341
  if (bustsPrefix) {
14637
14342
  if (!conversions.isCurrent(db)) {
14638
14343
  throw new MaterializeContentionError({
@@ -14649,6 +14354,9 @@ async function runPostTransformPhase(args) {
14649
14354
  }
14650
14355
  });
14651
14356
  preparedPrefix = foldResult;
14357
+ if (activeThinkingPrefixHold && firstRenderBust && committedFoldBustsServedPrefix) {
14358
+ releaseThinkingForRewrite();
14359
+ }
14652
14360
  prefixPreflightFailed = foldResult.materializationContentionRetryExhausted === true;
14653
14361
  foldExecutedThisPass = foldExecutesThisPass(foldDueDecision.value || softRefreshOpportunity, foldResult.m0RematerializedThisPass);
14654
14362
  foldBustsServedPrefixThisPass = foldExecutedThisPass && (committedFoldBustsServedPrefix ?? true);
@@ -14682,18 +14390,33 @@ async function runPostTransformPhase(args) {
14682
14390
  }
14683
14391
  sessionLog(args.sessionId, `m[0] HARD fold decision: reason=${m0MaterializeReason ?? foldDueDecision.reason ?? (foldExecutedThisPass ? "drift" : "soft_refresh")} executed=${foldExecutedThisPass} bustsServedPrefix=${foldBustsServedPrefixThisPass}`);
14684
14392
  }
14393
+ if (args.normalizeRecoveryTail && (foldBustsServedPrefixThisPass || firstRenderBust)) {
14394
+ args.normalizeRecoveryTail();
14395
+ args.tags = getTagsBySession(args.db, args.sessionId);
14396
+ newTargets = protectNewTagMutations(args.messages, args.targets, retainedThinking, args.thinkingBindingRecoveryEnabledForModel === true);
14397
+ }
14685
14398
  logTransformTiming(args.sessionId, "pp.prefixPreflight", tPostprocessSetup, `foldExecuted=${foldExecutedThisPass} m1Refreshed=${publishedM1RefreshedThisPass}`);
14686
14399
  const tPassDecisions = performance.now();
14400
+ const frontToolCandidates = args.tags.filter((tag) => tag.type === "tool" && tag.status === "active" && args.targets.get(tag.tagNumber)?.canDrop?.());
14401
+ if (activeThinkingPrefixHold && (args.forceSignedRebuildForFit || args.contextUsage.percentage >= 95 && frontToolCandidates.length > 0 && frontToolCandidates.every((tag) => newTargets.get(tag.tagNumber)?.thinkingRewriteProtected === true))) {
14402
+ releaseThinkingForRewrite(true);
14403
+ }
14404
+ if (activeThinkingPrefixHold) {
14405
+ isExplicitFlush = false;
14406
+ deferredMaterialize = false;
14407
+ materializationRequested = false;
14408
+ }
14687
14409
  const shouldReadPendingOps = !compactionOff && (materializationRequested || publishedM1RefreshedThisPass || args.schedulerDecision === "execute" || forceMaterialization || foldExecutedThisPass || firstRenderBust || compartmentRunning);
14688
14410
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
14689
14411
  const hasPendingUserOps = pendingOps.length > 0;
14690
14412
  if (args.schedulerDecision === "execute" && pendingOps.some((op) => newTargets.get(op.tagId)?.thinkingDropProtected))
14691
- args.pendingMaterializationSessions.add(args.sessionId);
14413
+ requestPendingMaterialization(args.pendingMaterializationSessions, args.sessionId, "thinking_hold");
14692
14414
  const formatPendingOpsDepth = () => {
14693
14415
  const depth = getPendingOpsCount(args.db, args.sessionId);
14694
14416
  return depth === null ? "not loaded (deferred pass)" : String(depth);
14695
14417
  };
14696
- const idleExpiryRebuild = !freezeM0M1 && args.m0M1?.hardSignals?.cacheExpired === true;
14418
+ const heldIdleExpiryRebuild = activeThinkingPrefixHold && args.m0M1?.hardSignals?.cacheExpired === true;
14419
+ const idleExpiryRebuild = (!freezeM0M1 || activeThinkingPrefixHold) && args.m0M1?.hardSignals?.cacheExpired === true;
14697
14420
  const rideSignals = {
14698
14421
  subagentExecute: !args.fullFeatureMode && args.schedulerDecision === "execute",
14699
14422
  hardFold: foldBustsServedPrefixThisPass || firstRenderBust || idleExpiryRebuild,
@@ -14701,15 +14424,16 @@ async function runPostTransformPhase(args) {
14701
14424
  explicitFlush: isExplicitFlush || deferredMaterialize && !prefixPreflightFailed,
14702
14425
  publishedHistory: publishedM1RefreshedThisPass || !m0M1EnabledForFold && (args.pendingCompartmentInjection?.compartmentCount ?? 0) > 0 && (args.historyRebuiltThisPass || args.rebuiltHistoryFromInitialPrepare || args.canConsumeDeferredLate && args.deferredHistoryWasPendingAtPassStart)
14703
14426
  };
14704
- const publishedWorkDrainAllowed = !compactionOff && hasReclaimRide(rideSignals);
14427
+ const publishedWorkDrainAllowed = !compactionOff && (!activeThinkingPrefixHold || firstRenderBust || idleExpiryRebuild || emergencyDropEligible) && hasReclaimRide(rideSignals);
14705
14428
  const shouldApplyPendingOps = publishedWorkDrainAllowed;
14706
- const shouldRunHeuristics = !compactionOff && hasReclaimRide(rideSignals) && (rideSignals.publishedHistory || materializationRequested || forceMaterialization || foldBustsServedPrefixThisPass || firstRenderBust || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
14429
+ const shouldRunHeuristics = !compactionOff && publishedWorkDrainAllowed && (rideSignals.publishedHistory || materializationRequested || forceMaterialization || foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
14707
14430
  const isCacheBustingPass = publishedWorkDrainAllowed;
14431
+ const canEditWholePrefix = isCacheBustingPass && !activeThinkingPrefixHold;
14708
14432
  const previousTemporalDecisions = args.temporalCandidates && !compactionOff ? new Map([
14709
14433
  ...args.temporalObservedDecisions ?? [],
14710
14434
  ...getTemporalDecisions(args.db, args.sessionId, args.temporalReplayIds ?? args.temporalCandidates.keys())
14711
14435
  ]) : undefined;
14712
- const temporalDecisions = args.temporalCandidates && !compactionOff ? isCacheBustingPass ? new Map([
14436
+ const temporalDecisions = args.temporalCandidates && !compactionOff ? canEditWholePrefix ? new Map([
14713
14437
  ...previousTemporalDecisions ?? [],
14714
14438
  ...freezeTemporalDecisions(args.db, args.sessionId, args.temporalCandidates)
14715
14439
  ]) : previousTemporalDecisions : undefined;
@@ -14725,6 +14449,43 @@ async function runPostTransformPhase(args) {
14725
14449
  const removedReasoningIds = new Set;
14726
14450
  let dropLeavesReasoning = false;
14727
14451
  let reasoningRemovalReadable = false;
14452
+ const refreshReasoningRemovalUnion = () => {
14453
+ if (compactionOff)
14454
+ return;
14455
+ try {
14456
+ if (reasoningRemovalEnabled) {
14457
+ const committed = getReasoningRemovalState(args.db, args.sessionId);
14458
+ for (const id of committed.messageIds)
14459
+ removedReasoningIds.add(id);
14460
+ dropLeavesReasoning = committed.dropLeavesReasoning;
14461
+ }
14462
+ const committedWatermark = args.db.prepare("SELECT cleared_reasoning_through_tag AS watermark, last_observed_model_key AS modelKey FROM session_meta WHERE session_id=?").get(args.sessionId);
14463
+ if (committedWatermark && committedWatermark.modelKey === args.sessionMeta.lastObservedModelKey) {
14464
+ args.sessionMeta.clearedReasoningThroughTag = Math.max(args.sessionMeta.clearedReasoningThroughTag, committedWatermark.watermark);
14465
+ if (!prefixBoundModel) {
14466
+ if (canUseEmptySentinels)
14467
+ replayClearedReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.sessionMeta.clearedReasoningThroughTag);
14468
+ replayStrippedInlineThinking(args.messages, args.messageTagNumbers, args.sessionMeta.clearedReasoningThroughTag);
14469
+ }
14470
+ }
14471
+ } catch (error) {
14472
+ failPass("reasoning-removal-read-failure", error);
14473
+ throw new EmergencyFailClosedError("Saved reasoning removals could not be refreshed", {
14474
+ cause: error
14475
+ });
14476
+ }
14477
+ };
14478
+ const replayCommittedVisibleDrops = () => {
14479
+ if (compactionOff)
14480
+ return false;
14481
+ const visible = new Set(args.messages);
14482
+ const targets = new Map([...args.targets].filter(([, target]) => {
14483
+ if (target.mutationParts)
14484
+ return target.mutationParts.some(({ message, part }) => visible.has(message) && message.info.syntheticHead !== true && message.parts.includes(part) && (!isRecord(part) || part.synthetic !== true));
14485
+ return target.message !== undefined && visible.has(target.message) && target.message.info.syntheticHead !== true;
14486
+ }));
14487
+ return applyFlushedStatuses(args.sessionId, args.db, targets);
14488
+ };
14728
14489
  if (reasoningRemovalEnabled) {
14729
14490
  try {
14730
14491
  const state = getReasoningRemovalState(args.db, args.sessionId);
@@ -14740,7 +14501,7 @@ async function runPostTransformPhase(args) {
14740
14501
  }
14741
14502
  if (shouldRunHeuristics) {
14742
14503
  const subagentRerun = !args.fullFeatureMode && alreadyRanThisTurn && args.schedulerDecision === "execute" && !isExplicitFlush && !forceMaterialization;
14743
- const reason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : forceMaterialization ? `force_materialization (${args.contextUsage.percentage.toFixed(1)}% >= ${args.forceMaterializationPercentage}%)` : foldBustsServedPrefixThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : subagentRerun ? `${reclaimRideLabel(rideSignals)} subagent_rerun (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})` : `${reclaimRideLabel(rideSignals)} (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
14504
+ const reason = isExplicitFlush ? `pending_materialization origin=${pendingOrigin}` : deferredMaterialize ? "deferred_materialization" : forceMaterialization ? `force_materialization (${args.contextUsage.percentage.toFixed(1)}% >= ${args.forceMaterializationPercentage}%)` : foldBustsServedPrefixThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : subagentRerun ? `${reclaimRideLabel(rideSignals)} subagent_rerun (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})` : `${reclaimRideLabel(rideSignals)} (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
14744
14505
  sessionLog(args.sessionId, `heuristics WILL RUN — reason=${reason}, context=${args.contextUsage.percentage.toFixed(1)}%, turn=${args.currentTurnId}`);
14745
14506
  } else if (args.schedulerDeferReason !== undefined && args.schedulerDeferReason !== null) {
14746
14507
  sessionLog(args.sessionId, `heuristics WILL NOT RUN — reason=${args.schedulerDeferReason}`);
@@ -14803,7 +14564,7 @@ async function runPostTransformPhase(args) {
14803
14564
  logTransformTiming(args.sessionId, "pp.passDecisions", tPassDecisions, `pendingOps=${pendingOps.length}`);
14804
14565
  try {
14805
14566
  if (shouldApplyPendingOps) {
14806
- const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : foldBustsServedPrefixThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : `${reclaimRideLabel(rideSignals)} (scheduler=${args.schedulerDecision})`;
14567
+ const applyReason = isExplicitFlush ? `pending_materialization origin=${pendingOrigin}` : deferredMaterialize ? "deferred_materialization" : foldBustsServedPrefixThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : `${reclaimRideLabel(rideSignals)} (scheduler=${args.schedulerDecision})`;
14807
14568
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${formatPendingOpsDepth()}, context=${args.contextUsage.percentage.toFixed(1)}%`);
14808
14569
  const tApply = performance.now();
14809
14570
  pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, new Set([
@@ -14830,7 +14591,7 @@ async function runPostTransformPhase(args) {
14830
14591
  wordRules: args.cavemanTextCompression.wordRules
14831
14592
  } : undefined;
14832
14593
  const heuristicTags = shouldApplyPendingOps ? getActiveTagsBySession(args.db, args.sessionId) : args.tags;
14833
- const emergencyRewriteAlreadyPriced = pendingOpsDidMutate || foldBustsServedPrefixThisPass || args.historyRebuiltThisPass || args.compartmentInjectionRebuiltFromDb || args.rebuiltHistoryFromInitialPrepare;
14594
+ const emergencyRewriteAlreadyPriced = pendingOpsDidMutate || foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || args.historyRebuiltThisPass || args.compartmentInjectionRebuiltFromDb || args.rebuiltHistoryFromInitialPrepare;
14834
14595
  if (emergencyDropEligible && emergencyRewriteAlreadyPriced && getEmergencyInputSample(args.db, args.sessionId) > 0) {
14835
14596
  clearEmergencyDropSample(args.db, args.sessionId);
14836
14597
  }
@@ -14892,7 +14653,8 @@ async function runPostTransformPhase(args) {
14892
14653
  bindingStrippedIds.add(messageId);
14893
14654
  }
14894
14655
  }
14895
- const budgetCutoff = routineCleanupApplied ? Math.min(safeReasoningCutoff, opencodeReasoningBudgetCutoff({
14656
+ const reasoningEstimates = tryGetReasoningTokenEstimatesByMessage(args.db, args.sessionId, sessionDecisionCalibration(args.db, args.sessionId).proseRatio);
14657
+ const budgetCutoff = routineCleanupApplied && !activeThinkingPrefixHold && reasoningEstimates ? Math.min(safeReasoningCutoff, opencodeReasoningBudgetCutoff({
14896
14658
  messages: args.messages,
14897
14659
  messageTagNumbers: args.messageTagNumbers,
14898
14660
  budget: args.keepReasoningTokens ?? DEFAULT_KEEP_REASONING_TOKENS,
@@ -14902,7 +14664,7 @@ async function runPostTransformPhase(args) {
14902
14664
  proseRatio: sessionDecisionCalibration(args.db, args.sessionId).proseRatio,
14903
14665
  frozenMergedIds: getMergedReasoningStrippedIds(args.db, args.sessionId),
14904
14666
  anthropic: canUseEmptySentinels || prefixBoundModel || hasAnthropicReasoning(args.messages),
14905
- textEstimateByMessageId: getReasoningTokenEstimatesByMessage(args.db, args.sessionId, sessionDecisionCalibration(args.db, args.sessionId).proseRatio)
14667
+ textEstimateByMessageId: reasoningEstimates
14906
14668
  })) : 0;
14907
14669
  const clearedReasoning = routineCleanupApplied && canUseEmptySentinels && ageLaneAllowed ? clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, budgetCutoff) : 0;
14908
14670
  if (routineCleanupApplied && canUseEmptySentinels) {
@@ -14914,7 +14676,7 @@ async function runPostTransformPhase(args) {
14914
14676
  replayStrippedInlineThinking(args.messages, args.messageTagNumbers, persistedInlineWatermark);
14915
14677
  }
14916
14678
  let removedReasoningMessages = 0;
14917
- if (routineCleanupApplied && reasoningRemovalSelectable && reasoningRemovalReadable) {
14679
+ if (routineCleanupApplied && !prefixBoundModel && reasoningRemovalSelectable && reasoningRemovalReadable) {
14918
14680
  const newIds = selectReasoningRemovals({
14919
14681
  messages: args.messages,
14920
14682
  messageTagNumbers: args.messageTagNumbers,
@@ -14969,7 +14731,7 @@ async function runPostTransformPhase(args) {
14969
14731
  heuristicOrReasoningEditBesidesTrim = heuristicMutationCount + clearedReasoning + strippedInline > 0;
14970
14732
  droppedCount += clearedReasoning + strippedInline + removedReasoningMessages;
14971
14733
  if (pendingMaterializationAtPassStart && !freezeM0M1 && !pendingOps.some((op) => newTargets.get(op.tagId)?.thinkingDropProtected)) {
14972
- args.pendingMaterializationSessions.delete(args.sessionId);
14734
+ clearPendingMaterialization(args.pendingMaterializationSessions, args.sessionId);
14973
14735
  }
14974
14736
  if (args.currentTurnId) {
14975
14737
  args.lastHeuristicsTurnId.set(args.sessionId, args.currentTurnId);
@@ -15031,7 +14793,7 @@ async function runPostTransformPhase(args) {
15031
14793
  logTransformTiming(args.sessionId, "toolReclaim.select", tReclaimSelect, `targets=${autoReclaimTargetCount}`);
15032
14794
  if (syntheticPendingOps.length > 0) {
15033
14795
  const tReclaimApply = performance.now();
15034
- autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTagIds, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
14796
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, args.protectedTagIds, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
15035
14797
  logTransformTiming(args.sessionId, "toolReclaim.apply", tReclaimApply);
15036
14798
  if (autoReclaimDidMutate) {
15037
14799
  droppedCount += syntheticPendingOps.length;
@@ -15084,7 +14846,7 @@ async function runPostTransformPhase(args) {
15084
14846
  const t8 = performance.now();
15085
14847
  const frozenStaleReduceIds = replaySnapshot.staleReduceStrippedIds;
15086
14848
  const staleReduceResult = dropStaleReduceCalls(args.messages, frozenStaleReduceIds, {
15087
- detect: isCacheBustingPass,
14849
+ detect: canEditWholePrefix,
15088
14850
  protectedCount: args.protectedCount,
15089
14851
  protectedCallIds: new Set(args.tags.filter((tag) => protectedToolTags.has(tag.tagNumber)).map((tag) => tag.messageId)),
15090
14852
  onFirstApplication: recordFirstApplicationAt
@@ -15098,19 +14860,34 @@ async function runPostTransformPhase(args) {
15098
14860
  sessionLog(args.sessionId, "transform failed dropping stale mmc_reduce calls:", error);
15099
14861
  }
15100
14862
  }
15101
- if (canUseEmptySentinels && !compactionOff && replaySnapshot) {
14863
+ if (!compactionOff && replaySnapshot) {
15102
14864
  try {
15103
14865
  const tImg = performance.now();
14866
+ const imageWatermark = canEditWholePrefix ? Math.max(args.watermark, getMaxDroppedTagNumber(args.db, args.sessionId)) : args.watermark;
15104
14867
  const frozenImageIds = replaySnapshot.processedImageStrippedIds;
15105
- const imageResult = stripProcessedImages(args.messages, frozenImageIds, {
15106
- detect: isCacheBustingPass && args.watermark > 0,
15107
- watermark: args.watermark,
14868
+ const frozenMarkers = replaySnapshot.processedImageMarkers;
14869
+ const replacementText = canUseEmptySentinels ? "" : "[image stripped]";
14870
+ stripProcessedImages(args.messages, frozenImageIds, {
14871
+ detect: canEditWholePrefix && imageWatermark > 0,
14872
+ watermark: imageWatermark,
15108
14873
  messageTagNumbers: args.messageTagNumbers,
14874
+ protectedCutoff: args.protectedCutoff,
14875
+ contextPercentage: args.contextUsage.percentage,
14876
+ replacementText,
14877
+ frozenMarkers,
14878
+ protectedMessageIds: new Set([
14879
+ ...args.tags.filter((tag) => protectedToolTags.has(tag.tagNumber)).map((tag) => tag.toolOwnerMessageId ?? tag.messageId)
14880
+ ]),
14881
+ persistNewIds: (ids, markers) => {
14882
+ if (!addProcessedImageStrippedIds(args.db, args.sessionId, ids, markers))
14883
+ return false;
14884
+ const committed = getProcessedImageMarkers(args.db, args.sessionId);
14885
+ for (const id of ids)
14886
+ frozenMarkers.set(id, committed.get(id) ?? replacementText);
14887
+ return true;
14888
+ },
15109
14889
  onFirstApplication: recordFirstApplicationAt
15110
14890
  });
15111
- if (isCacheBustingPass && imageResult.newlyStrippedIds.length > 0) {
15112
- addProcessedImageStrippedIds(args.db, args.sessionId, imageResult.newlyStrippedIds);
15113
- }
15114
14891
  logTransformTiming(args.sessionId, "stripProcessedImages", tImg);
15115
14892
  } catch (error) {
15116
14893
  failPass("image-strip-exception", error);
@@ -15316,7 +15093,7 @@ async function runPostTransformPhase(args) {
15316
15093
  messages: args.messages,
15317
15094
  fullFeatureMode: args.fullFeatureMode,
15318
15095
  compactionOff,
15319
- isCacheBustingPass,
15096
+ isCacheBustingPass: canEditWholePrefix,
15320
15097
  sessionMeta: args.sessionMeta,
15321
15098
  todowriteAvailability: args.todowriteAvailability,
15322
15099
  client: args.client,
@@ -15382,23 +15159,22 @@ async function runPostTransformPhase(args) {
15382
15159
  autoSearchHintAppendedThisPass = autoSearchCompletedThisPass && countAutoSearchHints(args.db, args.sessionId) !== autoSearchHintsBefore;
15383
15160
  logTransformTiming(args.sessionId, "pp.autoSearchHint", tAutoSearch);
15384
15161
  }
15385
- if (args.fullFeatureMode && isCacheBustingPass) {
15162
+ if (args.fullFeatureMode && canEditWholePrefix) {
15386
15163
  const visibleIds = new Set;
15387
15164
  for (const message of args.messages) {
15388
15165
  if (typeof message.info?.id === "string") {
15389
15166
  visibleIds.add(message.info.id);
15390
15167
  }
15391
15168
  }
15392
- const prunedDecisions = pruneAutoSearchHintDecisions(args.db, args.sessionId, visibleIds);
15393
- if (prunedDecisions > 0) {
15394
- sessionLog(args.sessionId, `sticky-injection GC: pruned ${prunedDecisions} auto-search decision(s)`);
15395
- }
15169
+ flushAutoSearchHintDecisionRetirement(args.db, args.sessionId, visibleIds);
15396
15170
  }
15397
- const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? "explicit_flush" : null);
15171
+ const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? pendingOrigin : null);
15398
15172
  const materialized = m0RematerializedThisPass || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
15173
+ refreshReasoningRemovalUnion();
15174
+ replayCommittedVisibleDrops();
15399
15175
  const dropModeSwitchesThisPass = !canUseEmptySentinels && !dropLeavesReasoning && typeof args.resolvedProviderID === "string" && isCacheBustingPass && reasoningRemovalReadable && args.messages.some((message) => message.info.role === "assistant" && message.parts.some((part) => isNeutralizedReasoningPart(part)));
15400
15176
  let bustedThisPass = firstApplicationEdits.any || firstRenderBust || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || convertedToolSkeletonsDidMutate || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass;
15401
- const proactiveThinkingStripPermitted = isCacheBustingPass;
15177
+ const proactiveThinkingStripPermitted = isCacheBustingPass && !activeThinkingPrefixHold;
15402
15178
  const tFrozenDecisions = performance.now();
15403
15179
  if (reasoningMutationTargetUnknown) {
15404
15180
  const reasoningCandidates = args.reasoningByMessage.size > 0 ? args.reasoningByMessage.keys() : args.messages;
@@ -15450,7 +15226,7 @@ async function runPostTransformPhase(args) {
15450
15226
  sessionLog(args.sessionId, "thinking binding recovery: persistence failed; leaving the bound blocks intact");
15451
15227
  }
15452
15228
  }
15453
- if (canUseEmptySentinels && isCacheBustingPass) {
15229
+ if (canUseEmptySentinels && canEditWholePrefix) {
15454
15230
  const candidates = findMergedReasoningStripDecisions(args.messages, args.resolvedProviderID, mergedReasoningStrippedIds, {
15455
15231
  mutationExemptMessage: reasoningMutationExemptMessage,
15456
15232
  protectedMessages: args.protectedThinkingMessages
@@ -15497,7 +15273,7 @@ async function runPostTransformPhase(args) {
15497
15273
  }
15498
15274
  };
15499
15275
  const newestAssistantId = typeof trailingBlankNewestAssistant?.info.id === "string" ? trailingBlankNewestAssistant.info.id : undefined;
15500
- if (isCacheBustingPass && trailingBlankDecisions.size > 0) {
15276
+ if (canEditWholePrefix && trailingBlankDecisions.size > 0) {
15501
15277
  const visibleMessageIds = new Set(args.messages.flatMap((message) => typeof message.info.id === "string" ? [message.info.id] : []));
15502
15278
  const poisonedKeepIds = [];
15503
15279
  for (const [id, decision] of trailingBlankDecisions) {
@@ -15586,7 +15362,7 @@ async function runPostTransformPhase(args) {
15586
15362
  }
15587
15363
  }
15588
15364
  }
15589
- const prefixEditBesidesReasoningTrim = firstRenderBust || materializationRequested || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully || foldBustsServedPrefixThisPass || publishedM1RefreshedThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass || pendingOpsDidMutate || heuristicOrReasoningEditBesidesTrim || emergency || autoReclaimDidMutateThisPass || convertedToolSkeletonsDidMutate || dropModeSwitchesThisPass || deferredClearedMarkerRetires || autoSearchHintAppendedThisPass || todoAnchorMovedThisPass || firstApplicationEdits.beforeNewerThinking;
15365
+ const prefixEditBesidesReasoningTrim = firstRenderBust || heldIdleExpiryRebuild || materializationRequested || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully || foldBustsServedPrefixThisPass || publishedM1RefreshedThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass || pendingOpsDidMutate || heuristicOrReasoningEditBesidesTrim || emergency || autoReclaimDidMutateThisPass || convertedToolSkeletonsDidMutate || dropModeSwitchesThisPass || deferredClearedMarkerRetires || autoSearchHintAppendedThisPass || todoAnchorMovedThisPass || firstApplicationEdits.beforeNewerThinking;
15590
15366
  if (!compactionOff && args.thinkingBindingRecoveryEnabledForModel === true && proactiveThinkingStripPermitted && prefixEditBesidesReasoningTrim) {
15591
15367
  const outcome = freezeReasoningOnBustingPass({
15592
15368
  db: args.db,
@@ -15626,7 +15402,7 @@ async function runPostTransformPhase(args) {
15626
15402
  const tFinalRepresentation = performance.now();
15627
15403
  if (temporalDecisions)
15628
15404
  injectTemporalMarkers(args.messages, temporalDecisions);
15629
- const finalRepresentation = finalizeMessageRepresentation(args.messages, args.resolvedProviderID, {
15405
+ const finalizeOptions = {
15630
15406
  prependedMessageCount,
15631
15407
  reasoningMutatedMessages,
15632
15408
  reasoningMutationExemptMessage,
@@ -15637,10 +15413,21 @@ async function runPostTransformPhase(args) {
15637
15413
  trailingBlankDecisions,
15638
15414
  skipMergedReasoningStrip: compactionOff,
15639
15415
  skipTrailingWhitespaceStrip: compactionOff
15640
- });
15416
+ };
15417
+ const finalRepresentation = finalizeMessageRepresentation(args.messages, args.resolvedProviderID, finalizeOptions);
15641
15418
  const removedReasoningParts = reasoningRemovalEnabled ? removeReasoningParts(args.messages, removedReasoningIds, args.resolvedProviderID) : 0;
15642
15419
  const reasoningStatusRatio = sessionDecisionCalibration(args.db, args.sessionId).proseRatio;
15643
15420
  captureOpencodeReasoningBudgetStatus(args.sessionId, args.messages, args.keepReasoningTokens ?? DEFAULT_KEEP_REASONING_TOKENS, prefixBoundModel, reasoningStatusRatio);
15421
+ if (!rewritingSignedPrefix)
15422
+ args.restoreLatestTurnOriginals?.();
15423
+ if (!rewritingSignedPrefix && args.restoreLatestTurnOriginals) {
15424
+ refreshReasoningRemovalUnion();
15425
+ if (replayCommittedVisibleDrops()) {
15426
+ finalizeMessageRepresentation(args.messages, args.resolvedProviderID, finalizeOptions);
15427
+ }
15428
+ if (reasoningRemovalEnabled)
15429
+ removeReasoningParts(args.messages, removedReasoningIds, args.resolvedProviderID);
15430
+ }
15644
15431
  sessionLog(args.sessionId, `final representation: clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts} removedReasoningParts=${removedReasoningParts} settledDroppedReasoning=${settledDroppedReasoning}`);
15645
15432
  logTransformTiming(args.sessionId, "finalizeMessageRepresentation", tFinalRepresentation, `clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
15646
15433
  const tTailBaseline = performance.now();
@@ -15739,6 +15526,7 @@ async function runPostTransformPhase(args) {
15739
15526
  logTransformTiming(args.sessionId, "pp.tailGuard", tTailGuard);
15740
15527
  args.restoreLatestTurnOriginals?.();
15741
15528
  return {
15529
+ thinkingReleasedForFit,
15742
15530
  explicitMaterializedSuccessfully,
15743
15531
  deferredMaterializedSuccessfully,
15744
15532
  materialized,
@@ -15764,7 +15552,7 @@ function checkM0MutationDriftAndSignal(args) {
15764
15552
  const currentMaxMutationId = getMaxM0MutationId(args.db, args.sessionId) ?? 0;
15765
15553
  const cachedMaxMutationId = args.cachedM0MaxMutationId ?? 0;
15766
15554
  if (currentMaxMutationId !== cachedMaxMutationId) {
15767
- args.pendingMaterializationSessions.add(args.sessionId);
15555
+ requestPendingMaterialization(args.pendingMaterializationSessions, args.sessionId, "mutation_drift");
15768
15556
  args.historyRefreshSessions?.add(args.sessionId);
15769
15557
  sessionLog(args.sessionId, `m[0] drift watcher: mutation id changed ${cachedMaxMutationId} → ${currentMaxMutationId}; scheduling next-pass materialization`);
15770
15558
  return true;
@@ -15808,7 +15596,6 @@ class UnresolvedHistoryBoundaryError extends Error {
15808
15596
  this.name = "UnresolvedHistoryBoundaryError";
15809
15597
  }
15810
15598
  }
15811
-
15812
15599
  // src/hooks/magic-context/transform.ts
15813
15600
  var MESSAGE_TOKENS_CACHE_MAX = 100;
15814
15601
  var messageTokensBySession = new BoundedSessionMap(MESSAGE_TOKENS_CACHE_MAX);
@@ -15881,6 +15668,7 @@ function resolveTransformHostSeams(deps) {
15881
15668
  return {
15882
15669
  hostRawMessages: deps.hostRawMessages ?? readRawSessionMessages2,
15883
15670
  hostMessageReconciliationSource: deps.hostMessageReconciliationSource ?? readRawSessionMessages2,
15671
+ hostPrepareRawOrdinals: deps.hostPrepareRawOrdinals ?? prepareRawSessionOrdinals2,
15884
15672
  hostProtectedTailBoundary: deps.hostProtectedTailBoundary ?? resolveOpenCodeProtectedTailBoundary,
15885
15673
  hostModelFallback: deps.hostModelFallback ?? findLastAssistantModelFromOpenCodeDb,
15886
15674
  hostRefusalNotice: deps.hostRefusalNotice ?? sendEmergencyRefusalNotice,
@@ -15888,12 +15676,18 @@ function resolveTransformHostSeams(deps) {
15888
15676
  };
15889
15677
  }
15890
15678
  function createTransform(deps) {
15679
+ registerSystemDateRefresh(deps.historyRefreshSessions);
15891
15680
  const finalWireUsage = createFinalWireUsageTracker();
15892
15681
  const host = resolveTransformHostSeams(deps);
15893
15682
  const loadedSessions = new Set;
15683
+ const prefixHoldLogTurnIds = new Map;
15684
+ const thinkingFitRetries = new Set;
15685
+ const clearSessionLogState = (sessionId) => {
15686
+ prefixHoldLogTurnIds.delete(sessionId);
15687
+ };
15894
15688
  const unmanagedOverWindowSessions = new Set;
15895
- const deferredHistoryRefreshSessions = deps.deferredHistoryRefreshSessions ?? new Set;
15896
- const deferredMaterializationSessions = deps.deferredMaterializationSessions ?? new Set;
15689
+ const deferredHistoryRefreshSessions = deps.deferredHistoryRefreshSessions ??= new Set;
15690
+ const deferredMaterializationSessions = deps.deferredMaterializationSessions ??= new Set;
15897
15691
  const maintenanceTail = createMaintenanceTailLauncher({
15898
15692
  db: deps.db,
15899
15693
  getMaintenanceConfig: deps.maintenanceConfig,
@@ -15909,17 +15703,22 @@ function createTransform(deps) {
15909
15703
  const startTime = performance.now();
15910
15704
  const historianRun = deps.resolveHistorianRun?.();
15911
15705
  const messages = output.messages;
15706
+ const originalForThinkingFit = messages.some((message) => message.parts.some((part) => isRecord(part) && ["reasoning", "thinking", "redacted_thinking"].includes(String(part.type)))) ? structuredClone(messages) : null;
15912
15707
  const passOutcome = createPassOutcome();
15913
15708
  const tSessionId = performance.now();
15914
15709
  const sessionId = findSessionId(messages);
15915
15710
  if (!sessionId) {
15916
15711
  return;
15917
15712
  }
15713
+ deps.onMessagesPassStarted?.(sessionId, messages);
15714
+ const dateRequest = systemDateRequestForOutput(deps.historyRefreshSessions, output) ?? beginSystemDateRequest(deps.historyRefreshSessions, sessionId, "messages", systemDateTurnId(messages), systemDateRequestIdentity(output));
15715
+ bindSystemDateRequest(output, dateRequest);
15918
15716
  const temporalCandidates = deps.experimentalTemporalAwareness ? collectTemporalCandidates(messages) : undefined;
15919
15717
  const temporalReplayIds = temporalCandidates ? messages.flatMap((message) => message.info.id ? [message.info.id] : []) : undefined;
15920
15718
  logTransformTiming(sessionId, "findSessionId", tSessionId, `messages=${messages.length}`);
15921
15719
  const tLkgEntry = performance.now();
15922
15720
  const lkgInput = projectEntry(sessionId, messages);
15721
+ const recovery = deps.compactionOff ? {} : readLkgRecoveryPrefix(sessionId, lkgInput, messages, deps.db);
15923
15722
  logTransformTiming(sessionId, "lkg.entryProjection", tLkgEntry, entryReuse ? `reused=${entryReuse.reused} retained=${entryReuse.retained} retainedBytes=${entryReuse.retainedBytes}` : undefined);
15924
15723
  const resolvedSessionId = sessionId;
15925
15724
  const runNotificationParams = (sid) => {
@@ -15929,6 +15728,13 @@ function createTransform(deps) {
15929
15728
  beginLkgPass(sessionId);
15930
15729
  clearOpenCodePendingTransformDecision(sessionId);
15931
15730
  const db = deps.db;
15731
+ try {
15732
+ const tPrepare = performance.now();
15733
+ const ready = await host.hostPrepareRawOrdinals(sessionId);
15734
+ logTransformTiming(sessionId, "rawOrdinals.prepare", tPrepare, `ready=${ready}`);
15735
+ } catch (error) {
15736
+ sessionLog(sessionId, "transform: canonical ordinal warm-up threw:", error);
15737
+ }
15932
15738
  const failPass = (site, error, kind) => {
15933
15739
  passOutcome.record(site, kind);
15934
15740
  if (deps.compactionOff === true)
@@ -16070,20 +15876,30 @@ function createTransform(deps) {
16070
15876
  let restoreLatestTurnOriginals;
16071
15877
  const activeThinkingModel = findLastAssistantModel(messages) ?? deps.liveModelBySession?.get(sessionId);
16072
15878
  let activeThinkingTurn = hasActiveAnthropicThinkingTurn(messages, activeThinkingModel?.providerID, activeThinkingModel?.modelID);
16073
- let freezeM0M1 = activeThinkingTurn && isPrefixBoundThinkingModel(activeThinkingModel?.providerID, activeThinkingModel?.modelID) || sessionDirectoryFellBack && sessionMeta.cachedM0Bytes != null && sessionMeta.cachedM1Bytes != null && (() => {
15879
+ const unresolvedDirectoryHold = sessionDirectoryFellBack && sessionMeta.cachedM0Bytes != null && sessionMeta.cachedM1Bytes != null && (() => {
16074
15880
  try {
16075
15881
  return hasRecordedSessionProjectIdentity(db, sessionId);
16076
15882
  } catch {
16077
15883
  return true;
16078
15884
  }
16079
15885
  })();
16080
- if (freezeM0M1) {
16081
- sessionLog(sessionId, "session directory unresolved; replaying the frozen m[0]/m[1] and deferring any rebuild");
15886
+ let activeThinkingPrefixHold = activeThinkingTurn && isPrefixBoundThinkingModel(activeThinkingModel?.providerID, activeThinkingModel?.modelID);
15887
+ let freezeM0M1 = unresolvedDirectoryHold || activeThinkingPrefixHold;
15888
+ if (freezeM0M1 && prefixHoldLogTurnIds.get(sessionId) !== currentTurnId) {
15889
+ prefixHoldLogTurnIds.set(sessionId, currentTurnId);
15890
+ const reason = activeThinkingPrefixHold ? "active thinking turn (prefix-bound model)" : "session directory unresolved";
15891
+ sessionLog(sessionId, `m[0]/m[1] frozen: ${reason}; replaying the frozen prefix and deferring any rebuild`);
16082
15892
  }
16083
15893
  const compartmentDirectory = sessionDirectory;
16084
15894
  const historianRunnable = deps.historianRunnable !== false;
16085
15895
  const canRunCompartments = fullFeatureMode && !compactionOff && historianRunnable && (deps.client !== undefined || deps.hiddenCompletionExecutor !== undefined) && compartmentDirectory.length > 0;
16086
15896
  const fallbackModelId = deps.getFallbackModelId?.(sessionId);
15897
+ if (fullFeatureMode && !compactionOff && historianRunnable) {
15898
+ const tOwners = performance.now();
15899
+ await waitForTagOwners(db, sessionId, sessionDecisionCalibration(db, sessionId).proseRatio);
15900
+ sessionMeta = getOrCreateSessionMeta(db, sessionId);
15901
+ logTransformTiming(sessionId, "tagOwners.prepare", tOwners);
15902
+ }
16087
15903
  const tModelDetect = performance.now();
16088
15904
  const persistedUsageBeforeResets = contextUsagePassSnapshot(sessionMeta).persistedUsage;
16089
15905
  if (deps.liveModelBySession) {
@@ -16201,7 +16017,13 @@ function createTransform(deps) {
16201
16017
  }
16202
16018
  const resolvedProviderID = modelForBudget?.providerID;
16203
16019
  activeThinkingTurn ||= hasActiveAnthropicThinkingTurn(messages, resolvedProviderID, modelForBudget?.modelID);
16204
- freezeM0M1 ||= activeThinkingTurn && isPrefixBoundThinkingModel(resolvedProviderID, modelForBudget?.modelID);
16020
+ activeThinkingPrefixHold ||= activeThinkingTurn && isPrefixBoundThinkingModel(resolvedProviderID, modelForBudget?.modelID);
16021
+ freezeM0M1 ||= activeThinkingPrefixHold;
16022
+ if (freezeM0M1 && prefixHoldLogTurnIds.get(sessionId) !== currentTurnId) {
16023
+ prefixHoldLogTurnIds.set(sessionId, currentTurnId);
16024
+ const reason = activeThinkingPrefixHold ? "active thinking turn (prefix-bound model)" : "session directory unresolved";
16025
+ sessionLog(sessionId, `m[0]/m[1] frozen: ${reason}; replaying the frozen prefix and deferring any rebuild`);
16026
+ }
16205
16027
  const protectedThinkingMessagesForPass = activeThinkingTurn || isAnthropicFamilyRoute(resolvedProviderID, modelForBudget?.modelID) ? latestAssistantTurnMessages(messages) : new Set;
16206
16028
  const canUseEmptySentinels = modelAcceptsEmptyContent(resolvedProviderID);
16207
16029
  const resolvedContextLimit = modelForBudget ? resolveTrustedContextLimit(modelForBudget.providerID, modelForBudget.modelID, {
@@ -16267,7 +16089,7 @@ function createTransform(deps) {
16267
16089
  const schedulerDeferReason = schedulerDecision === "defer" ? "scheduler_defer" : null;
16268
16090
  const historyRefreshExplicitBeforePrepare = !freezeM0M1 && deps.historyRefreshSessions.has(sessionId);
16269
16091
  const deferredHistoryWasPendingAtPassStart = !freezeM0M1 && deferredHistoryRefreshSessions.has(sessionId);
16270
- const prefixTrimSourceOrder = deferredHistoryWasPendingAtPassStart ? capturePrefixTrimSourceOrder(messages) : undefined;
16092
+ const prefixTrimSourceOrder = capturePrefixTrimSourceOrder(messages);
16271
16093
  const earlyActiveRunBlocksMaterialization = (getActiveCompartmentRun(sessionId) !== undefined || sessionMeta.compartmentInProgress) && contextUsageEarly.percentage < forceMaterializationPercentage;
16272
16094
  const canConsumeDeferredEarly = canConsumeDeferredOnThisPass({
16273
16095
  schedulerDecision,
@@ -16411,8 +16233,11 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16411
16233
  if (!compactionOff && taggerFloor === 0 && messages.length > 0) {
16412
16234
  sessionLog(sessionId, `tag floor: 0 (full-scan fallback) — no leading wire message resolved a tag across ${messages.length} msgs`);
16413
16235
  }
16236
+ const ownerRatio = sessionDecisionCalibration(db, sessionId).proseRatio;
16237
+ prewarmTagOwners(db, sessionId, ownerRatio);
16238
+ const tagOwnersReady = isTagOwnerSummaryReady(db, sessionId, ownerRatio);
16414
16239
  let triggerBoundarySnapshot;
16415
- if (fullFeatureMode && !compactionOff && historianRunnable && !sessionMeta.compartmentInProgress) {
16240
+ if (fullFeatureMode && !compactionOff && historianRunnable && tagOwnersReady && !sessionMeta.compartmentInProgress) {
16416
16241
  const tTrigger = performance.now();
16417
16242
  try {
16418
16243
  const inMemoryTail = buildTriggerInMemoryTail(db, sessionId, extractInMemoryMessageViews(messages));
@@ -16474,6 +16299,26 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16474
16299
  logTransformTiming(sessionId, "injectTemporalMarkers", tTemporal);
16475
16300
  }
16476
16301
  let taggingSucceeded = false;
16302
+ let recoveryBatch = null;
16303
+ let recoveryNormalized = false;
16304
+ const normalizeRecoveryTail = recovery.prefix ? () => {
16305
+ if (recoveryNormalized)
16306
+ return;
16307
+ const tail = messages.filter((message) => recovery.prefix?.untaggedInputIds.has(message.info.id ?? ""));
16308
+ const tagged = tagMessages(sessionId, tail, deps.tagger, db, {
16309
+ skipPrefixInjection: !ctxReduceCallable,
16310
+ servedMessages: messages,
16311
+ normalizeRestoredAssistantText: deps.normalizeRestoredAssistantText
16312
+ });
16313
+ for (const [number, target] of tagged.targets)
16314
+ targets.set(number, target);
16315
+ for (const [message, parts] of tagged.reasoningByMessage)
16316
+ reasoningByMessage.set(message, parts);
16317
+ for (const [message, number] of tagged.messageTagNumbers)
16318
+ messageTagNumbers.set(message, number);
16319
+ recoveryBatch = tagged.batch;
16320
+ recoveryNormalized = true;
16321
+ } : undefined;
16477
16322
  if (!compactionOff) {
16478
16323
  try {
16479
16324
  const t0 = performance.now();
@@ -16484,18 +16329,24 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16484
16329
  const messagesForTagging = messagesBeforeInitialPrepare && hiddenMessagesAtCompactionSeam.length > 0 ? messagesBeforeInitialPrepare : messages;
16485
16330
  const canAdoptScopedToolSweep = isCacheBusting || canConsumeDeferredEarly;
16486
16331
  const scopedToolSweep = useScopedToolSweep(db, sessionId, canAdoptScopedToolSweep) ? true : createPreAdoptionToolSweepResolver(db, sessionId);
16487
- const result = tagMessages(sessionId, messagesForTagging, deps.tagger, db, {
16332
+ const result = tagMessages(sessionId, recovery.prefix ? messagesForTagging.filter((message) => !recovery.prefix?.untaggedInputIds.has(message.info.id ?? "")) : messagesForTagging, deps.tagger, db, {
16488
16333
  skipPrefixInjection,
16489
16334
  scopedToolSweep,
16490
16335
  servedMessages: messages,
16491
- protectedThinkingMessages: protectedThinkingMessagesForPass
16336
+ protectedThinkingMessages: protectedThinkingMessagesForPass,
16337
+ normalizeRestoredAssistantText: deps.normalizeRestoredAssistantText
16492
16338
  });
16493
16339
  targets = result.targets;
16494
16340
  if (thinkingRecovery.restore)
16495
16341
  restoreLatestTurnOriginals = captureLatestTurnOriginals(messages);
16496
16342
  reasoningByMessage = result.reasoningByMessage;
16497
16343
  messageTagNumbers = result.messageTagNumbers;
16498
- batch = result.batch;
16344
+ batch = {
16345
+ finalize: () => {
16346
+ result.batch?.finalize();
16347
+ recoveryBatch?.finalize();
16348
+ }
16349
+ };
16499
16350
  if (resolvedProviderID && !isAnthropicFamilyRoute(resolvedProviderID, modelForBudget?.modelID)) {
16500
16351
  for (const target of targets.values()) {
16501
16352
  if (target.requiresToolArcSkeleton)
@@ -16614,7 +16465,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16614
16465
  sessionMeta = { ...sessionMeta, compartmentInProgress };
16615
16466
  logTransformTiming(sessionId, "compartmentPhase", tCompartmentPhase);
16616
16467
  if (pendingCompartmentInjection?.needsFreshMaterialization) {
16617
- deps.pendingMaterializationSessions.add(sessionId);
16468
+ requestPendingMaterialization(deps.pendingMaterializationSessions, sessionId, "boundary_repair");
16618
16469
  deferredMaterializationSessions.add(sessionId);
16619
16470
  }
16620
16471
  const hardModel = deps.liveModelBySession?.get(sessionId);
@@ -16708,6 +16559,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16708
16559
  const protectedCutoff = protectionWindow.cutoff;
16709
16560
  const tPostProcess = performance.now();
16710
16561
  const postTransformResult = await runPostTransformPhase({
16562
+ normalizeRecoveryTail,
16711
16563
  compactionMarkerStrategy: deps.compactionMarkerStrategy ?? defaultCompactionMarkerStrategy,
16712
16564
  sessionId,
16713
16565
  db,
@@ -16726,7 +16578,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16726
16578
  client: deps.client,
16727
16579
  activeAgent,
16728
16580
  batch,
16729
- contextUsage,
16581
+ contextUsage: thinkingFitRetries.has(sessionId) ? { ...contextUsage, percentage: Math.max(95, contextUsage.percentage) } : contextUsage,
16730
16582
  usableWindow: resolvedContextLimit ?? 0,
16731
16583
  schedulerDecision,
16732
16584
  schedulerDeferReason,
@@ -16738,6 +16590,17 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16738
16590
  compartmentInProgress,
16739
16591
  historyRefreshExplicitBeforePrepare,
16740
16592
  freezeM0M1,
16593
+ activeThinkingPrefixHold,
16594
+ directoryPrefixHold: unresolvedDirectoryHold,
16595
+ forceSignedRebuildForFit: thinkingFitRetries.has(sessionId) || !!recovery.rebuildReason,
16596
+ readLastServedMessages: () => {
16597
+ const slot = getInMemorySlot(sessionId) ?? loadPersistedLkgSlot(db, sessionId);
16598
+ try {
16599
+ return slot ? JSON.parse(slot.jsonPrefix) : null;
16600
+ } catch {
16601
+ return null;
16602
+ }
16603
+ },
16741
16604
  deferredHistoryWasPendingAtPassStart,
16742
16605
  compartmentInjectionRebuiltFromDb: pendingCompartmentInjection?.rebuiltFromDb === true,
16743
16606
  rebuiltHistoryFromInitialPrepare,
@@ -16789,6 +16652,15 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16789
16652
  hardSignals: m0HardSignals
16790
16653
  }
16791
16654
  });
16655
+ if (recovery.prefix && !recoveryNormalized) {
16656
+ const priorIds = new Set(recovery.prefix.inputs.map((input) => input.id));
16657
+ const newIds = new Set(lkgInput.flatMap((input) => input.id && !priorIds.has(input.id) ? [input.id] : []));
16658
+ const suffix = messages.filter((message) => message.info.id && newIds.has(message.info.id));
16659
+ messages.splice(0, messages.length, ...JSON.parse(recovery.prefix.json), ...suffix);
16660
+ }
16661
+ const recoveryReleaseReason = recovery.rebuildReason ?? (recoveryNormalized ? "recovery_tail_normalized" : undefined);
16662
+ if (recoveryReleaseReason)
16663
+ postTransformResult.bustedThisPass = true;
16792
16664
  passOutcome.markFinalized();
16793
16665
  const finalWireTail = describeFinalWireTail(messages);
16794
16666
  let finalWireEstimate;
@@ -16819,6 +16691,21 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16819
16691
  if (finalWireEstimate) {
16820
16692
  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}`);
16821
16693
  }
16694
+ if ((finalWireEstimate?.refusalGrade || finalWireEstimate?.refusalBasis === "byte-bound" && finalWireEstimate.refusalTokens !== undefined) && boundaryContextLimit > 0 && (finalWireEstimate.refusalTokens ?? finalWireEstimate.tokens) > boundaryContextLimit) {
16695
+ if (!thinkingFitRetries.has(sessionId) && activeThinkingPrefixHold && originalForThinkingFit && hasActiveAnthropicThinkingTurn(messages, resolvedProviderID, modelForBudget?.modelID)) {
16696
+ thinkingFitRetries.add(sessionId);
16697
+ requestPendingMaterialization(deps.pendingMaterializationSessions, sessionId, "fit_retry");
16698
+ messages.splice(0, messages.length, ...originalForThinkingFit);
16699
+ try {
16700
+ await transform({}, output);
16701
+ } finally {
16702
+ thinkingFitRetries.delete(sessionId);
16703
+ }
16704
+ return;
16705
+ }
16706
+ if (thinkingFitRetries.has(sessionId) || postTransformResult.thinkingReleasedForFit)
16707
+ throw contextRefusalError("The request remains over the model window after stripping signed thinking and reclaiming context");
16708
+ }
16822
16709
  if (postTransformResult.prefixTrimStatus === "refused") {
16823
16710
  const untrimmed = finalWireEstimate ?? estimateFinalWireInputTokens({
16824
16711
  messages,
@@ -16933,11 +16820,16 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16933
16820
  if (!finalWireEstimate) {
16934
16821
  sessionLog(sessionId, `transform: final-wire telemetry estimate=unavailable trusted=false conversation=unknown tools=unknown system=unknown toolDefinitions=unknown tail=${finalWireTail}`);
16935
16822
  }
16936
- if (passOutcome.captureEligible) {
16823
+ finishSystemDateRefresh(deps.historyRefreshSessions, dateRequest, postTransformResult.bustedThisPass && !activeThinkingPrefixHold || postTransformResult.thinkingReleasedForFit === true);
16824
+ if (activeThinkingPrefixHold && !postTransformResult.thinkingReleasedForFit && sessionMeta.cachedM0Bytes != null && sessionMeta.cachedM1Bytes != null) {
16825
+ recordHeldHeadForRequest(sessionId, currentLkgRequestIdentity(sessionId), (ownsHistory) => deferDeliveredSystemRefresh(deps, sessionId, ownsHistory));
16826
+ }
16827
+ if (passOutcome.captureEligible || recovery.prefix || recoveryReleaseReason) {
16937
16828
  const keys = resolveLkgModelKeys(messages);
16938
16829
  const modelKey = modelForBudget ? `${modelForBudget.providerID}/${modelForBudget.modelID}` : keys.modelKey;
16939
16830
  const providerKey = modelForBudget?.providerID ?? keys.providerKey;
16940
16831
  const captured = captureLkgSlot({
16832
+ completeOutput: !!recovery.prefix || !!recoveryReleaseReason || isPrefixBoundThinkingModel(resolvedProviderID, modelForBudget?.modelID),
16941
16833
  sessionId,
16942
16834
  input: lkgInput,
16943
16835
  output: messages,
@@ -16949,13 +16841,37 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16949
16841
  if (captured) {
16950
16842
  const capturedSlot = getInMemorySlot(sessionId);
16951
16843
  if (capturedSlot) {
16952
- withoutSqliteTransformPass(() => setImmediate(() => saveLkgSlotToDb(db, sessionId, capturedSlot)));
16844
+ const revision = lkgDirtyRevision(sessionId) ?? dirtyLkgRecoveryRevision(sessionId, capturedSlot);
16845
+ const fence = {
16846
+ ownDirtyRevision: revision,
16847
+ requiredRevision: revision && ownsLkgDirtyFence(sessionId, revision) ? revision : undefined,
16848
+ rebuild: !!recoveryReleaseReason,
16849
+ retainDirtyRevision: recovery.prefix && !recoveryReleaseReason ? revision : undefined
16850
+ };
16851
+ withoutSqliteTransformPass(() => setImmediate(() => {
16852
+ if (fence.requiredRevision) {
16853
+ const latest = getInMemorySlot(sessionId);
16854
+ if (!latest || latest.capturedAt !== capturedSlot.capturedAt || latest.jsonPrefix !== capturedSlot.jsonPrefix || latest.lastInputMessageId !== capturedSlot.lastInputMessageId || latest.modelKey !== capturedSlot.modelKey || latest.providerKey !== capturedSlot.providerKey)
16855
+ return;
16856
+ }
16857
+ try {
16858
+ saveLkgSlotToDb(db, sessionId, capturedSlot, undefined, fence);
16859
+ } catch (error) {
16860
+ sessionLog(sessionId, "Last-good deferred publication remains uncertified:", error);
16861
+ }
16862
+ }));
16953
16863
  }
16864
+ noteLkgHealthyPrefix(sessionId, lkgInput, messages, !!recoveryReleaseReason);
16954
16865
  }
16955
- if (postTransformResult.bustedThisPass && !captured) {
16956
- dropSlot(sessionId, "lkg_refresh_declined");
16866
+ if (!captured) {
16867
+ if (postTransformResult.bustedThisPass)
16868
+ dropSlot(sessionId, "lkg_refresh_declined");
16869
+ noteUncertifiedNormalLkg(sessionId, invalidateCleanLkgRecoveryFence(sessionId, () => loadPersistedLkgSlot(db, sessionId, true), lkgDirtyRevision(sessionId)));
16957
16870
  }
16958
16871
  } else if (passOutcome.degradations.length > 0) {
16872
+ if (compactionOff) {
16873
+ noteUncertifiedNormalLkg(sessionId, invalidateCleanLkgRecoveryFence(sessionId, () => loadPersistedLkgSlot(db, sessionId, true), lkgDirtyRevision(sessionId)));
16874
+ }
16959
16875
  sessionLog(sessionId, `lkg_capture_declined degradations=${passOutcome.degradations.map((item) => item.site).join(",")}`);
16960
16876
  }
16961
16877
  if (postTransformResult.bustedThisPass || hardCacheExpired) {
@@ -16974,7 +16890,8 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
16974
16890
  droppedTokens: postTransformResult.droppedTokens,
16975
16891
  droppedCount: postTransformResult.droppedCount,
16976
16892
  inputTokens: contextUsage.inputTokens,
16977
- bustedThisPass: true
16893
+ bustedThisPass: true,
16894
+ lkgFrozenReleaseReason: recoveryReleaseReason
16978
16895
  });
16979
16896
  }
16980
16897
  logTransformTiming(sessionId, "postTransformPhase", tPostProcess);
@@ -17093,7 +17010,11 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
17093
17010
  sessionLog(sessionId, "memory maintenance launch probe failed (ignored):", error);
17094
17011
  }
17095
17012
  };
17096
- return transform;
17013
+ return Object.assign(transform, {
17014
+ clearSessionLogState(sessionId) {
17015
+ clearSessionLogState(sessionId);
17016
+ }
17017
+ });
17097
17018
  }
17098
17019
  var HIDDEN_CHILD_AGENT_IDS = new Set([
17099
17020
  HISTORIAN_AGENT,
@@ -18339,7 +18260,7 @@ function registerRpcHandlers(rpcServer, args) {
18339
18260
  return detail;
18340
18261
  });
18341
18262
  const buildManagedCtx = async (db) => {
18342
- const { deriveHistorianChunkTokens, resolveHistorianContextLimit } = await import("./derive-budgets-rjjz5f7y.js");
18263
+ const { deriveHistorianChunkTokens, resolveHistorianContextLimit } = await import("./derive-budgets-s1p37t2g.js");
18343
18264
  const { resolveHistorianModel } = await import("./model-resolution-wazv26n0.js");
18344
18265
  const DEFAULT_HISTORIAN_TIMEOUT_MS = 600000;
18345
18266
  const runConfig = historianRunConfig(config, currentPluginConfigReader(directory)?.poll().effective ?? config);
@@ -18369,8 +18290,8 @@ function registerRpcHandlers(rpcServer, args) {
18369
18290
  const db = readDatabase();
18370
18291
  if (!db)
18371
18292
  return { ok: false, error: "db unavailable" };
18372
- const { runManagedRecomp } = await import("./recomp-orchestrator-frmwj9hz.js");
18373
- const { sendIgnoredMessage } = await import("./send-session-notification-rnc4nrsr.js");
18293
+ const { runManagedRecomp } = await import("./recomp-orchestrator-wrjy3qym.js");
18294
+ const { sendIgnoredMessage } = await import("./send-session-notification-cz6ev088.js");
18374
18295
  log(`[rpc] recomp requested for session ${sessionId}`);
18375
18296
  if (!args.client && !args.hiddenCompletionExecutor) {
18376
18297
  return {
@@ -18405,7 +18326,7 @@ function registerRpcHandlers(rpcServer, args) {
18405
18326
  const message = executeFlush(db, sessionId);
18406
18327
  liveSessionState.historyRefreshSessions.add(sessionId);
18407
18328
  liveSessionState.systemPromptRefreshSessions.add(sessionId);
18408
- liveSessionState.pendingMaterializationSessions.add(sessionId);
18329
+ requestPendingMaterialization(liveSessionState.pendingMaterializationSessions, sessionId, "user_flush");
18409
18330
  return { ok: true, message };
18410
18331
  });
18411
18332
  rpcServer.handle("wrapup", async (params) => {
@@ -18419,7 +18340,7 @@ function registerRpcHandlers(rpcServer, args) {
18419
18340
  const db = readDatabase();
18420
18341
  if (!db)
18421
18342
  return { ok: false, error: "db unavailable" };
18422
- const { runManagedWrapup } = await import("./wrapup-orchestrator-yknks3ad.js");
18343
+ const { runManagedWrapup } = await import("./wrapup-orchestrator-bmybc5p5.js");
18423
18344
  const model = liveSessionState.liveModelBySession.get(sessionId);
18424
18345
  const contextLimit = model ? resolveContextLimit(model.providerID, model.modelID, { db, sessionID: sessionId }) : 128000;
18425
18346
  const ctx = {
@@ -19232,8 +19153,8 @@ var v2CompactionMarkerStrategy = {
19232
19153
  import { randomUUID as randomUUID2 } from "node:crypto";
19233
19154
 
19234
19155
  // src/v2/fold/owner.ts
19235
- import { createHash as createHash6 } from "node:crypto";
19236
- var foldDigest = (value) => createHash6("sha256").update(value).digest("hex");
19156
+ import { createHash as createHash4 } from "node:crypto";
19157
+ var foldDigest = (value) => createHash4("sha256").update(value).digest("hex");
19237
19158
 
19238
19159
  class FoldOwner {
19239
19160
  storage;
@@ -19540,6 +19461,11 @@ function toolStateContent(state) {
19540
19461
  `);
19541
19462
  }
19542
19463
 
19464
+ // src/v2/hooks/copied-tag-notation.ts
19465
+ function stripCopiedTagNotation(text) {
19466
+ return text.includes("§") ? stripPersistedAssistantText(text) : text;
19467
+ }
19468
+
19543
19469
  // src/v2/hooks/payload.ts
19544
19470
  var HEAD_IDS = ["__magic_context_v2_m0__", "__magic_context_v2_m1__"];
19545
19471
  var HOST_CONTENT_TYPES = new Set([
@@ -19559,6 +19485,35 @@ function rejectContentPart(message, part, reason) {
19559
19485
  else
19560
19486
  log(text);
19561
19487
  }
19488
+ var emptyTextOmissionLogged = new Set;
19489
+ function placeCacheHint(content, hint) {
19490
+ if (content.length === 0 || content.some((part) => part.cache !== undefined))
19491
+ return;
19492
+ let lastText = content.length - 1;
19493
+ while (lastText >= 0 && content[lastText]?.type !== "text")
19494
+ lastText -= 1;
19495
+ const index = lastText >= 0 ? lastText : content.length - 1;
19496
+ content[index] = { ...content[index], cache: hint };
19497
+ }
19498
+ function removeEmptyTextParts(content, rendered, mayRemove) {
19499
+ let hint;
19500
+ let removed = 0;
19501
+ for (let index = content.length - 1;index >= 0; index -= 1) {
19502
+ const part = content[index];
19503
+ if (part?.type !== "text" || part.text !== "" || !mayRemove(part))
19504
+ continue;
19505
+ if (hint === undefined)
19506
+ hint = part.cache;
19507
+ content.splice(index, 1);
19508
+ removed += 1;
19509
+ }
19510
+ if (hint !== undefined) {
19511
+ const target = content.length > 0 ? content : rendered.at(-1)?.content;
19512
+ if (target)
19513
+ placeCacheHint(target, hint);
19514
+ }
19515
+ return removed;
19516
+ }
19562
19517
  function isHostInstance(value) {
19563
19518
  if (!value || typeof value !== "object" || Array.isArray(value))
19564
19519
  return false;
@@ -19621,12 +19576,14 @@ function contentValueAttachments(value) {
19621
19576
  ...typeof item.name === "string" ? { filename: item.name } : {}
19622
19577
  }));
19623
19578
  }
19624
- function rebuildFileContent(value, output) {
19579
+ function rebuildFileContent(value, output, attachments) {
19625
19580
  if (isDroppedToolOutput(output))
19626
19581
  return { type: "text", value: output };
19627
19582
  const rebuilt = [];
19628
19583
  let placed = false;
19629
19584
  for (const item of value) {
19585
+ if (item?.type === "file" && Array.isArray(attachments) && !attachments.some((attachment) => attachment?.url === item.uri && attachment?.mime === item.mime))
19586
+ continue;
19630
19587
  if (item?.type !== "text") {
19631
19588
  rebuilt.push(item);
19632
19589
  continue;
@@ -19727,6 +19684,8 @@ function adaptPayload(draft, admittedIDs = new Set) {
19727
19684
  if (paired.has(content))
19728
19685
  continue;
19729
19686
  const part = callParts.get(content) ?? (content.type === "tool-result" ? nativeTool(undefined, { part: content, message }) : clonePreservingInstances(content));
19687
+ if (message.role === "assistant" && part.type === "text" && typeof part.text === "string")
19688
+ part.text = stripCopiedTagNotation(part.text);
19730
19689
  if (message.id && admittedIDs.has(message.id))
19731
19690
  part.synthetic = true;
19732
19691
  parts.push(part);
@@ -19766,12 +19725,17 @@ function adaptPayload(draft, admittedIDs = new Set) {
19766
19725
  commit() {
19767
19726
  let head = 0;
19768
19727
  const rendered = [];
19728
+ const emptyTextGuard = modelAcceptsEmptyContent(draft.model.providerID);
19729
+ const imageOmissions = getEmptyImageParts(messages);
19730
+ let omitted = 0;
19731
+ let emptyTextParts = 0;
19769
19732
  for (const message of messages) {
19770
19733
  const original = originals.get(message) ?? (typeof message.info.id === "string" ? originalsByID.get(message.info.id) : undefined);
19771
19734
  const id = message.info.syntheticHead ? HEAD_IDS[head++] : original?.id;
19772
19735
  const content = [];
19736
+ const emptyImages = new Set;
19773
19737
  const following = [];
19774
- for (const candidate of message.parts) {
19738
+ for (const [partIndex, candidate] of message.parts.entries()) {
19775
19739
  const part = candidate;
19776
19740
  const bridge = bridges.get(part) ?? (part.type === "tool" && typeof part.callID === "string" ? bridgesFor(message)?.get(part.callID) : undefined);
19777
19741
  if (!bridge) {
@@ -19827,7 +19791,10 @@ function adaptPayload(draft, admittedIDs = new Set) {
19827
19791
  }
19828
19792
  const { synthetic: _synthetic, ...clean } = part;
19829
19793
  const original = hostPartTypes.has(clean.type) && !containsHostInstance(clean) ? hostParts.get(contentKey(clean)) : undefined;
19830
- content.push(original ?? clean);
19794
+ const projected = original ?? clean;
19795
+ if (imageOmissions.get(message.info.id ?? "")?.has(partIndex))
19796
+ emptyImages.add(projected);
19797
+ content.push(projected);
19831
19798
  continue;
19832
19799
  }
19833
19800
  const state = part.state;
@@ -19859,7 +19826,7 @@ function adaptPayload(draft, admittedIDs = new Set) {
19859
19826
  if (bridge.result && bridge.resultMessage) {
19860
19827
  const result = {
19861
19828
  ...bridge.result,
19862
- result: state.output === bridge.output ? bridge.result.result : bridge.files && typeof state.output === "string" ? rebuildFileContent(bridge.files, state.output) : {
19829
+ result: state.output === bridge.output ? bridge.result.result : bridge.files && typeof state.output === "string" ? rebuildFileContent(bridge.files, state.output, state.attachments) : {
19863
19830
  type: state.status === "error" ? "error" : "text",
19864
19831
  value: state.output
19865
19832
  }
@@ -19870,7 +19837,8 @@ function adaptPayload(draft, admittedIDs = new Set) {
19870
19837
  following.push({ ...bridge.resultMessage, content: [result] });
19871
19838
  }
19872
19839
  }
19873
- if (content.length || !original?.content.length) {
19840
+ const guarded = removeEmptyTextParts(content, rendered, (part) => emptyTextGuard || emptyImages.has(part));
19841
+ if (content.length || !original?.content.length && guarded === 0) {
19874
19842
  const value = {
19875
19843
  ...original,
19876
19844
  id,
@@ -19879,9 +19847,16 @@ function adaptPayload(draft, admittedIDs = new Set) {
19879
19847
  };
19880
19848
  const existing = id ? existingHeads.get(id) : undefined;
19881
19849
  rendered.push(existing ? Object.assign(existing, { content }) : value);
19850
+ } else if (emptyTextGuard || guarded > 0) {
19851
+ omitted += 1;
19882
19852
  }
19853
+ emptyTextParts += guarded;
19883
19854
  rendered.push(...following);
19884
19855
  }
19856
+ if (omitted + emptyTextParts > 0 && !emptyTextOmissionLogged.has(draft.sessionID)) {
19857
+ emptyTextOmissionLogged.add(draft.sessionID);
19858
+ sessionLog(draft.sessionID, `v2 payload: left out ${omitted} emptied messages and ${emptyTextParts} empty text parts, ${emptyTextGuard ? "because Anthropic rejects an empty text block" : "to replay frozen image omissions"} (logged once per session)`);
19859
+ }
19885
19860
  draft.messages.splice(0, draft.messages.length, ...rendered);
19886
19861
  }
19887
19862
  };
@@ -21545,13 +21520,28 @@ class DeletedSessionTombstones {
21545
21520
  }
21546
21521
 
21547
21522
  // src/v2/hooks/generate.ts
21523
+ function generateReplayAnchor(messages) {
21524
+ let index = messages.length - 1;
21525
+ while (index >= 0 && !messages[index]?.id)
21526
+ index--;
21527
+ if (index < 0)
21528
+ return;
21529
+ const id = messages.at(index)?.id;
21530
+ if (!id)
21531
+ return;
21532
+ return { id, tail: clonePreservingInstances(messages.slice(index + 1)) };
21533
+ }
21534
+
21548
21535
  class V2GenerateReplay {
21549
21536
  prefixes = new Map;
21550
21537
  capture(draft, anchor) {
21551
- if (!anchor)
21538
+ if (!anchor) {
21539
+ this.prefixes.delete(draft.sessionID);
21540
+ sessionLog(draft.sessionID, "generate replay capture skipped: no-anchor");
21552
21541
  return;
21542
+ }
21553
21543
  this.prefixes.set(draft.sessionID, {
21554
- anchor,
21544
+ anchor: clonePreservingInstances(anchor),
21555
21545
  model: `${draft.model.providerID}/${draft.model.id}`,
21556
21546
  agent: draft.agent,
21557
21547
  system: clonePreservingInstances(draft.system),
@@ -21564,12 +21554,23 @@ class V2GenerateReplay {
21564
21554
  }
21565
21555
  apply(draft) {
21566
21556
  const prefix = this.prefixes.get(draft.sessionID);
21567
- if (!prefix || prefix.model !== `${draft.model.providerID}/${draft.model.id}` || prefix.agent !== draft.agent)
21557
+ const skip = (reason) => {
21558
+ sessionLog(draft.sessionID, `generate replay apply skipped: ${reason}`);
21568
21559
  return false;
21569
- const index = draft.messages.findIndex((message) => message.id === prefix.anchor);
21560
+ };
21561
+ if (!prefix)
21562
+ return skip("no-prefix");
21563
+ if (prefix.model !== `${draft.model.providerID}/${draft.model.id}`)
21564
+ return skip("model-mismatch");
21565
+ if (prefix.agent !== draft.agent)
21566
+ return skip("agent-mismatch");
21567
+ const index = draft.messages.findIndex((message) => message.id === prefix.anchor.id);
21570
21568
  if (index < 0)
21571
- return false;
21572
- const tail = draft.messages.slice(index + 1);
21569
+ return skip("anchor-not-found");
21570
+ const end = index + 1 + prefix.anchor.tail.length;
21571
+ if (JSON.stringify(draft.messages.slice(index + 1, end)) !== JSON.stringify(prefix.anchor.tail))
21572
+ return skip("anchor-tail-mismatch");
21573
+ const tail = draft.messages.slice(end);
21573
21574
  draft.messages.splice(0, draft.messages.length, ...clonePreservingInstances(prefix.messages), ...tail);
21574
21575
  draft.system.splice(0, draft.system.length, ...clonePreservingInstances(prefix.system));
21575
21576
  draft.tools = clonePreservingInstances(prefix.tools);
@@ -21587,9 +21588,9 @@ function hiddenTerminalError(event) {
21587
21588
  }
21588
21589
 
21589
21590
  // src/v2/hooks/lkg-system.ts
21590
- import { createHash as createHash7 } from "node:crypto";
21591
- function identity2(slot) {
21592
- return createHash7("sha256").update(JSON.stringify({
21591
+ import { createHash as createHash5 } from "node:crypto";
21592
+ function identity(slot) {
21593
+ return createHash5("sha256").update(JSON.stringify({
21593
21594
  capturedAt: slot.capturedAt,
21594
21595
  captureSequence: slot.captureSequence,
21595
21596
  rowVersion: slot.rowVersion,
@@ -21604,14 +21605,14 @@ class V2LkgSystemReplay {
21604
21605
  systems = new BoundedSessionMap(1000);
21605
21606
  capture(sessionId, slot, input, served) {
21606
21607
  this.systems.set(sessionId, {
21607
- slot: identity2(slot),
21608
+ slot: identity(slot),
21608
21609
  input: JSON.stringify(input),
21609
21610
  served: structuredClone(served)
21610
21611
  });
21611
21612
  }
21612
21613
  restore(sessionId, slot, input, target) {
21613
21614
  const saved = this.systems.get(sessionId);
21614
- if (!slot || !saved || saved.slot !== identity2(slot) || saved.input !== JSON.stringify(input))
21615
+ if (!slot || !saved || saved.slot !== identity(slot) || saved.input !== JSON.stringify(input))
21615
21616
  return false;
21616
21617
  target.splice(0, target.length, ...structuredClone(saved.served));
21617
21618
  return true;
@@ -22389,6 +22390,24 @@ function createV2RawMessageProvider(reader, sessionID) {
22389
22390
  };
22390
22391
  }
22391
22392
 
22393
+ // src/v2/hooks/system-prompt-response.ts
22394
+ function bindV2SystemPromptResponse(sessionId, reader) {
22395
+ const request = currentLkgRequestIdentity(sessionId);
22396
+ if (request?.kind !== "v2" || request.responseAfterSequence === undefined)
22397
+ return;
22398
+ const first = reader.assistantSince(sessionId, request.responseAfterSequence)[0];
22399
+ if (!first)
22400
+ return;
22401
+ const createdAt = first.data.time?.created ?? first.time_created;
22402
+ if (typeof createdAt !== "number")
22403
+ return;
22404
+ bindSystemPromptResponse(sessionId, request, {
22405
+ responseId: first.id,
22406
+ createdAt,
22407
+ modelKey: first.data.model?.providerID && first.data.model?.id ? `${first.data.model.providerID}/${first.data.model.id}` : undefined
22408
+ });
22409
+ }
22410
+
22392
22411
  // src/v2/hooks/tools.ts
22393
22412
  import { tool as tool6 } from "@opencode-ai/plugin";
22394
22413
 
@@ -22534,7 +22553,7 @@ function resolveCtxExpandMode(args, domain) {
22534
22553
  }
22535
22554
 
22536
22555
  // src/tools/ctx-expand/render.ts
22537
- import { createHash as createHash8 } from "node:crypto";
22556
+ import { createHash as createHash6 } from "node:crypto";
22538
22557
  function isRecord4(value) {
22539
22558
  return value !== null && typeof value === "object" && !Array.isArray(value);
22540
22559
  }
@@ -22758,7 +22777,7 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
22758
22777
  const index = scoped ? Number(scoped[2]) : 0;
22759
22778
  const piText = message.parts.filter((part) => isRecord4(part) && part.type === "text");
22760
22779
  const part = message.parts[index];
22761
- const matching = derived ? piText.filter((part) => isRecord4(part) && typeof part.text === "string" && createHash8("sha256").update(part.text).digest("hex") === derived[3]) : [];
22780
+ const matching = derived ? piText.filter((part) => isRecord4(part) && typeof part.text === "string" && createHash6("sha256").update(part.text).digest("hex") === derived[3]) : [];
22762
22781
  const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" ? piText[index] : part;
22763
22782
  if (!selected)
22764
22783
  return `Tag ${number}'s original text part is no longer in stored history.`;
@@ -22766,6 +22785,56 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
22766
22785
  return renderedPart !== null ? renderedPart : `Tag ${number}'s stored text part has no renderable content (empty or structural-only); the requested content was not recovered.`;
22767
22786
  }
22768
22787
 
22788
+ // src/tools/ctx-expand/search-archive.ts
22789
+ function renderSearchArchive(db, sessionId, start, end, tokenBudget, archiveOnly = false) {
22790
+ return withMessageFtsSessionFilter(db, sessionId, (sessionFirst) => {
22791
+ let rawIds;
22792
+ const rows = db.prepare(sessionFirst ? `SELECT f.message_ordinal AS ordinal, f.message_id AS id, f.role, f.content
22793
+ FROM message_fts_rowid_map AS m
22794
+ JOIN message_history_fts AS f ON f.rowid = m.fts_rowid
22795
+ WHERE m.session_id = ?1 AND m.message_ordinal BETWEEN ?2 AND ?3
22796
+ AND f.session_id = ?1 AND f.message_ordinal BETWEEN ?2 AND ?3
22797
+ ORDER BY m.message_ordinal` : `SELECT message_ordinal AS ordinal, message_id AS id, role, content
22798
+ FROM message_history_fts
22799
+ WHERE session_id = ?1 AND message_ordinal BETWEEN ?2 AND ?3
22800
+ ORDER BY CAST(message_ordinal AS INTEGER)`);
22801
+ const blocks = [];
22802
+ let tokens = 0;
22803
+ let lastOrdinal = start - 1;
22804
+ let truncated = false;
22805
+ for (const row of rows.iterate(sessionId, start, end)) {
22806
+ if (!row.content)
22807
+ continue;
22808
+ if (!archiveOnly) {
22809
+ rawIds ??= readRawSessionMessageIdOrdinalsForRange2(sessionId, start, end);
22810
+ if (rawIds.get(row.id) === row.ordinal)
22811
+ continue;
22812
+ }
22813
+ const block = `[${row.ordinal}] ${row.role === "user" ? "U" : "A"}: ${row.content}`;
22814
+ const cost = estimateTokens(block);
22815
+ if (blocks.length > 0 && tokens + cost > tokenBudget) {
22816
+ truncated = true;
22817
+ break;
22818
+ }
22819
+ blocks.push(block);
22820
+ tokens += cost;
22821
+ lastOrdinal = row.ordinal;
22822
+ }
22823
+ if (blocks.length === 0)
22824
+ return null;
22825
+ const text = [
22826
+ "From the search archive: indexed text only; no surrounding messages or tool output.",
22827
+ "",
22828
+ ...blocks
22829
+ ];
22830
+ if (truncated) {
22831
+ text.push("", `Truncated at message ${lastOrdinal} (budget: ~${tokenBudget} tokens). Call mmc_expand(start=${lastOrdinal + 1}, end=${end}) for more archived text.`);
22832
+ }
22833
+ return text.join(`
22834
+ `);
22835
+ });
22836
+ }
22837
+
22769
22838
  // src/tools/ctx-expand/tools.ts
22770
22839
  var ctxExpandArgsShape = {
22771
22840
  tag: tool.schema.union([tool.schema.number(), tool.schema.string()]).optional().describe("Tag number from a §N§ tag or a [dropped §N§] placeholder, not a message ordinal. Returns that one item in full. Use alone."),
@@ -22789,6 +22858,8 @@ function createCtxExpandTool(deps) {
22789
22858
  message: "number"
22790
22859
  });
22791
22860
  const sessionId = toolContext.sessionID;
22861
+ if (!await prepareRawSessionOrdinals2(sessionId))
22862
+ return "Session ordinals are warming in the background. Retry mmc_expand shortly; no approximate ordinal was served.";
22792
22863
  try {
22793
22864
  const mode = resolveCtxExpandMode(args, "positive");
22794
22865
  if (mode.kind === "error") {
@@ -22798,7 +22869,7 @@ function createCtxExpandTool(deps) {
22798
22869
  return renderItemByTag(deps.db, sessionId, mode.tag);
22799
22870
  }
22800
22871
  if (mode.kind === "message") {
22801
- return renderMessageByOrdinal(sessionId, mode.message);
22872
+ return renderSearchArchive(deps.db, sessionId, mode.message, mode.message, CTX_EXPAND_TOKEN_BUDGET) ?? renderMessageByOrdinal(sessionId, mode.message);
22802
22873
  }
22803
22874
  const { start, end, verbose } = mode;
22804
22875
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
@@ -22806,6 +22877,9 @@ function createCtxExpandTool(deps) {
22806
22877
  return `Range ${start}-${end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`;
22807
22878
  }
22808
22879
  const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(end, lastCompartmentEnd) : end;
22880
+ const archived = renderSearchArchive(deps.db, sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
22881
+ if (archived)
22882
+ return archived;
22809
22883
  if (verbose) {
22810
22884
  const v = renderVerboseRange(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET, deps.expandTools);
22811
22885
  if (!v.text) {
@@ -23596,9 +23670,15 @@ Stamp: file reads, search results and tool outputs the work ahead no longer need
23596
23670
  Keep: user messages (never stamp one for its directive), your own conversation text, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may still matter.
23597
23671
 
23598
23672
  Look at each tag before stamping it; never blanket-stamp a range like "1-50". Many small targeted stamps beat one sweep. \`drop\` accepts "3-5", "1,2,9", "1-5,8,12-15".`;
23599
- var CTX_REDUCE_SELF_STAMP_MESSAGE_TEMPLATE = "§N§ is an mmc_reduce call; leave those alone, they are cleaned up automatically.";
23600
- function ctxReduceSelfStampMessage(tagNumber) {
23601
- return CTX_REDUCE_SELF_STAMP_MESSAGE_TEMPLATE.replace("§N§", `§${tagNumber}§`);
23673
+ function formatCtxReduceReply(stampedCount, selfStampCount = 0) {
23674
+ const parts = [];
23675
+ if (stampedCount > 0) {
23676
+ parts.push(`${stampedCount} tag${stampedCount === 1 ? "" : "s"} stamped.`);
23677
+ }
23678
+ if (selfStampCount > 0) {
23679
+ parts.push(selfStampCount === 1 ? "1 was a ctx_reduce call; leave those alone, they are cleaned up automatically." : `${selfStampCount} were ctx_reduce calls; leave those alone, they are cleaned up automatically.`);
23680
+ }
23681
+ return parts.join(" ");
23602
23682
  }
23603
23683
  // src/tools/ctx-reduce/tools.ts
23604
23684
  import { tool as tool4 } from "@opencode-ai/plugin";
@@ -23681,22 +23761,10 @@ function createCtxReduceTool(deps) {
23681
23761
  return `Error: Unknown tag(s) ${formatIds(unknownIds)}. Check available tags in conversation.`;
23682
23762
  }
23683
23763
  const ctxReduceSelfStampIds = new Set(allTags.filter((tag) => tag.toolName?.toLowerCase() === "mmc_reduce").map((tag) => tag.tagNumber));
23684
- const ctxReduceSelfStampNote = dropIds.filter((id) => ctxReduceSelfStampIds.has(id)).filter((id, index, ids) => ids.indexOf(id) === index).map(ctxReduceSelfStampMessage).join(" ");
23764
+ const ctxReduceSelfStampCount = new Set(dropIds.filter((id) => ctxReduceSelfStampIds.has(id))).size;
23685
23765
  dropIds = dropIds.filter((id) => !ctxReduceSelfStampIds.has(id));
23686
- let protectedSet;
23687
- if (deps.protectedSet) {
23688
- protectedSet = typeof deps.protectedSet === "function" ? deps.protectedSet(sessionId) : deps.protectedSet;
23689
- } else if (deps.getProtectionWindow) {
23690
- protectedSet = deps.getProtectionWindow(sessionId).protectedTagNumbers;
23691
- } else {
23692
- protectedSet = getProtectionWindowForSession(deps.db, sessionId, deps.floor).protectedTagNumbers;
23693
- }
23694
23766
  const tagStatusMap = new Map(allTags.map((tag) => [tag.tagNumber, tag.status]));
23695
- protectedSet = new Set([
23696
- ...protectedSet,
23697
- ...protectedToolTagNumbers(getActiveTagsBySession(deps.db, sessionId), deps.protectedTools)
23698
- ]);
23699
- const inertWhitespaceTagNumbers = new Set(getInertWhitespaceAssistantTags(deps.db, sessionId).map((tag) => tag.tagNumber));
23767
+ const inertWhitespaceTagNumbers = new Set(getInertWhitespaceAssistantTagsByNumbers(deps.db, sessionId, dropIds).map((tag) => tag.tagNumber));
23700
23768
  const inertDropIds = [
23701
23769
  ...new Set(dropIds.filter((id) => inertWhitespaceTagNumbers.has(id)))
23702
23770
  ];
@@ -23712,15 +23780,15 @@ function createCtxReduceTool(deps) {
23712
23780
  if (conflicts.length > 0) {
23713
23781
  return `Error: Conflicting operations — ${conflicts.join("; ")}.`;
23714
23782
  }
23715
- const preFilterDropCount = dropIds.length;
23783
+ const stampedCount = new Set(dropIds.filter((id) => !inertWhitespaceTagNumbers.has(id))).size;
23716
23784
  dropIds = dropIds.filter((id) => !inertWhitespaceTagNumbers.has(id) && tagStatusMap.get(id) !== "dropped" && pendingMap.get(id) !== "drop");
23717
- const skippedCount = preFilterDropCount - dropIds.length;
23718
23785
  if (dropIds.length === 0) {
23719
- const noActionNotes = [ctxReduceSelfStampNote, inertNote].filter(Boolean);
23720
- if (!ctxReduceSelfStampNote) {
23721
- noActionNotes.push("All requested tags were already queued or processed. No new action is needed.");
23722
- }
23723
- return noActionNotes.join(" ");
23786
+ const reply = formatCtxReduceReply(stampedCount, ctxReduceSelfStampCount);
23787
+ if (reply)
23788
+ return `${reply}${inertNote ? ` ${inertNote}` : ""}`;
23789
+ if (inertNote)
23790
+ return inertNote;
23791
+ return "All requested tags were already queued or processed. No new action is needed.";
23724
23792
  }
23725
23793
  try {
23726
23794
  deps.db.transaction(() => {
@@ -23735,25 +23803,8 @@ function createCtxReduceTool(deps) {
23735
23803
  }
23736
23804
  const currentInputTokens = deps.getSessionTokens?.(sessionId) ?? getOrCreateSessionMeta(deps.db, sessionId).lastInputTokens;
23737
23805
  updateSessionMeta(deps.db, sessionId, { lastNudgeTokens: currentInputTokens });
23738
- const immediateDropIds = dropIds.filter((id) => !protectedSet.has(id));
23739
- const deferredDropIds = [...new Set(dropIds.filter((id) => protectedSet.has(id)))];
23740
- const skippedNote = skippedCount > 0 ? ` ${skippedCount} requested tag${skippedCount === 1 ? " was" : "s were"} already queued and need no action.` : "";
23741
- let heldSentence = "";
23742
- if (deferredDropIds.length === 1) {
23743
- heldSentence = `Held: §${deferredDropIds[0]} is inside the protected working set; it applies once newer work displaces it.`;
23744
- } else if (deferredDropIds.length > 1) {
23745
- heldSentence = `Held: ${deferredDropIds.map((id) => `§${id}`).join(", ")} are inside the protected working set; they apply once newer work displaces them.`;
23746
- }
23747
- if (immediateDropIds.length > 0 && heldSentence.length > 0) {
23748
- return `Queued: drop ${formatIds(immediateDropIds)}.${skippedNote}${inertNote ? ` ${inertNote}` : ""} ${heldSentence}${ctxReduceSelfStampNote ? ` ${ctxReduceSelfStampNote}` : ""}`;
23749
- }
23750
- if (immediateDropIds.length > 0) {
23751
- return `Queued: drop ${formatIds(immediateDropIds)}.${skippedNote}${inertNote ? ` ${inertNote}` : ""}${ctxReduceSelfStampNote ? ` ${ctxReduceSelfStampNote}` : ""}`;
23752
- }
23753
- if (heldSentence.length > 0) {
23754
- return `${heldSentence}${skippedNote}${inertNote ? ` ${inertNote}` : ""}${ctxReduceSelfStampNote ? ` ${ctxReduceSelfStampNote}` : ""}`;
23755
- }
23756
- return `Queued: drop ${formatIds(dropIds)}.${skippedNote}${inertNote ? ` ${inertNote}` : ""}${ctxReduceSelfStampNote ? ` ${ctxReduceSelfStampNote}` : ""}`;
23806
+ const reply = formatCtxReduceReply(stampedCount, ctxReduceSelfStampCount);
23807
+ return `${reply}${inertNote ? ` ${inertNote}` : ""}`;
23757
23808
  }
23758
23809
  });
23759
23810
  }
@@ -24126,8 +24177,16 @@ async function applyV2SystemPrompt(systemPrompt, draft) {
24126
24177
  }
24127
24178
  })));
24128
24179
  const system = { system: draft.system.map((part) => String(part.text ?? "")) };
24180
+ bindSystemDateOutput(system.system, () => {
24181
+ draft.system.splice(0, draft.system.length, ...system.system.map((text, index) => ({
24182
+ ...draft.system[index],
24183
+ type: "text",
24184
+ text
24185
+ })));
24186
+ });
24129
24187
  await systemPrompt.handler({
24130
24188
  sessionID: draft.sessionID,
24189
+ dateRefreshRequest: draft,
24131
24190
  model: { providerID: draft.model.providerID, modelID: draft.model.id }
24132
24191
  }, system);
24133
24192
  const originals = [...draft.system];
@@ -24147,6 +24206,7 @@ function createHostSeams(context, readAllForConversion, reconciliationSource, li
24147
24206
  return {
24148
24207
  hostRawMessages: readAllForConversion,
24149
24208
  hostMessageReconciliationSource: reconciliationSource,
24209
+ hostPrepareRawOrdinals: async () => true,
24150
24210
  hostProtectedTailBoundary: (args) => resolveOpenCodeProtectedTailBoundary({
24151
24211
  ...args,
24152
24212
  cacheNamespace: `opencode2:${args.sessionId}`
@@ -24445,6 +24505,7 @@ async function registerContext(context) {
24445
24505
  const sessionDirectories = new Map;
24446
24506
  const forkSeedSettled = new Set;
24447
24507
  const historyRefreshSessions = new Set;
24508
+ registerSystemDateRefresh(historyRefreshSessions);
24448
24509
  const pendingMaterializationSessions = new Set;
24449
24510
  const lastHeuristicsTurnId = new Map;
24450
24511
  const restoredRows = new RestoredRowCache;
@@ -24529,6 +24590,11 @@ async function registerContext(context) {
24529
24590
  try {
24530
24591
  const latest = reader.latestAssistant(draft.sessionID);
24531
24592
  latestLkgResponseIds.set(draft.sessionID, latest?.id);
24593
+ try {
24594
+ bindV2SystemPromptResponse(draft.sessionID, reader);
24595
+ } catch (error) {
24596
+ sessionLog(draft.sessionID, "v2 response ownership unavailable; keeping system identity pending", error);
24597
+ }
24532
24598
  if (latest)
24533
24599
  noteLkgProviderResponse({
24534
24600
  sessionId: draft.sessionID,
@@ -24621,6 +24687,10 @@ async function registerContext(context) {
24621
24687
  nativeFoldReplay?.onEvent(event);
24622
24688
  restoredRows.forget(sessionID);
24623
24689
  }
24690
+ if (event.type?.startsWith("session.revert.") && db) {
24691
+ const database = db;
24692
+ withoutSqliteTransformPass(() => scheduleClearAndReindex(database, sessionID, pagedRead));
24693
+ }
24624
24694
  if (event.type === "session.error" || event.type === "session.execution.failed") {
24625
24695
  const error = hiddenTerminalError(event);
24626
24696
  if (error !== undefined)
@@ -24642,7 +24712,9 @@ async function registerContext(context) {
24642
24712
  continue;
24643
24713
  }
24644
24714
  if (event.type === "session.deleted") {
24715
+ clearLkgMeasuredRequest(sessionID);
24645
24716
  deletedSessions.add(sessionID);
24717
+ transform?.clearSessionLogState(sessionID);
24646
24718
  if (db) {
24647
24719
  markSessionCleanupPending(db, sessionID);
24648
24720
  clearSession(db, sessionID);
@@ -24657,7 +24729,7 @@ async function registerContext(context) {
24657
24729
  agents.delete(sessionID);
24658
24730
  channel1.delete(sessionID);
24659
24731
  historyRefreshSessions.delete(sessionID);
24660
- pendingMaterializationSessions.delete(sessionID);
24732
+ clearPendingMaterialization(pendingMaterializationSessions, sessionID);
24661
24733
  lastHeuristicsTurnId.delete(sessionID);
24662
24734
  restoredRows.forget(sessionID);
24663
24735
  await nativeFoldReplay?.forget(sessionID);
@@ -24766,6 +24838,7 @@ async function registerContext(context) {
24766
24838
  const systemAtEntry = structuredClone(draft.system);
24767
24839
  const slotAtEntry = getSlot(draft.sessionID);
24768
24840
  const restoreLkgSystem = () => {
24841
+ retirePendingSystemPromptDelivery(draft.sessionID);
24769
24842
  if (!lkgSystems.restore(draft.sessionID, getSlot(draft.sessionID), systemAtEntry, draft.system)) {
24770
24843
  sessionLog(draft.sessionID, "lkg_system_state_mismatch");
24771
24844
  throw new Error("LKG system identity is unavailable or changed");
@@ -24917,6 +24990,18 @@ async function registerContext(context) {
24917
24990
  injectionEnabled: config.system_prompt_injection.enabled,
24918
24991
  injectionSkipSignatures: config.system_prompt_injection.skip_signatures
24919
24992
  });
24993
+ let responseAfterSequence;
24994
+ try {
24995
+ const responseReader = openStoreReader();
24996
+ try {
24997
+ responseAfterSequence = responseReader.latestSequence(draft.sessionID);
24998
+ } finally {
24999
+ responseReader.close();
25000
+ }
25001
+ } catch (error) {
25002
+ sessionLog(draft.sessionID, "v2 response boundary unavailable; no system delivery acknowledgement", error);
25003
+ }
25004
+ beginV2LkgRequest(draft.sessionID, `${draft.model.providerID}/${draft.model.id}`, latestLkgResponseIds.get(draft.sessionID), responseAfterSequence);
24920
25005
  const system = await applyV2SystemPrompt(systemPrompt, draft);
24921
25006
  noteMaintenanceSystemSnapshot(draft.sessionID, system.join(`
24922
25007
  `));
@@ -24941,6 +25026,16 @@ async function registerContext(context) {
24941
25026
  await cacheV2SessionDirectory(context.session, draft.sessionID, sessionDirectories);
24942
25027
  if (!rawProviders.has(draft.sessionID))
24943
25028
  rawProviders.set(draft.sessionID, setBoundedRawMessageProvider2(draft.sessionID, createV2RawMessageProvider(pagedRead, draft.sessionID)));
25029
+ const indexDatabase = db;
25030
+ if (indexDatabase) {
25031
+ withoutSqliteTransformPass(() => {
25032
+ try {
25033
+ scheduleReconciliationCatchup(indexDatabase, draft.sessionID, pagedRead);
25034
+ } catch (error) {
25035
+ sessionLog(draft.sessionID, "message index catchup scheduling failed:", error);
25036
+ }
25037
+ });
25038
+ }
24944
25039
  transform ??= createTransform({
24945
25040
  cacheTtlConfig: config.cache_ttl,
24946
25041
  cacheTtlConfigured: config.cacheTtlConfigured,
@@ -24951,6 +25046,7 @@ async function registerContext(context) {
24951
25046
  contextUsageMap: usage,
24952
25047
  compactionOff,
24953
25048
  storeGeneration: "v2",
25049
+ normalizeRestoredAssistantText: stripCopiedTagNotation,
24954
25050
  hostCleanupCompactionMarkers: () => ({
24955
25051
  verified: true,
24956
25052
  removedLineages: 0,
@@ -25009,7 +25105,7 @@ async function registerContext(context) {
25009
25105
  const boundaryRepair = db && !compactionOff ? checkHistoryBoundary(db, reader, draft.sessionID) : undefined;
25010
25106
  if (boundaryRepair?.kind === "repaired" || boundaryRepair?.kind === "baseline-reset") {
25011
25107
  historyRefreshSessions.add(draft.sessionID);
25012
- pendingMaterializationSessions.add(draft.sessionID);
25108
+ requestPendingMaterialization(pendingMaterializationSessions, draft.sessionID, "boundary_repair");
25013
25109
  }
25014
25110
  const incoming = cut && draft.messages.find((message) => message.id === cut.id);
25015
25111
  postFold = cut !== undefined;
@@ -25027,7 +25123,7 @@ async function registerContext(context) {
25027
25123
  }
25028
25124
  if (nativeRestored && nativeFoldReplay?.sourceChanged(draft.sessionID)) {
25029
25125
  historyRefreshSessions.add(draft.sessionID);
25030
- pendingMaterializationSessions.add(draft.sessionID);
25126
+ requestPendingMaterialization(pendingMaterializationSessions, draft.sessionID, "host_history_change");
25031
25127
  }
25032
25128
  if (cut && incoming && nativeRestored) {
25033
25129
  const present = new Set(draft.messages.flatMap((message) => message.id ? [message.id] : []));
@@ -25045,7 +25141,7 @@ async function registerContext(context) {
25045
25141
  onHard: (reason) => {
25046
25142
  log(`[magic-context] HARD reason=${reason} session=${draft.sessionID}`);
25047
25143
  materialize(draft);
25048
- pendingMaterializationSessions.add(draft.sessionID);
25144
+ requestPendingMaterialization(pendingMaterializationSessions, draft.sessionID, "host_compaction");
25049
25145
  }
25050
25146
  });
25051
25147
  checkpoint = structuredClone(identity.rendered ?? incoming);
@@ -25072,7 +25168,7 @@ async function registerContext(context) {
25072
25168
  }
25073
25169
  }
25074
25170
  const mapped = adaptPayload(draft, admitted);
25075
- beginV2LkgRequest(draft.sessionID, `${draft.model.providerID}/${draft.model.id}`, latestLkgResponseIds.get(draft.sessionID));
25171
+ bindSystemDateRequestIdentity(mapped, draft);
25076
25172
  await transform({}, mapped);
25077
25173
  mapped.commit();
25078
25174
  if (db) {
@@ -25235,10 +25331,12 @@ ${baseline}
25235
25331
  throw new V2ContextRefusal(STORAGE_BUSY_MESSAGE, { cause: refusal });
25236
25332
  }
25237
25333
  }
25238
- const anchor = draft.messages.at(-1)?.id;
25334
+ const anchor = generateReplayAnchor(draft.messages);
25239
25335
  await runManagedContext(draft);
25240
- if (!compactionOff && draft.messages.some((message) => message.id === HEAD_IDS[0] || message.content.some((part) => typeof part.text === "string" && part.text.includes("<session-history>"))))
25336
+ if (!compactionOff)
25241
25337
  generateReplay.capture(draft, anchor);
25338
+ else
25339
+ sessionLog(draft.sessionID, "generate replay capture skipped: compaction-disabled");
25242
25340
  }));
25243
25341
  await context.session.hook("generate", async (draft) => {
25244
25342
  if (!compactionOff && !deletedSessions.has(draft.sessionID))