@cortexkit/opencode-magic-context 0.49.6 → 0.49.7

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 (153) hide show
  1. package/dist/auto-search-worker.js +10 -7
  2. package/dist/{compaction-marker-candidates-cz72pm1t.js → compaction-marker-candidates-a0edbqrr.js} +2 -2
  3. package/dist/compaction-marker-candidates-worker.js +2 -2
  4. package/dist/{conflict-warning-hook-1jq1rxx7.js → conflict-warning-hook-myk5hbt9.js} +5 -5
  5. package/dist/{context-ae3gxdye.js → context-ffr7e0g7.js} +46 -34
  6. package/dist/{embedding-backfill-dtjmyktb.js → embedding-backfill-re16xncd.js} +6 -6
  7. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/hidden-single-shot.d.ts +6 -3
  9. package/dist/features/magic-context/dreamer/hidden-single-shot.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +7 -1
  11. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
  12. package/dist/features/magic-context/dreamer/map-memories.d.ts +2 -2
  13. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  14. package/dist/features/magic-context/dreamer/primer-seed.d.ts +2 -0
  15. package/dist/features/magic-context/dreamer/primer-seed.d.ts.map +1 -1
  16. package/dist/features/magic-context/dreamer/refresh-primers.d.ts +2 -2
  17. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  18. package/dist/features/magic-context/storage-meta-persisted.d.ts +10 -3
  19. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  20. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  21. package/dist/features/magic-context/storage-replay-document.d.ts +4 -0
  22. package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -1
  23. package/dist/hooks/magic-context/apply-operations.d.ts +7 -0
  24. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/note-nudger.d.ts +5 -3
  29. package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/postprocess-read-cache.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/read-session-chunk.d.ts +7 -0
  32. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/replay-committed-drops.d.ts +9 -0
  34. package/dist/hooks/magic-context/replay-committed-drops.d.ts.map +1 -0
  35. package/dist/hooks/magic-context/rust-lkg-freeze-registry.d.ts +2 -2
  36. package/dist/hooks/magic-context/rust-lkg-freeze-registry.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/rust-mode-transform.d.ts +3 -0
  38. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/rust-served-raw.d.ts +29 -0
  40. package/dist/hooks/magic-context/rust-served-raw.d.ts.map +1 -0
  41. package/dist/hooks/magic-context/strip-content.d.ts +23 -24
  42. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
  44. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/tool-drop-target.d.ts +2 -0
  46. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  48. package/dist/{index-6q5h70h2.js → index-0gyq3djm.js} +1 -1
  49. package/dist/{index-vnjhmkwk.js → index-37q80veb.js} +1 -1
  50. package/dist/{index-besxhcsh.js → index-4btknbqh.js} +9 -8
  51. package/dist/{index-vh3e2mvr.js → index-5tbvdff4.js} +59 -28
  52. package/dist/{index-cm4hmxyk.js → index-9yyqq3mm.js} +1 -1
  53. package/dist/{index-ab3bgm2m.js → index-a8acx4gc.js} +2 -2
  54. package/dist/{index-j55vn694.js → index-ax2jhztn.js} +1 -1
  55. package/dist/{index-zgzdm6dg.js → index-ebnngykq.js} +145 -94
  56. package/dist/{index-g9exahnx.js → index-g8qbdb03.js} +1 -1
  57. package/dist/{index-ch08qwgd.js → index-j58gqrn2.js} +65 -65
  58. package/dist/{index-xqyj5c85.js → index-j5hce1ab.js} +2 -2
  59. package/dist/{index-0nnd4bry.js → index-j8epsc0g.js} +34 -13
  60. package/dist/{index-vztbgcdk.js → index-n5a170gs.js} +227 -38
  61. package/dist/{index-s0gsmthp.js → index-n7kwfw2r.js} +4 -4
  62. package/dist/{index-ck6y3td2.js → index-rwmpkss5.js} +3 -3
  63. package/dist/{index-bg5qqene.js → index-rzmjkdd6.js} +1 -1
  64. package/dist/{index-9s58tq95.js → index-sf94at64.js} +3 -3
  65. package/dist/{index-f3dnvnw6.js → index-sh91t19a.js} +1 -1
  66. package/dist/{index-mj1fgfrk.js → index-tagss15p.js} +857 -125
  67. package/dist/{index-1j8jcjpa.js → index-tmtty2x5.js} +1 -1
  68. package/dist/{index-hr64sth2.js → index-vt4sf4ep.js} +1 -1
  69. package/dist/{index-9m7bn21d.js → index-w4mw92w3.js} +1 -1
  70. package/dist/{index-7q1m1qjk.js → index-w6bd03dc.js} +1 -1
  71. package/dist/{index-a796ct3s.js → index-w6vr754t.js} +4 -4
  72. package/dist/{index-1xaw2pq8.js → index-wq16ahsn.js} +5 -5
  73. package/dist/index.js +36 -36
  74. package/dist/{message-index-bounded-630j53ps.js → message-index-bounded-04rycpx4.js} +9 -9
  75. package/dist/{message-index-c84wm2g0.js → message-index-rqtse28y.js} +9 -9
  76. package/dist/{message-time-backfill-8kj57qe3.js → message-time-backfill-a5ytnp53.js} +1 -1
  77. package/dist/migration-worker.js +6 -6
  78. package/dist/plugin/messages-transform.d.ts.map +1 -1
  79. package/dist/{project-embedding-registry-vv59t7ah.js → project-embedding-registry-k0sd9k7v.js} +5 -5
  80. package/dist/raw-ordinal-warmup-worker.js +17 -15
  81. package/dist/{read-session-chunk-t3vjd0h0.js → read-session-chunk-h1btx938.js} +7 -5
  82. package/dist/{read-session-raw-gf7eytnm.js → read-session-raw-vdf4wh78.js} +2 -2
  83. package/dist/{recomp-orchestrator-1d08g57a.js → recomp-orchestrator-trcgmm23.js} +20 -20
  84. package/dist/{search-kth2p6cc.js → search-3rbdx6hw.js} +16 -16
  85. package/dist/{send-session-notification-3j6krd4w.js → send-session-notification-2tp44tfa.js} +4 -4
  86. package/dist/shared/processed-image-eligibility.d.ts +22 -0
  87. package/dist/shared/processed-image-eligibility.d.ts.map +1 -0
  88. package/dist/shared/recovery-reasoning-prefix.d.ts +9 -0
  89. package/dist/shared/recovery-reasoning-prefix.d.ts.map +1 -0
  90. package/dist/shared/sqlite.d.ts +1 -1
  91. package/dist/shared/sqlite.d.ts.map +1 -1
  92. package/dist/shared/tag-transcript.d.ts +9 -0
  93. package/dist/shared/tag-transcript.d.ts.map +1 -1
  94. package/dist/{sqlite-d7edbk1s.js → sqlite-j3tvg8r0.js} +1 -1
  95. package/dist/{storage-memory-af5zdz98.js → storage-memory-8168bgef.js} +2 -2
  96. package/dist/{storage-tags-dyj7mmbq.js → storage-tags-61z5j2f9.js} +2 -2
  97. package/dist/tools/ctx-expand/search-archive.d.ts +4 -0
  98. package/dist/tools/ctx-expand/search-archive.d.ts.map +1 -0
  99. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  100. package/dist/{transform-postprocess-phase-bv3kb5s1.js → transform-postprocess-phase-7qts7h54.js} +20 -20
  101. package/dist/v2/auto-search-worker.js +10 -7
  102. package/dist/v2/{compaction-marker-candidates-6gqp5wrc.js → compaction-marker-candidates-6nypjkpf.js} +2 -2
  103. package/dist/v2/compaction-marker-candidates-worker.js +2 -2
  104. package/dist/v2/{context-p8f2shp1.js → context-xq4rtywr.js} +1019 -233
  105. package/dist/v2/{embedding-backfill-cg4rv90f.js → embedding-backfill-xd9ygzgd.js} +6 -6
  106. package/dist/v2/hooks/payload.d.ts.map +1 -1
  107. package/dist/v2/{message-index-3ws17x1g.js → message-index-8e5z2xx8.js} +9 -9
  108. package/dist/v2/{message-index-bounded-yzxmf6vp.js → message-index-bounded-5vh1h2g7.js} +9 -9
  109. package/dist/v2/{message-time-backfill-5nf10c65.js → message-time-backfill-2z365239.js} +1 -1
  110. package/dist/v2/migration-worker.js +6 -6
  111. package/dist/v2/{project-embedding-registry-rsnf2263.js → project-embedding-registry-b77awytr.js} +5 -5
  112. package/dist/v2/raw-ordinal-warmup-worker.js +17 -15
  113. package/dist/v2/{read-session-chunk-vtb0w2kd.js → read-session-chunk-4sat83cx.js} +7 -5
  114. package/dist/v2/{read-session-raw-gpcddemt.js → read-session-raw-a3hw9jsp.js} +2 -2
  115. package/dist/v2/{recomp-orchestrator-v64r4s4r.js → recomp-orchestrator-n11zv1f1.js} +19 -19
  116. package/dist/v2/{search-bpa4fxq1.js → search-mvgqtkkz.js} +16 -16
  117. package/dist/v2/{send-session-notification-5aj2j7vf.js → send-session-notification-ppgc8qdz.js} +4 -4
  118. package/dist/v2/{server-wrem2nmz.js → server-1rfgbjfn.js} +1 -1
  119. package/dist/v2/{server-qn23p4h7.js → server-7srtwsa4.js} +5 -5
  120. package/dist/v2/{server-cm4hmxyk.js → server-9yyqq3mm.js} +1 -1
  121. package/dist/v2/{server-yezz0cp2.js → server-brhj8qtd.js} +1 -1
  122. package/dist/v2/{server-387vkhjq.js → server-dd0nja67.js} +1 -1
  123. package/dist/v2/{server-keswwkvv.js → server-fpqe39as.js} +1 -1
  124. package/dist/v2/{server-158ehmk2.js → server-fw31krgq.js} +3 -3
  125. package/dist/v2/{server-fby0cxbq.js → server-gtv9wbhq.js} +227 -38
  126. package/dist/v2/{server-ww9nr8pe.js → server-hcvcxzx7.js} +2 -2
  127. package/dist/v2/{server-0nnd4bry.js → server-j8epsc0g.js} +34 -13
  128. package/dist/v2/{server-x2r63c0x.js → server-jdwd03ep.js} +4 -4
  129. package/dist/v2/{server-btgnsv7w.js → server-kdrezdy0.js} +1 -1
  130. package/dist/v2/{server-7skbfb81.js → server-mv1w7t6z.js} +61 -30
  131. package/dist/v2/{server-aa9w15cs.js → server-nfan6zb6.js} +65 -65
  132. package/dist/v2/{server-9zqewm6g.js → server-qcw43339.js} +1 -1
  133. package/dist/v2/{server-f9py7s7a.js → server-sy4b1sra.js} +1 -1
  134. package/dist/v2/{server-09g9pkvx.js → server-v4fwha43.js} +9 -8
  135. package/dist/v2/{server-yd01snxq.js → server-w3zeywp1.js} +1 -1
  136. package/dist/v2/{server-4tn95znb.js → server-wkdgjjch.js} +1 -1
  137. package/dist/v2/{server-6a9mxnb2.js → server-x08d1xf3.js} +1 -1
  138. package/dist/v2/server.js +4 -4
  139. package/dist/v2/{sqlite-d7edbk1s.js → sqlite-j3tvg8r0.js} +1 -1
  140. package/dist/v2/{storage-memory-af5zdz98.js → storage-memory-8168bgef.js} +2 -2
  141. package/dist/v2/{storage-tags-d47bpaqa.js → storage-tags-j2fzq3sw.js} +2 -2
  142. package/dist/v2/{wrapup-orchestrator-cahk37n7.js → wrapup-orchestrator-s92ebfzv.js} +19 -19
  143. package/dist/{wrapup-orchestrator-mqp0v195.js → wrapup-orchestrator-df61jmz9.js} +21 -21
  144. package/package.json +3 -3
  145. package/src/shared/processed-image-eligibility.test.ts +68 -0
  146. package/src/shared/processed-image-eligibility.ts +41 -0
  147. package/src/shared/processed-image-position-review.json +20 -0
  148. package/src/shared/processed-image-position-review.test.ts +19 -0
  149. package/src/shared/recovery-reasoning-prefix.test.ts +34 -0
  150. package/src/shared/recovery-reasoning-prefix.ts +35 -0
  151. package/src/shared/sqlite-writer-diagnostics.test.ts +31 -0
  152. package/src/shared/sqlite.ts +63 -22
  153. package/src/shared/tag-transcript.ts +118 -0
@@ -109,6 +109,8 @@ import {
109
109
  resolveLkgModelKeys,
110
110
  createLkgEntryProjector,
111
111
  captureLkgSlot,
112
+ validateAnthropicReasoningRuns,
113
+ validateLkgSeam,
112
114
  replayLkg,
113
115
  coldStartRawServedIndex,
114
116
  coldStartUncapturedReplay,
@@ -263,7 +265,7 @@ import {
263
265
  startCompartmentAgent,
264
266
  setRecompStarting2,
265
267
  setRecompTerminal2
266
- } from "./server-7skbfb81.js";
268
+ } from "./server-mv1w7t6z.js";
267
269
  import {
268
270
  CANONICAL_DREAM_TASKS,
269
271
  DREAM_TASK_CAPABILITIES,
@@ -301,7 +303,7 @@ import {
301
303
  openDatabaseAsync,
302
304
  isDatabasePersisted,
303
305
  getDatabasePersistenceError
304
- } from "./server-09g9pkvx.js";
306
+ } from "./server-v4fwha43.js";
305
307
  import {
306
308
  setHomeProjectPermission,
307
309
  shouldSkipHomeProjectMemory,
@@ -317,7 +319,7 @@ import {
317
319
  storedPathBelongsToIdentity,
318
320
  beginBootQuietPeriod,
319
321
  scheduleAfterBootQuiet
320
- } from "./server-btgnsv7w.js";
322
+ } from "./server-kdrezdy0.js";
321
323
  import {
322
324
  detectLatestTurnThinkingMismatch,
323
325
  isPrefixBoundThinkingModel,
@@ -346,6 +348,8 @@ import {
346
348
  sessionDecisionCalibration,
347
349
  transitionSessionHygieneUnits,
348
350
  getProtectionWindowForSession,
351
+ withReplayEnvelopeSnapshot,
352
+ invalidateReplayEnvelopeSnapshot,
349
353
  isRustMarkerAdmissionFenced,
350
354
  setRustMarkerAdmissionFence,
351
355
  ReplayDocumentError,
@@ -415,6 +419,7 @@ import {
415
419
  getStaleReduceStrippedIds,
416
420
  addStaleReduceStrippedIds,
417
421
  getProcessedImageStrippedIds,
422
+ getProcessedImageMarkers,
418
423
  addProcessedImageStrippedIds,
419
424
  getPendingCompactionMarkerState,
420
425
  getCompactionMarkerHealth,
@@ -424,6 +429,7 @@ import {
424
429
  clearPendingCompactionMarkerStateIf,
425
430
  getPendingPiCompactionMarkerState,
426
431
  clearPendingPiCompactionMarkerStateIf,
432
+ logPiCompactionMarkerClear,
427
433
  setSessionWorkMetrics,
428
434
  getObservedEpochFloor,
429
435
  resolveEpochFloorForPass,
@@ -458,6 +464,7 @@ import {
458
464
  stripReasoningFromAssistantIds,
459
465
  findMergedReasoningStripDecisions,
460
466
  stripReasoningFromMergedAssistants,
467
+ getEmptyImageParts,
461
468
  stripProcessedImages,
462
469
  projectOpencodeReasoningBudgetCutoff,
463
470
  DEFAULT_KEEP_REASONING_TOKENS,
@@ -483,6 +490,7 @@ import {
483
490
  withRawSessionMessageCache2,
484
491
  readRawSessionMessages2,
485
492
  visitRawSessionMessages2,
493
+ visitRawSessionMessagesAsync2,
486
494
  prepareRawSessionOrdinals2,
487
495
  primeTailRawMessageCache2,
488
496
  primeInMemoryTailRawMessageCache2,
@@ -495,7 +503,7 @@ import {
495
503
  readRawSessionMessageById2,
496
504
  readSessionChunk2,
497
505
  readRawSessionSeedTail2
498
- } from "./server-fby0cxbq.js";
506
+ } from "./server-gtv9wbhq.js";
499
507
  import {
500
508
  textMentionsRecentCommit,
501
509
  hasMeaningfulUserText2,
@@ -527,21 +535,21 @@ import {
527
535
  isTransientSqliteError2,
528
536
  SqliteAcquisitionBusyError2,
529
537
  withAsyncPrivilegedWriter2
530
- } from "./server-0nnd4bry.js";
538
+ } from "./server-j8epsc0g.js";
531
539
  import {
532
540
  sameProjectDirectory,
533
541
  canonicalProjectDirectory
534
- } from "./server-yezz0cp2.js";
542
+ } from "./server-brhj8qtd.js";
535
543
  import {
536
544
  deleteChunkEmbedBackoffForSession
537
- } from "./server-keswwkvv.js";
545
+ } from "./server-fpqe39as.js";
538
546
  import {
539
547
  registerOwnRpcServerInstance,
540
548
  rpcPortDir,
541
549
  rpcPortFilePath,
542
550
  parseRpcPortFile,
543
551
  inspectProcessesAsync
544
- } from "./server-wrem2nmz.js";
552
+ } from "./server-1rfgbjfn.js";
545
553
  import {
546
554
  BoundedSessionMap,
547
555
  LkgRecoveryFenceError,
@@ -560,7 +568,7 @@ import {
560
568
  isMessageIndexReconciledThrough2,
561
569
  indexMessagesAfterOrdinal2,
562
570
  sweepOrphanedOpenCodeMessageIndexes2
563
- } from "./server-qn23p4h7.js";
571
+ } from "./server-7srtwsa4.js";
564
572
  import {
565
573
  stableStringify,
566
574
  getTemporalDecisions,
@@ -659,14 +667,14 @@ import {
659
667
  adoptNullOwnerToolTag2,
660
668
  getCandidateToolOwners2,
661
669
  pickNearestPriorOwner2
662
- } from "./server-yd01snxq.js";
670
+ } from "./server-w3zeywp1.js";
663
671
  import"./server-51779zs2.js";
664
672
  import {
665
673
  isRawCompactionSummaryInfo2,
666
674
  extractInMemoryMessageViews2,
667
675
  buildInMemoryTailRawMessages2,
668
676
  runSessionWarmupWorker
669
- } from "./server-387vkhjq.js";
677
+ } from "./server-dd0nja67.js";
670
678
  import {
671
679
  resolveOpenCodeDbPath,
672
680
  assertOpenCodeStoreGeneration,
@@ -682,7 +690,7 @@ import {
682
690
  latestRealUserTurnIdFromOpenCodeDb,
683
691
  getMessageTimesFromOpenCodeDb,
684
692
  findLastAssistantModelFromOpenCodeDb
685
- } from "./server-4tn95znb.js";
693
+ } from "./server-wkdgjjch.js";
686
694
  import {
687
695
  getCurrentToolSetHash,
688
696
  recordToolDefinition,
@@ -700,7 +708,7 @@ import {
700
708
  resolveStoredPathWorkspaceIdentity,
701
709
  storedPathBelongsToWorkspace,
702
710
  computeWorkspaceEpochFingerprint
703
- } from "./server-ww9nr8pe.js";
711
+ } from "./server-hcvcxzx7.js";
704
712
  import {
705
713
  GIT_SWEEP_LEASE_RENEWAL_MS,
706
714
  acquireGitSweepLease,
@@ -727,7 +735,7 @@ import {
727
735
  drainCommitBacklogForProject2,
728
736
  embedSessionCompartmentChunks2,
729
737
  getEmbeddingCoverageStatus2
730
- } from "./server-158ehmk2.js";
738
+ } from "./server-fw31krgq.js";
731
739
  import"./server-fb3zxn06.js";
732
740
  import {
733
741
  SESSION_NOTE_CONDITION_ERROR,
@@ -771,7 +779,7 @@ import {
771
779
  readGitFileChangeTimesSince,
772
780
  verificationFileExists,
773
781
  normalizeVerificationFiles
774
- } from "./server-x2r63c0x.js";
782
+ } from "./server-jdwd03ep.js";
775
783
  import {
776
784
  deriveTriggerBudget2,
777
785
  deriveHistorianChunkTokens2,
@@ -779,7 +787,7 @@ import {
779
787
  resolveHistorianProducerLimits2,
780
788
  resolveHistorianContextLimit2
781
789
  } from "./server-t528t8qm.js";
782
- import"./server-6a9mxnb2.js";
790
+ import"./server-x08d1xf3.js";
783
791
  import {
784
792
  isEmbeddingHostBusy2
785
793
  } from "./server-qt1qqasc.js";
@@ -793,7 +801,7 @@ import {
793
801
  } from "./server-ras2sxnc.js";
794
802
  import {
795
803
  cosineSimilarity
796
- } from "./server-f9py7s7a.js";
804
+ } from "./server-sy4b1sra.js";
797
805
  import {
798
806
  computeCueContentHash,
799
807
  hasMuralCueColumns,
@@ -818,7 +826,7 @@ import {
818
826
  mergeMemoryStats2,
819
827
  archiveMemory2,
820
828
  getMemoryCountsByStatus2
821
- } from "./server-cm4hmxyk.js";
829
+ } from "./server-9yyqq3mm.js";
822
830
  import {
823
831
  registerNotificationSink2,
824
832
  pushNotification2,
@@ -830,7 +838,7 @@ import"./server-676xw7at.js";
830
838
  import {
831
839
  flushIgnoredMessages2,
832
840
  sendStatusNotification2
833
- } from "./server-9zqewm6g.js";
841
+ } from "./server-qcw43339.js";
834
842
  import {
835
843
  renderSingleStoreMigrationRequiredRefusal,
836
844
  SingleStoreMigrationRequiredError,
@@ -845,12 +853,13 @@ import {
845
853
  SubcModuleTransport,
846
854
  indexCommitsForProject,
847
855
  embedUnembeddedCommits,
856
+ withMessageFtsSessionFilter,
848
857
  createUnifiedSearchDiagnostics2,
849
858
  parseIdShapedQuery2,
850
859
  resolveMemoriesByIdsForSearch2,
851
860
  unifiedSearch2,
852
861
  formatSearchResults2
853
- } from "./server-aa9w15cs.js";
862
+ } from "./server-nfan6zb6.js";
854
863
  import {
855
864
  resolvePromptContext2
856
865
  } from "./server-fmsr7v69.js";
@@ -2782,6 +2791,7 @@ function forgetCachedPrefix(sessionId) {
2782
2791
  function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = false, onRetire) {
2783
2792
  db.exec("BEGIN IMMEDIATE");
2784
2793
  let finished = false;
2794
+ let clearedMarker = null;
2785
2795
  try {
2786
2796
  if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
2787
2797
  db.exec("ROLLBACK");
@@ -2798,14 +2808,16 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = f
2798
2808
  if (activeBranch) {
2799
2809
  db.prepare("UPDATE session_meta SET prior_boundary_ordinal = ? WHERE session_id = ?").run((anchor?.endMessage ?? 0) + 1, sessionId);
2800
2810
  const marker = getPendingPiCompactionMarkerState(db, sessionId);
2801
- if (marker)
2802
- clearPendingPiCompactionMarkerStateIf(db, sessionId, marker);
2811
+ if (marker && clearPendingPiCompactionMarkerStateIf(db, sessionId, marker, null))
2812
+ clearedMarker = marker;
2803
2813
  }
2804
2814
  deleteChunkEmbedBackoffForSession(db, sessionId);
2805
2815
  queueM0Mutation(db, { sessionId, mutationType: "recomp_boundary_change" });
2806
2816
  clearCachedM0M1(db, sessionId);
2807
2817
  db.exec("COMMIT");
2808
2818
  finished = true;
2819
+ if (clearedMarker)
2820
+ logPiCompactionMarkerClear(sessionId, clearedMarker, "active-branch-retired");
2809
2821
  return dropped.map((row) => row.sequence);
2810
2822
  } finally {
2811
2823
  if (!finished) {
@@ -6483,21 +6495,40 @@ function buildReplacementContent(tagId) {
6483
6495
  return `[dropped §${tagId}§]`;
6484
6496
  }
6485
6497
  function applyNewToolDrop(target, options) {
6486
- if (!target)
6498
+ if (options.persist && (target?.prepareDrop?.() ?? target?.canDrop?.()) === false) {
6499
+ return { result: "incomplete", mode: "full" };
6500
+ }
6501
+ if (!target) {
6502
+ options.persist?.("full");
6487
6503
  return { result: "absent", mode: "full" };
6488
- if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true) {
6504
+ }
6505
+ if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true || target.wouldStrandConversationEnd?.() === true) {
6489
6506
  const strip = target.hasAttachments?.() === true;
6507
+ options.persist?.(strip ? "skeleton_stripped" : "skeleton_real");
6490
6508
  return {
6491
6509
  result: (strip ? target.skeletonStripped?.() : target.skeletonReal?.()) ?? "absent",
6492
6510
  mode: strip ? "skeleton_stripped" : "skeleton_real"
6493
6511
  };
6494
6512
  }
6513
+ options.persist?.("full");
6495
6514
  const result = target.drop?.() ?? "absent";
6496
6515
  if (result === "truncated" && target.hasAttachments?.() === true) {
6497
6516
  return { result: target.skeletonStripped?.() ?? "absent", mode: "skeleton_stripped" };
6498
6517
  }
6499
6518
  return { result, mode: result === "truncated" ? "skeleton_real" : "full" };
6500
6519
  }
6520
+ function applyPersistedToolDrop(db, sessionId, tagNumber, target, options, afterWrite) {
6521
+ return applyNewToolDrop(target, {
6522
+ ...options,
6523
+ persist: (mode) => {
6524
+ db.transaction(() => {
6525
+ updateTagDropMode2(db, sessionId, tagNumber, mode);
6526
+ updateTagStatus2(db, sessionId, tagNumber, "dropped");
6527
+ afterWrite?.();
6528
+ }).immediate();
6529
+ }
6530
+ });
6531
+ }
6501
6532
  function hasSmallToolInput(target) {
6502
6533
  const bytes = target?.inputStringBytes?.() ?? null;
6503
6534
  return bytes !== null && bytes <= SKELETON_REAL_INPUT_MAX_BYTES;
@@ -6577,11 +6608,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
6577
6608
  diagnostics.reasons[reason] = (diagnostics.reasons[reason] ?? 0) + 1;
6578
6609
  };
6579
6610
  let admitted = false;
6580
- let startedAt = 0;
6581
6611
  try {
6582
6612
  db.transaction(() => {
6583
6613
  admitted = true;
6584
- startedAt = performance.now();
6614
+ }).immediate();
6615
+ {
6585
6616
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
6586
6617
  const tags = preloadedTags ?? getTagsByNumbers2(db, sessionId, [
6587
6618
  ...new Set([
@@ -6633,10 +6664,31 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
6633
6664
  continue;
6634
6665
  }
6635
6666
  }
6667
+ const persistDrop = (mode) => {
6668
+ let startedAt = 0;
6669
+ try {
6670
+ db.transaction(() => {
6671
+ startedAt = performance.now();
6672
+ if (mode)
6673
+ updateTagDropMode2(db, sessionId, pendingOp.tagId, mode);
6674
+ updateTagStatus2(db, sessionId, pendingOp.tagId, "dropped");
6675
+ if (!synthetic)
6676
+ removePendingOp(db, sessionId, pendingOp.tagId);
6677
+ }).immediate();
6678
+ } finally {
6679
+ if (startedAt)
6680
+ logSlowWriteTransaction("apply_pending_operation", startedAt);
6681
+ }
6682
+ };
6636
6683
  let shouldPersistDrop = false;
6637
6684
  if (isToolTag) {
6638
6685
  if (editMarkerTagIds.has(pendingOp.tagId)) {
6639
6686
  const strip = target?.hasAttachments?.() === true;
6687
+ if (target?.canDrop?.() === false || target?.prepareDrop?.() === false || !target) {
6688
+ reject("edit_marker_incomplete");
6689
+ continue;
6690
+ }
6691
+ persistDrop(strip ? "edit_marker_stripped" : "edit_marker");
6640
6692
  const markResult = (strip ? target?.editMarkerStripped?.() : target?.editMarker?.()) ?? "absent";
6641
6693
  if (markResult === "incomplete" || markResult === "absent") {
6642
6694
  reject(`edit_marker_${markResult}`);
@@ -6645,11 +6697,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
6645
6697
  didMutateMessage = true;
6646
6698
  operationMutated = true;
6647
6699
  onTagReduced?.({ tagNumber: pendingOp.tagId, mode: "edit_marker" });
6648
- updateTagDropMode2(db, sessionId, pendingOp.tagId, strip ? "edit_marker_stripped" : "edit_marker");
6649
6700
  shouldPersistDrop = true;
6650
6701
  } else {
6651
6702
  const { result: dropResult, mode: appliedMode } = applyNewToolDrop(target, {
6652
- inWindow: skeletonWindow.has(pendingOp.tagId)
6703
+ inWindow: skeletonWindow.has(pendingOp.tagId),
6704
+ persist: persistDrop
6653
6705
  });
6654
6706
  if (dropResult === "incomplete" || synthetic && dropResult !== "removed" && dropResult !== "truncated") {
6655
6707
  reject(`drop_${dropResult}`);
@@ -6665,12 +6717,12 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
6665
6717
  } else {
6666
6718
  reject(`drop_${dropResult}`);
6667
6719
  }
6668
- updateTagDropMode2(db, sessionId, pendingOp.tagId, appliedMode);
6669
6720
  shouldPersistDrop = true;
6670
6721
  }
6671
6722
  } else if (target) {
6672
6723
  const replacement = buildReplacementContent(pendingOp.tagId);
6673
6724
  const priorContent = target.getContent?.();
6725
+ persistDrop();
6674
6726
  const changed = target.setContent(replacement);
6675
6727
  if (changed) {
6676
6728
  didMutateMessage = true;
@@ -6687,30 +6739,25 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
6687
6739
  shouldPersistDrop = true;
6688
6740
  } else if (!synthetic) {
6689
6741
  reject("target_absent");
6742
+ persistDrop();
6690
6743
  shouldPersistDrop = true;
6691
6744
  }
6692
6745
  if (!shouldPersistDrop) {
6693
6746
  reject("not_persisted");
6694
6747
  continue;
6695
6748
  }
6696
- updateTagStatus2(db, sessionId, pendingOp.tagId, "dropped");
6697
- if (!synthetic)
6698
- removePendingOp(db, sessionId, pendingOp.tagId);
6699
6749
  if (operationMutated)
6700
6750
  diagnostics.mutated += 1;
6701
6751
  else
6702
6752
  diagnostics.persistedWithoutMutation += 1;
6703
6753
  }
6704
- }).immediate();
6754
+ }
6705
6755
  } catch (error) {
6706
6756
  if (!admitted && /database (?:table )?is locked|sqlite_(busy|locked)/i.test(String(error))) {
6707
6757
  sessionLog2(sessionId, "pending operations write admission busy; retaining prior bytes for retry");
6708
6758
  return false;
6709
6759
  }
6710
6760
  throw error;
6711
- } finally {
6712
- if (admitted)
6713
- logSlowWriteTransaction("apply_pending_operations", startedAt);
6714
6761
  }
6715
6762
  if (diagnostics) {
6716
6763
  onBatchComplete?.(diagnostics);
@@ -7208,31 +7255,27 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
7208
7255
  if (plan.shouldDrop) {
7209
7256
  const toDrop = new Set(plan.tagNumbers);
7210
7257
  const newestEmergencyTags = recentTags;
7211
- db.transaction(() => {
7212
- for (const tag of tags) {
7213
- if (!toDrop.has(tag.tagNumber))
7214
- continue;
7215
- if (tag.status !== "active" || tag.type !== "tool")
7216
- continue;
7217
- const target = targets.get(tag.tagNumber);
7218
- const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
7219
- const { result, mode } = applyNewToolDrop(target, {
7220
- inWindow: recent,
7221
- keepSkeleton: target?.requiresToolArcSkeleton === true
7258
+ for (const tag of tags) {
7259
+ if (!toDrop.has(tag.tagNumber))
7260
+ continue;
7261
+ if (tag.status !== "active" || tag.type !== "tool")
7262
+ continue;
7263
+ const target = targets.get(tag.tagNumber);
7264
+ const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
7265
+ const { result } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
7266
+ inWindow: recent,
7267
+ keepSkeleton: target?.requiresToolArcSkeleton === true
7268
+ });
7269
+ if (result === "removed" || result === "truncated") {
7270
+ droppedTools++;
7271
+ emergencyDroppedTools++;
7272
+ droppedTokenReductions.push({
7273
+ tagNumber: tag.tagNumber,
7274
+ mode: result === "removed" ? "full" : "truncated"
7222
7275
  });
7223
- if (result === "removed" || result === "truncated") {
7224
- updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
7225
- updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
7226
- droppedTools++;
7227
- emergencyDroppedTools++;
7228
- droppedTokenReductions.push({
7229
- tagNumber: tag.tagNumber,
7230
- mode: result === "removed" ? "full" : "truncated"
7231
- });
7232
- emergencyReclaimedTokens += estimateEmergencyDropReclaimTokens(measuredByTag.get(tag.tagNumber) ?? tag);
7233
- }
7276
+ emergencyReclaimedTokens += estimateEmergencyDropReclaimTokens(measuredByTag.get(tag.tagNumber) ?? tag);
7234
7277
  }
7235
- }).immediate();
7278
+ }
7236
7279
  sessionLog2(sessionId, `emergency tiered drop: ${plan.reason}`);
7237
7280
  } else {
7238
7281
  sessionLog2(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
@@ -7313,35 +7356,33 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
7313
7356
  group.push(tag);
7314
7357
  fingerprintGroups.set(fingerprint, group);
7315
7358
  }
7316
- db.transaction(() => {
7317
- for (const [, group] of fingerprintGroups) {
7318
- if (group.length <= 1)
7359
+ for (const [, group] of fingerprintGroups) {
7360
+ if (group.length <= 1)
7361
+ continue;
7362
+ group.sort((a, b) => a.tagNumber - b.tagNumber);
7363
+ for (let i = 0;i < group.length - 1; i++) {
7364
+ const tag = group[i];
7365
+ if (config.protectedTagNumbers.has(tag.tagNumber))
7319
7366
  continue;
7320
- group.sort((a, b) => a.tagNumber - b.tagNumber);
7321
- for (let i = 0;i < group.length - 1; i++) {
7322
- const tag = group[i];
7323
- if (config.protectedTagNumbers.has(tag.tagNumber))
7324
- continue;
7325
- const target = targets.get(tag.tagNumber);
7326
- if (protectedTools.has(tag.tagNumber))
7327
- continue;
7328
- if (target?.canDrop?.() === false)
7329
- continue;
7330
- const { result, mode } = applyNewToolDrop(target, { inWindow: false });
7331
- if (result === "incomplete")
7332
- continue;
7333
- updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
7334
- updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
7335
- if (result === "removed" || result === "truncated") {
7336
- deduplicatedTools++;
7337
- droppedTokenReductions.push({
7338
- tagNumber: tag.tagNumber,
7339
- mode: result === "removed" ? "full" : "truncated"
7340
- });
7341
- }
7367
+ const target = targets.get(tag.tagNumber);
7368
+ if (protectedTools.has(tag.tagNumber))
7369
+ continue;
7370
+ if (target?.canDrop?.() === false)
7371
+ continue;
7372
+ const { result } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
7373
+ inWindow: false
7374
+ });
7375
+ if (result === "incomplete")
7376
+ continue;
7377
+ if (result === "removed" || result === "truncated") {
7378
+ deduplicatedTools++;
7379
+ droppedTokenReductions.push({
7380
+ tagNumber: tag.tagNumber,
7381
+ mode: result === "removed" ? "full" : "truncated"
7382
+ });
7342
7383
  }
7343
7384
  }
7344
- }).immediate();
7385
+ }
7345
7386
  }
7346
7387
  if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0) {
7347
7388
  sessionLog2(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
@@ -7515,6 +7556,7 @@ function postprocessReplaySnapshot(db, sessionId) {
7515
7556
  ...replay,
7516
7557
  staleReduceStrippedIds: new Set(replay.staleReduceStrippedIds),
7517
7558
  processedImageStrippedIds: new Set(replay.processedImageStrippedIds),
7559
+ processedImageMarkers: new Map(replay.processedImageMarkers),
7518
7560
  strippedPlaceholderIds: new Set(replay.strippedPlaceholderIds),
7519
7561
  hiddenSeamPlaceholderIds: new Set(replay.hiddenSeamPlaceholderIds),
7520
7562
  mergedReasoningStrippedIds: new Set(replay.mergedReasoningStrippedIds),
@@ -8820,11 +8862,6 @@ function runRustModePostprocess(args) {
8820
8862
  for (const anchor of getNoteNudgeAnchors(args.db, args.sessionId)) {
8821
8863
  appendReminderToUserMessageById(args.messages, anchor.messageId, anchor.text);
8822
8864
  }
8823
- for (const decision of getAutoSearchHintDecisions(args.db, args.sessionId)) {
8824
- if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(args.sessionId, decision.messageId) === undefined) {
8825
- appendReminderToUserMessageById(args.messages, decision.messageId, decision.text);
8826
- }
8827
- }
8828
8865
  const trailingBlankDecisions = new Map;
8829
8866
  const visibleAssistantIds = args.messages.filter((message) => message.info.role === "assistant").map((message) => message.info.id).filter((id) => typeof id === "string");
8830
8867
  if (modelAcceptsEmptyContent(args.resolvedProviderID)) {
@@ -8862,7 +8899,7 @@ function runRustModePostprocess(args) {
8862
8899
  userMessageIds: args.messages.filter((m) => m.info.role === "user" && typeof m.info.id === "string").map((m) => m.info.id),
8863
8900
  anchorMessageId: currentUserMessageId,
8864
8901
  isLiveTail: args.messages.at(-1)?.info.id === currentUserMessageId,
8865
- isCacheBustingPass: args.cacheBustingPass === true
8902
+ prefixRewriteCoversAnchor: false
8866
8903
  });
8867
8904
  const noteReadStillVisible = hasVisibleNoteReadCall(args.messages);
8868
8905
  const deferredNoteText = peekNoteNudgeText(args.db, args.sessionId, currentUserMessageId, args.projectPath, noteReadStillVisible, eligibility);
@@ -8879,6 +8916,11 @@ function runRustModePostprocess(args) {
8879
8916
  sessionLog2(args.sessionId, `rust note-nudge delivery skipped wire append: ${outcome.kind}`);
8880
8917
  }
8881
8918
  }
8919
+ for (const decision of getAutoSearchHintDecisions(args.db, args.sessionId)) {
8920
+ if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(args.sessionId, decision.messageId) === undefined) {
8921
+ appendReminderToUserMessageById(args.messages, decision.messageId, decision.text);
8922
+ }
8923
+ }
8882
8924
  const { thinkingBindingRecovery, proactiveThinkingStrip } = applyRustModeThinkingStrips(args, noteNudgeAppended || servedMarkerRecord() !== servedMarkerBefore);
8883
8925
  const marker = getPersistedCompactionMarkerState(args.db, args.sessionId);
8884
8926
  return {
@@ -9737,15 +9779,32 @@ async function runPostTransformPhase(args) {
9737
9779
  sessionLog2(args.sessionId, "transform failed dropping stale ctx_reduce calls:", error);
9738
9780
  }
9739
9781
  }
9740
- if (canUseEmptySentinels && !compactionOff && replaySnapshot) {
9782
+ if (!compactionOff && replaySnapshot) {
9741
9783
  try {
9742
9784
  const tImg = performance.now();
9785
+ const imageWatermark = canEditWholePrefix ? Math.max(args.watermark, getMaxDroppedTagNumber2(args.db, args.sessionId)) : args.watermark;
9743
9786
  const frozenImageIds = replaySnapshot.processedImageStrippedIds;
9787
+ const frozenMarkers = replaySnapshot.processedImageMarkers;
9788
+ const replacementText = canUseEmptySentinels ? "" : "[image stripped]";
9744
9789
  stripProcessedImages(args.messages, frozenImageIds, {
9745
- detect: canEditWholePrefix && args.watermark > 0,
9746
- watermark: args.watermark,
9790
+ detect: canEditWholePrefix && imageWatermark > 0,
9791
+ watermark: imageWatermark,
9747
9792
  messageTagNumbers: args.messageTagNumbers,
9748
- persistNewIds: (ids) => addProcessedImageStrippedIds(args.db, args.sessionId, ids),
9793
+ protectedCutoff: args.protectedCutoff,
9794
+ contextPercentage: args.contextUsage.percentage,
9795
+ replacementText,
9796
+ frozenMarkers,
9797
+ protectedMessageIds: new Set([
9798
+ ...args.tags.filter((tag) => protectedToolTags.has(tag.tagNumber)).map((tag) => tag.toolOwnerMessageId ?? tag.messageId)
9799
+ ]),
9800
+ persistNewIds: (ids, markers) => {
9801
+ if (!addProcessedImageStrippedIds(args.db, args.sessionId, ids, markers))
9802
+ return false;
9803
+ const committed = getProcessedImageMarkers(args.db, args.sessionId);
9804
+ for (const id of ids)
9805
+ frozenMarkers.set(id, committed.get(id) ?? replacementText);
9806
+ return true;
9807
+ },
9749
9808
  onFirstApplication: recordFirstApplicationAt
9750
9809
  });
9751
9810
  logTransformTiming(args.sessionId, "stripProcessedImages", tImg);
@@ -9873,16 +9932,6 @@ async function runPostTransformPhase(args) {
9873
9932
  logTransformTiming(args.sessionId, "pp.placeholderNeutralize", tPlaceholder);
9874
9933
  }
9875
9934
  const tNudgeBlock = performance.now();
9876
- if (args.fullFeatureMode && !compactionOff && replaySnapshot) {
9877
- for (const anchor of replaySnapshot.noteNudgeAnchors) {
9878
- appendReminderToUserMessageById(args.messages, anchor.messageId, anchor.text);
9879
- }
9880
- for (const decision of replaySnapshot.autoSearchHintDecisions) {
9881
- if (decision.decision === "hint") {
9882
- appendReminderToUserMessageById(args.messages, decision.messageId, decision.text);
9883
- }
9884
- }
9885
- }
9886
9935
  logTransformTiming(args.sessionId, "pp.nudgeAndSticky", tNudgeBlock);
9887
9936
  const tMarker = performance.now();
9888
9937
  const explicitRebuildHappened = args.historyRefreshExplicitBeforePrepare && args.rebuiltHistoryFromInitialPrepare;
@@ -9958,13 +10007,15 @@ async function runPostTransformPhase(args) {
9958
10007
  const noteReadStillVisible = args.fullFeatureMode ? hasVisibleNoteReadCall(args.messages) : false;
9959
10008
  let noteNudgeAppendedThisPass = false;
9960
10009
  const noteNudgeUserMessageId = findNoteNudgeUserMessageId(args.messages);
10010
+ const lastServedForNote = canEditWholePrefix && (m0RematerializedThisPass || historyWasConsumedThisPass) ? args.readLastServedMessages?.() : null;
10011
+ const rewrittenHeadCoversNote = !!lastServedForNote && args.messages.slice(0, prependedMessageCount).some((message, index) => isSyntheticHeadMessage(message) && index < lastServedForNote.length && servedMessageKey(message, args.resolvedProviderID) !== servedMessageKey(lastServedForNote[index], args.resolvedProviderID));
9961
10012
  const noteNudgeEligibility = args.fullFeatureMode ? observeNoteNudgeServe({
9962
10013
  db: args.db,
9963
10014
  sessionId: args.sessionId,
9964
10015
  userMessageIds: args.messages.filter((m) => m.info.role === "user" && typeof m.info.id === "string").map((m) => m.info.id),
9965
10016
  anchorMessageId: noteNudgeUserMessageId,
9966
10017
  isLiveTail: args.messages.at(-1)?.info.id === noteNudgeUserMessageId,
9967
- isCacheBustingPass
10018
+ prefixRewriteCoversAnchor: canEditWholePrefix && rewrittenHeadCoversNote
9968
10019
  }) : undefined;
9969
10020
  const deferredNoteText = args.fullFeatureMode ? peekNoteNudgeText(args.db, args.sessionId, noteNudgeUserMessageId, args.projectPath, noteReadStillVisible, noteNudgeEligibility) : null;
9970
10021
  if (deferredNoteText) {
@@ -9981,6 +10032,16 @@ async function runPostTransformPhase(args) {
9981
10032
  sessionLog2(args.sessionId, `note-nudge delivery skipped wire append: ${outcome.kind}`);
9982
10033
  }
9983
10034
  }
10035
+ if (args.fullFeatureMode && !compactionOff && replaySnapshot) {
10036
+ for (const anchor of replaySnapshot.noteNudgeAnchors) {
10037
+ appendReminderToUserMessageById(args.messages, anchor.messageId, anchor.text);
10038
+ }
10039
+ for (const decision of replaySnapshot.autoSearchHintDecisions) {
10040
+ if (decision.decision === "hint") {
10041
+ appendReminderToUserMessageById(args.messages, decision.messageId, decision.text);
10042
+ }
10043
+ }
10044
+ }
9984
10045
  let todoAnchorMovedThisPass = false;
9985
10046
  if (args.fullFeatureMode && !compactionOff) {
9986
10047
  const todoAnchorBefore = replaySnapshot ? replaySnapshot.todoSyntheticAnchor : getPersistedTodoSyntheticAnchor(args.db, args.sessionId);
@@ -11180,7 +11241,7 @@ function createPassOutcome() {
11180
11241
  }
11181
11242
 
11182
11243
  // src/hooks/magic-context/rust-mode-transform.ts
11183
- import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
11244
+ import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
11184
11245
 
11185
11246
  // src/features/magic-context/memory/single-store-embedding-drain.ts
11186
11247
  function isWatermarkRow(row) {
@@ -13627,13 +13688,235 @@ function resolveRustLkgReplayParticipant(sessionId) {
13627
13688
  return latest;
13628
13689
  return alive.length === 1 ? alive[0] : undefined;
13629
13690
  }
13630
- function noteExternalLkgReplay(participant, sessionId, inputCount) {
13691
+ function noteExternalLkgReplay(participant, sessionId, inputMessages, servedMessages) {
13631
13692
  if (!participant)
13632
13693
  return;
13633
- participant.enterFreezeFromExternalServe(sessionId, inputCount);
13694
+ participant.enterFreezeFromExternalServe(sessionId, inputMessages, servedMessages);
13634
13695
  sessionLog2(sessionId, "lkg_external_replay_froze_rust_adapter");
13635
13696
  }
13636
13697
 
13698
+ // src/hooks/magic-context/rust-served-raw.ts
13699
+ import { createHash as createHash4 } from "node:crypto";
13700
+ class RustServedRawConflict extends EmergencyFailClosedError {
13701
+ reason;
13702
+ messageIds;
13703
+ firstBlockId;
13704
+ constructor(reason, messageIds, firstBlockId) {
13705
+ super(`Rust served-raw conflict: ${reason} at ${firstBlockId}`);
13706
+ this.reason = reason;
13707
+ this.messageIds = messageIds;
13708
+ this.firstBlockId = firstBlockId;
13709
+ }
13710
+ }
13711
+ function partDigest(part) {
13712
+ const serialized = JSON.stringify(part);
13713
+ return createHash4("sha256").update(stableStringify(serialized === undefined ? undefined : JSON.parse(serialized))).digest("hex");
13714
+ }
13715
+ function records(doc) {
13716
+ if (doc.rustServedRaw === undefined)
13717
+ return [];
13718
+ if (!Array.isArray(doc.rustServedRaw))
13719
+ throw new EmergencyFailClosedError("Invalid Rust served-raw evidence");
13720
+ for (const row of doc.rustServedRaw) {
13721
+ if (!row || typeof row.messageId !== "string" || !Number.isSafeInteger(row.index) || row.index < 0 || row.partId !== undefined && typeof row.partId !== "string" || typeof row.digest !== "string" || !/^[a-f0-9]{64}$/.test(row.digest) || !Array.isArray(row.blockIds) || !row.blockIds.every((id) => typeof id === "string")) {
13722
+ throw new EmergencyFailClosedError("Invalid Rust served-raw part");
13723
+ }
13724
+ }
13725
+ return doc.rustServedRaw;
13726
+ }
13727
+ function rustRawRebuild(db, sessionId) {
13728
+ const value = readReplayEnvelope(db, sessionId).rustRawRebuild;
13729
+ if (value === undefined)
13730
+ return;
13731
+ if (!value || typeof value.identity !== "string" || !/^[a-f0-9]{64}$/.test(value.identity) || !["source_removed", "source_changed", "candidate_loss", "candidate_conflict"].includes(value.reason) || !Array.isArray(value.messageIds) || !value.messageIds.every((id) => typeof id === "string") || typeof value.firstBlockId !== "string" || typeof value.pending !== "boolean") {
13732
+ throw new EmergencyFailClosedError("Invalid Rust raw-rebuild request");
13733
+ }
13734
+ return value;
13735
+ }
13736
+ function servedRawBlockIds(db, sessionId) {
13737
+ return records(readReplayEnvelope(db, sessionId)).flatMap((row) => row.blockIds);
13738
+ }
13739
+ function sourcePart(row, message) {
13740
+ return row.partId === undefined ? message?.parts[row.index] : message?.parts.find((part) => part?.id === row.partId);
13741
+ }
13742
+ function conflict(row, reason) {
13743
+ return new RustServedRawConflict(reason, [row.messageId], row.blockIds[0] ?? `${row.messageId}#0`);
13744
+ }
13745
+ function checkRustServedRawIngress(db, sessionId, input) {
13746
+ const rows = records(readReplayEnvelope(db, sessionId));
13747
+ if (!rows.length)
13748
+ return;
13749
+ const byId = new Map(input.map((message) => [message.info.id, message]));
13750
+ const changed = rows.filter((row) => partDigest(sourcePart(row, byId.get(row.messageId))) !== row.digest);
13751
+ const first = changed[0];
13752
+ if (!first)
13753
+ return;
13754
+ throw new RustServedRawConflict(sourcePart(first, byId.get(first.messageId)) === undefined ? "source_removed" : "source_changed", [...new Set(changed.map((row) => row.messageId))], first.blockIds[0] ?? `${first.messageId}#0`);
13755
+ }
13756
+ function requestRustRawRebuild(db, sessionId, error) {
13757
+ if (!updateReplayDocument(db, sessionId, (doc) => {
13758
+ const previous = doc.rustRawRebuild;
13759
+ if (previous?.pending && previous.reason === error.reason && previous.firstBlockId === error.firstBlockId && error.messageIds.every((id) => previous.messageIds.includes(id)))
13760
+ return false;
13761
+ const messageIds = [
13762
+ ...new Set([
13763
+ ...previous?.pending ? previous.messageIds : [],
13764
+ ...error.messageIds
13765
+ ])
13766
+ ];
13767
+ const identity = partDigest([
13768
+ previous?.identity ?? "",
13769
+ error.reason,
13770
+ messageIds,
13771
+ error.firstBlockId
13772
+ ]);
13773
+ doc.version = 2;
13774
+ doc.rustRawRebuild = {
13775
+ identity,
13776
+ reason: error.reason,
13777
+ messageIds,
13778
+ firstBlockId: error.firstBlockId,
13779
+ pending: true
13780
+ };
13781
+ return true;
13782
+ }))
13783
+ throw new EmergencyFailClosedError("Cannot persist Rust raw-rebuild request");
13784
+ }
13785
+ function noteRustServedRaw(db, sessionId, input, served) {
13786
+ const additions = [];
13787
+ const byId = new Map(served.filter((message) => typeof message?.info?.id === "string").map((message) => [message.info.id, message]));
13788
+ for (const message of input) {
13789
+ const visible = byId.get(message.info.id);
13790
+ if (!visible)
13791
+ continue;
13792
+ let offset = 0;
13793
+ for (const [index, value] of message.parts.entries()) {
13794
+ if (!value || typeof value !== "object")
13795
+ continue;
13796
+ const part = value;
13797
+ const count = encodeOpenCodeMessagesToCk([{ ...message, parts: [part] }])[0].ck.content.length;
13798
+ const actual = typeof part.id === "string" ? visible.parts.find((candidate) => candidate?.id === part.id) : visible.parts[index];
13799
+ const status = part.state?.status;
13800
+ const reachesProvider = !(part.type === "text" && part.ignored === true) && ![
13801
+ "compaction",
13802
+ "step-start",
13803
+ "step-finish",
13804
+ "snapshot",
13805
+ "patch",
13806
+ "agent",
13807
+ "retry"
13808
+ ].includes(String(part.type)) && (part.type !== "tool" || status === "completed" || status === "error");
13809
+ if (reachesProvider && count > 0) {
13810
+ const digest = partDigest(part);
13811
+ if (actual === part || partDigest(actual) === digest) {
13812
+ additions.push({
13813
+ messageId: message.info.id,
13814
+ index,
13815
+ ...typeof part.id === "string" ? { partId: part.id } : {},
13816
+ digest,
13817
+ blockIds: Array.from({ length: count }, (_, i) => `${message.info.id}#${offset + i}`)
13818
+ });
13819
+ }
13820
+ }
13821
+ offset += count;
13822
+ }
13823
+ }
13824
+ if (!additions.length)
13825
+ return;
13826
+ if (!updateReplayDocument(db, sessionId, (doc) => {
13827
+ const rows = records(doc);
13828
+ const key = (row) => stableStringify([row.messageId, row.partId ?? row.index]);
13829
+ const merged = new Map(rows.map((row) => [key(row), row]));
13830
+ for (const row of additions)
13831
+ merged.set(key(row), row);
13832
+ const next = [...merged.values()];
13833
+ if (stableStringify(rows) === stableStringify(next))
13834
+ return false;
13835
+ doc.version = 2;
13836
+ doc.rustServedRaw = next;
13837
+ return true;
13838
+ }))
13839
+ throw new EmergencyFailClosedError("Cannot persist Rust served-raw evidence");
13840
+ }
13841
+ function preserveRustServedRaw(db, sessionId, output, input = output, reconstruct = new Set, covered = new Set) {
13842
+ const rows = records(readReplayEnvelope(db, sessionId));
13843
+ if (!rows.length)
13844
+ return false;
13845
+ const sources = new Map(input.map((message) => [message.info.id, message]));
13846
+ const byId = new Map(output.map((message) => [message.info.id, message]));
13847
+ let repaired = false;
13848
+ for (const row of rows) {
13849
+ if (covered.has(row.messageId))
13850
+ continue;
13851
+ const original = sources.get(row.messageId);
13852
+ const source = sourcePart(row, original);
13853
+ if (!original || !source)
13854
+ throw conflict(row, "source_removed");
13855
+ if (partDigest(source) !== row.digest)
13856
+ throw conflict(row, "source_changed");
13857
+ let message = byId.get(row.messageId);
13858
+ if (!message) {
13859
+ if (!reconstruct.has(row.messageId))
13860
+ throw conflict(row, "candidate_loss");
13861
+ const sourceIndex = input.indexOf(original);
13862
+ const next = output.findIndex((candidate) => input.findIndex((entry) => entry.info.id === candidate.info.id) > sourceIndex);
13863
+ message = structuredClone(original);
13864
+ output.splice(next < 0 ? output.length : next, 0, message);
13865
+ byId.set(row.messageId, message);
13866
+ repaired = true;
13867
+ }
13868
+ const parts = message.parts;
13869
+ if (row.partId === undefined) {
13870
+ const candidate = parts[row.index];
13871
+ if (partDigest(candidate) === row.digest)
13872
+ continue;
13873
+ const reversibleTag = source.type === "text" && typeof source.text === "string" && typeof candidate?.text === "string" && /^§\d+§ /.test(candidate.text) && candidate.text.replace(/^§\d+§ /, "") === source.text && partDigest({ ...candidate, text: source.text }) === row.digest;
13874
+ if (!reversibleTag) {
13875
+ if (!reconstruct.has(row.messageId))
13876
+ throw conflict(row, "candidate_conflict");
13877
+ message.parts = structuredClone(original.parts);
13878
+ repaired = true;
13879
+ continue;
13880
+ }
13881
+ parts[row.index] = structuredClone(source);
13882
+ continue;
13883
+ }
13884
+ const matches = parts.flatMap((part, i) => part.id === row.partId ? [i] : []);
13885
+ if (matches.length > 1)
13886
+ throw conflict(row, "candidate_conflict");
13887
+ const index = matches[0];
13888
+ if (index !== undefined) {
13889
+ if (partDigest(parts[index]) !== row.digest)
13890
+ parts[index] = structuredClone(source);
13891
+ } else if (["reasoning", "thinking", "redacted_thinking"].includes(String(source.type)) || reconstruct.has(row.messageId)) {
13892
+ parts.splice(row.index, 0, structuredClone(source));
13893
+ repaired = true;
13894
+ } else
13895
+ throw conflict(row, "candidate_loss");
13896
+ }
13897
+ return repaired;
13898
+ }
13899
+ function releaseRustServedRaw(db, sessionId, output, rebuiltIdentity) {
13900
+ const snapshot = readReplayEnvelope(db, sessionId);
13901
+ if (!records(snapshot).length && !snapshot.rustRawRebuild?.pending)
13902
+ return;
13903
+ const byId = new Map(output.map((message) => [message.info.id, message]));
13904
+ if (!updateReplayDocument(db, sessionId, (doc) => {
13905
+ const rows = records(doc);
13906
+ const kept = rows.filter((row) => partDigest(sourcePart(row, byId.get(row.messageId))) === row.digest);
13907
+ const recovery = doc.rustRawRebuild;
13908
+ const acknowledged = recovery?.pending && recovery.identity === rebuiltIdentity;
13909
+ if (kept.length === rows.length && !acknowledged)
13910
+ return false;
13911
+ doc.version = 2;
13912
+ doc.rustServedRaw = kept;
13913
+ if (acknowledged)
13914
+ doc.rustRawRebuild = { ...recovery, pending: false };
13915
+ return true;
13916
+ }))
13917
+ throw new EmergencyFailClosedError("Cannot release Rust served-raw evidence");
13918
+ }
13919
+
13637
13920
  // src/hooks/magic-context/transform-context-state.ts
13638
13921
  function loadPersistedUsageWatermark(db, sessionId) {
13639
13922
  const result = db.prepare("SELECT last_response_time FROM session_meta WHERE session_id = ?").get(sessionId);
@@ -13912,10 +14195,10 @@ function messageCacheSignature(message) {
13912
14195
  const parts = Array.isArray(message.parts) ? message.parts : [];
13913
14196
  const serializedParts = JSON.stringify(parts) ?? "null";
13914
14197
  const serializedMessage = JSON.stringify(message) ?? "null";
13915
- return `${messageIdOf(message) ?? ""}:${parts.length}:${Buffer.byteLength(serializedParts)}:${createHash4("sha256").update(serializedMessage).digest("hex")}`;
14198
+ return `${messageIdOf(message) ?? ""}:${parts.length}:${Buffer.byteLength(serializedParts)}:${createHash5("sha256").update(serializedMessage).digest("hex")}`;
13916
14199
  }
13917
14200
  function advanceWireFingerprint(previous, encoded) {
13918
- return createHash4("sha256").update(previous).update("\\0").update(JSON.stringify(encoded) ?? "null").digest("hex");
14201
+ return createHash5("sha256").update(previous).update("\\0").update(JSON.stringify(encoded) ?? "null").digest("hex");
13919
14202
  }
13920
14203
  function buildWireFingerprint(encoded) {
13921
14204
  let fingerprint = "rust-wire-v1";
@@ -14445,7 +14728,8 @@ function buildTransformBody(args) {
14445
14728
  args.providerId ? `provider:${args.providerId}` : "",
14446
14729
  args.modelKey ? `model:${args.modelKey}` : "",
14447
14730
  args.variant ? `variant:${args.variant}` : "",
14448
- args.systemPromptHash ? `system:${args.systemPromptHash}` : ""
14731
+ args.systemPromptHash ? `system:${args.systemPromptHash}` : "",
14732
+ args.rawRebuild ? `raw_replay:${args.rawRebuild.identity}` : ""
14449
14733
  ].filter(Boolean).join("|"),
14450
14734
  system_prompt_hash: args.systemPromptHash,
14451
14735
  adopted_system_prompt_hash: args.passInputs.adopted_system_prompt_hash,
@@ -14453,6 +14737,14 @@ function buildTransformBody(args) {
14453
14737
  is_subagent: args.passInputs.is_subagent === true,
14454
14738
  messages: args.input,
14455
14739
  native_messages: args.nativeMessages,
14740
+ served_raw_block_ids: args.servedRawBlockIds ?? [],
14741
+ ...args.rawRebuild?.pending ? {
14742
+ raw_replay_rebuild: {
14743
+ identity: args.rawRebuild.identity,
14744
+ reason: args.rawRebuild.reason,
14745
+ message_ids: args.rawRebuild.messageIds
14746
+ }
14747
+ } : {},
14456
14748
  tool_input_key_orders: args.toolInputKeyOrders ?? toolInputKeyOrders(args.input),
14457
14749
  ...args.fullArrayFingerprint ? { full_array_fingerprint: args.fullArrayFingerprint } : {},
14458
14750
  ...args.tailDelta ? {
@@ -14643,6 +14935,7 @@ function createRustModeTransform(deps, options) {
14643
14935
  throw error;
14644
14936
  } finally {
14645
14937
  clock.clearTimeout(timer);
14938
+ invalidateReplayEnvelopeSnapshot(deps.db, args.sessionId);
14646
14939
  }
14647
14940
  };
14648
14941
  const callTransformWithStallProbe = async (args, attemptTimeoutMs) => {
@@ -14751,6 +15044,24 @@ function createRustModeTransform(deps, options) {
14751
15044
  logTransformTiming(sessionId, "rust.ordinal_rebuild", startedAt, detail);
14752
15045
  }
14753
15046
  };
15047
+ const recordRawConflict = (sessionId, error) => {
15048
+ requestRustRawRebuild(deps.db, sessionId, error);
15049
+ dropSlot(sessionId, `raw_replay_${error.reason}`);
15050
+ const state = ensureState(states, sessionId);
15051
+ state.forceFullWire = true;
15052
+ state.parked = false;
15053
+ sessionLog2(sessionId, `raw_replay_rebuild_requested reason=${error.reason} first_changed=${error.firstBlockId}`);
15054
+ };
15055
+ const observeRawReplay = (sessionId, input, served) => {
15056
+ try {
15057
+ checkRustServedRawIngress(deps.db, sessionId, input);
15058
+ noteRustServedRaw(deps.db, sessionId, input, served);
15059
+ } catch (error) {
15060
+ if (error instanceof RustServedRawConflict)
15061
+ recordRawConflict(sessionId, error);
15062
+ throw error;
15063
+ }
15064
+ };
14754
15065
  const replayLastGood = (sessionId, currentMessages, output, systemPromptTokens) => {
14755
15066
  try {
14756
15067
  if (states.get(sessionId)?.markerAdmissionFenced || isRustMarkerAdmissionFenced(deps.db, sessionId))
@@ -14837,6 +15148,7 @@ function createRustModeTransform(deps, options) {
14837
15148
  ownDirtyRevision: lkgDirtyRevision(sessionId)
14838
15149
  }));
14839
15150
  const replayInputCount = currentMessages.length;
15151
+ observeRawReplay(sessionId, currentMessages, replay.messages);
14840
15152
  replaceMessagesInPlace(output, replay.messages);
14841
15153
  ensureState(states, sessionId).lkgLatestServedDigest = lkgServedArrayDigest(JSON.stringify(replay.messages));
14842
15154
  enterLkgReplayFreeze(ensureState(states, sessionId), replayInputCount);
@@ -14900,7 +15212,10 @@ function createRustModeTransform(deps, options) {
14900
15212
  const passStampBySession = new Map;
14901
15213
  const replayParticipant = {
14902
15214
  lastPassStamp: (sessionId) => states.has(sessionId) ? passStampBySession.get(sessionId) : undefined,
14903
- enterFreezeFromExternalServe: (sessionId, inputCount) => enterLkgReplayFreeze(ensureState(states, sessionId), inputCount),
15215
+ enterFreezeFromExternalServe: (sessionId, inputMessages, servedMessages) => {
15216
+ observeRawReplay(sessionId, inputMessages, servedMessages);
15217
+ enterLkgReplayFreeze(ensureState(states, sessionId), inputMessages.length);
15218
+ },
14904
15219
  replayFits: (sessionId, messages, inputMessages) => {
14905
15220
  try {
14906
15221
  const fit = lkgReplayFits({
@@ -15046,6 +15361,24 @@ function createRustModeTransform(deps, options) {
15046
15361
  bindSystemDateRequest(output, dateRequest);
15047
15362
  const passObservedAtMs = Date.now();
15048
15363
  const state = ensureState(states, sessionId);
15364
+ let rawRebuild;
15365
+ let rawReadError;
15366
+ try {
15367
+ rawRebuild = rustRawRebuild(deps.db, sessionId);
15368
+ try {
15369
+ checkRustServedRawIngress(deps.db, sessionId, messages);
15370
+ } catch (error) {
15371
+ if (!(error instanceof RustServedRawConflict) || !rawRebuild?.pending)
15372
+ throw error;
15373
+ requestRustRawRebuild(deps.db, sessionId, error);
15374
+ rawRebuild = rustRawRebuild(deps.db, sessionId);
15375
+ }
15376
+ } catch (error) {
15377
+ if (error instanceof RustServedRawConflict)
15378
+ throw error;
15379
+ rawReadError = error;
15380
+ }
15381
+ const reconstructRaw = new Set(rawRebuild?.messageIds ?? []);
15049
15382
  let entrySlot = getSlot(sessionId);
15050
15383
  try {
15051
15384
  entrySlot = loadPersistedLkgSlot(deps.db, sessionId, true) ?? entrySlot;
@@ -15084,6 +15417,7 @@ function createRustModeTransform(deps, options) {
15084
15417
  let decision = "error";
15085
15418
  let materializeReason = "none";
15086
15419
  let frozenReleaseReason = null;
15420
+ let frozenReleaseFirstChanged = "unknown";
15087
15421
  let schedulerDecision;
15088
15422
  let responseCommitted;
15089
15423
  let responsePrefixBustPermitted;
@@ -15109,7 +15443,7 @@ function createRustModeTransform(deps, options) {
15109
15443
  let recoveryProjectRoot = options.projectRoot ?? deps.directory ?? "";
15110
15444
  const passUsageSnapshot = loadContextUsage(deps.contextUsageMap, deps.db, sessionId);
15111
15445
  requestInputTokens = Math.max(0, Math.floor(passUsageSnapshot.inputTokens));
15112
- let preflightError;
15446
+ let preflightError = rawReadError;
15113
15447
  let markerAdmissionRecovery = state.markerAdmissionFenced;
15114
15448
  try {
15115
15449
  markerAdmissionRecovery ||= isRustMarkerAdmissionFenced(deps.db, sessionId);
@@ -15303,7 +15637,7 @@ function createRustModeTransform(deps, options) {
15303
15637
  decision,
15304
15638
  committed: responseCommitted,
15305
15639
  prefixBustPermitted: responsePrefixBustPermitted,
15306
- reason: materializeReason,
15640
+ reason: applied && served && frozenReleaseReason ? `lkg_frozen_release:${frozenReleaseReason};first_changed=${frozenReleaseFirstChanged};materialize_reason=${materializeReason}` : materializeReason,
15307
15641
  schedulerDecision,
15308
15642
  schedulerDeferReason,
15309
15643
  historianNoFire,
@@ -15325,7 +15659,7 @@ function createRustModeTransform(deps, options) {
15325
15659
  materializeReason: materializeReason === "none" ? null : materializeReason,
15326
15660
  inputTokens: requestInputTokens,
15327
15661
  tsMs: passObservedAtMs,
15328
- lkgFrozenReleaseReason: applied && served ? frozenReleaseReason ?? undefined : undefined
15662
+ lkgFrozenReleaseReason: applied && served && frozenReleaseReason ? `${frozenReleaseReason};first_changed=${frozenReleaseFirstChanged}` : undefined
15329
15663
  });
15330
15664
  }
15331
15665
  };
@@ -15360,7 +15694,7 @@ function createRustModeTransform(deps, options) {
15360
15694
  sessionLog2(sessionId, `rust module stages (slow pass): ${detail}`);
15361
15695
  }
15362
15696
  };
15363
- if (state.parked) {
15697
+ if (state.parked && !preflightError) {
15364
15698
  state.passesSincePark += 1;
15365
15699
  if (!emergencyFailClosed && passUsageSnapshot.percentage < RUST_PARK_PROBE_PRESSURE_BYPASS_PCT && state.passCount % RUST_PARK_RETRY_INTERVAL !== 0) {
15366
15700
  decision = "parked";
@@ -15645,7 +15979,7 @@ function createRustModeTransform(deps, options) {
15645
15979
  const id = messageIdOf(message) ?? `index:${todoStart + offset}`;
15646
15980
  const parts = message.parts?.filter((part) => isRecord4(part) && part.type === "tool" && part.tool === "todowrite");
15647
15981
  if (parts?.length) {
15648
- todoCalls.set(id, createHash4("sha256").update(JSON.stringify(parts)).digest("hex"));
15982
+ todoCalls.set(id, createHash5("sha256").update(JSON.stringify(parts)).digest("hex"));
15649
15983
  } else {
15650
15984
  todoCalls.delete(id);
15651
15985
  }
@@ -15791,6 +16125,8 @@ function createRustModeTransform(deps, options) {
15791
16125
  };
15792
16126
  let body = buildTransformBody({
15793
16127
  sessionId,
16128
+ rawRebuild,
16129
+ servedRawBlockIds: servedRawBlockIds(deps.db, sessionId),
15794
16130
  input: encodedInput,
15795
16131
  nativeMessages: wireDelta ? messages.slice(wireDelta.rawStart) : messages,
15796
16132
  toolInputKeyOrders: toolInputKeyOrders(encodedInput),
@@ -15990,6 +16326,8 @@ function createRustModeTransform(deps, options) {
15990
16326
  const retryWireBuildStartedAt = performance.now();
15991
16327
  body = buildTransformBody({
15992
16328
  sessionId,
16329
+ rawRebuild,
16330
+ servedRawBlockIds: servedRawBlockIds(deps.db, sessionId),
15993
16331
  input: retryEncodedInput,
15994
16332
  nativeMessages: messages,
15995
16333
  toolInputKeyOrders: toolInputKeyOrders(retryEncodedInput),
@@ -16049,6 +16387,13 @@ function createRustModeTransform(deps, options) {
16049
16387
  sessionLog2(sessionId, `signed thinking reset fence: ${fenced.length} assistant(s) [${fenced.join(",")}]`);
16050
16388
  }
16051
16389
  const cacheBustingPass = moduleDecisionBusts;
16390
+ if (rawRebuild?.pending && !moduleDecisionBusts) {
16391
+ throw new EmergencyFailClosedError("Rust raw-rebuild request was not authorized by the module");
16392
+ }
16393
+ if (rawRebuild?.pending) {
16394
+ frozenReleaseReason = `raw_replay_${rawRebuild.reason}`;
16395
+ frozenReleaseFirstChanged = rawRebuild.firstBlockId;
16396
+ }
16052
16397
  let frozenReplayReleased = false;
16053
16398
  if (markerAdmissionRecovery && !moduleDecisionBusts)
16054
16399
  throw new RustTransformProtocolError("rust marker admission recovery: supported prefix_bust_permitted=true required; upgrade ck-mc if unsupported");
@@ -16080,6 +16425,7 @@ function createRustModeTransform(deps, options) {
16080
16425
  let appliedMessages = moduleMessages;
16081
16426
  let markerStrategyFailure;
16082
16427
  let replayedFrozenRepresentation = false;
16428
+ let reconstructedRaw = false;
16083
16429
  const boundaryId = response.boundary_id;
16084
16430
  if (typeof boundaryId === "string" && boundaryId.length > 0) {
16085
16431
  assertNativeBoundary(moduleMessages, sessionId, boundaryId);
@@ -16124,7 +16470,7 @@ function createRustModeTransform(deps, options) {
16124
16470
  }
16125
16471
  if (recoveryFenceRebuild) {
16126
16472
  frozenReplayReleased = true;
16127
- frozenReleaseReason = "lkg_recovery_fence_unproven";
16473
+ frozenReleaseReason ??= "lkg_recovery_fence_unproven";
16128
16474
  frozenReleaseLastServed = { messages: null, proven: false };
16129
16475
  }
16130
16476
  if (state.lkgRepresentationFrozen && !shouldAdoptModuleAfterFreeze(moduleDecisionBusts, frozenReplayReleased)) {
@@ -16189,6 +16535,10 @@ function createRustModeTransform(deps, options) {
16189
16535
  if (!replayedFrozenRepresentation) {
16190
16536
  const outputCloneStartedAt = performance.now();
16191
16537
  appliedMessages = cloneModuleNativeOutput(moduleMessages);
16538
+ if (!moduleDecisionBusts || rawRebuild?.pending && rawRebuild.reason.startsWith("candidate")) {
16539
+ const covered = moduleDecisionBusts && typeof response.coverage_ordinal === "number" ? new Set([...state.idOrdinalMemo].filter(([, ordinal]) => ordinal <= Number(response.coverage_ordinal)).map(([id]) => id)) : new Set;
16540
+ reconstructedRaw = preserveRustServedRaw(deps.db, sessionId, appliedMessages, messages, reconstructRaw, covered);
16541
+ }
16192
16542
  timings.outputClone += performance.now() - outputCloneStartedAt;
16193
16543
  }
16194
16544
  pendingWireCache.nativeOutput = moduleMessages;
@@ -16247,7 +16597,7 @@ function createRustModeTransform(deps, options) {
16247
16597
  prefixPermissionSupported: permissionSupported,
16248
16598
  moduleReasoningTrimOnly: response.reasoning_trim_only === true,
16249
16599
  moduleSignedThinkingReset: response.signed_thinking_reset === true,
16250
- ...permissionSupported && (passStartedFrozen || frozenReleaseReason || coldStartLastServed) ? { frozenReleaseLastServed } : {},
16600
+ ...moduleDecisionBusts && (passStartedFrozen || frozenReleaseReason || coldStartLastServed) ? { frozenReleaseLastServed } : {},
16251
16601
  trailingBlankSourceDecisions,
16252
16602
  trailingBlankNewestAssistantId: typeof trailingBlankNewestAssistantId === "string" ? trailingBlankNewestAssistantId : undefined,
16253
16603
  tagger: deps.tagger,
@@ -16258,6 +16608,23 @@ function createRustModeTransform(deps, options) {
16258
16608
  if (markerStrategyFailure)
16259
16609
  throw markerStrategyFailure;
16260
16610
  }
16611
+ if (!moduleDecisionBusts && !replayedFrozenRepresentation) {
16612
+ reconstructedRaw = preserveRustServedRaw(deps.db, sessionId, appliedMessages, messages, reconstructRaw) || reconstructedRaw;
16613
+ replayRustModeBindingMismatchStrips({
16614
+ db: deps.db,
16615
+ sessionId,
16616
+ messages: appliedMessages,
16617
+ resolvedProviderID: model?.providerID
16618
+ });
16619
+ if (servedRawBlockIds(deps.db, sessionId).length > 0 && (!validateLkgSeam([], appliedMessages, model?.providerID ?? null) || model?.providerID === "anthropic" && !validateAnthropicReasoningRuns(appliedMessages.map((message) => ({
16620
+ ...message,
16621
+ parts: message.parts.filter((part) => !isRecord4(part) || !["text", "reasoning"].includes(String(part.type)) || part.text !== "")
16622
+ }))))) {
16623
+ throw new RustServedRawConflict("candidate_conflict", [
16624
+ ...new Set(servedRawBlockIds(deps.db, sessionId).map((id) => id.replace(/#\d+$/, "")))
16625
+ ], servedRawBlockIds(deps.db, sessionId)[0] ?? "unknown");
16626
+ }
16627
+ }
16261
16628
  if (typeof boundaryId === "string" && boundaryId.length > 0) {
16262
16629
  assertNativeBoundary(appliedMessages, sessionId, boundaryId);
16263
16630
  }
@@ -16293,6 +16660,24 @@ function createRustModeTransform(deps, options) {
16293
16660
  }
16294
16661
  }
16295
16662
  logStage(sessionId, "apply", applyStartedAt, timings);
16663
+ if (frozenReleaseReason && frozenReleaseLastServed.messages && [...frozenReleaseLastServed.messages, ...appliedMessages].every((message) => isRecord4(message) && isRecord4(message.info) && Array.isArray(message.parts))) {
16664
+ frozenReleaseFirstChanged = "none";
16665
+ const byId = new Map(appliedMessages.map((message) => [message.info.id, message]));
16666
+ for (const before of frozenReleaseLastServed.messages) {
16667
+ const after = byId.get(before.info.id);
16668
+ const index = before.parts.findIndex((part, i) => JSON.stringify(part) !== JSON.stringify(after?.parts[i]));
16669
+ if (!after || index >= 0) {
16670
+ const blockIndex = index > 0 ? encodeOpenCodeMessagesToCk([
16671
+ { ...before, parts: before.parts.slice(0, index) }
16672
+ ])[0].ck.content.length : 0;
16673
+ frozenReleaseFirstChanged = `${before.info.id}#${blockIndex}`;
16674
+ break;
16675
+ }
16676
+ }
16677
+ }
16678
+ if (replayedFrozenRepresentation || reconstructedRaw) {
16679
+ noteRustServedRaw(deps.db, sessionId, messages, appliedMessages);
16680
+ }
16296
16681
  const lkgInputIds = messages.map((message) => message.info.id);
16297
16682
  const lkgInputKeys = resolveLkgModelKeys(messages);
16298
16683
  const applyReplaceStartedAt = performance.now();
@@ -16550,6 +16935,9 @@ function createRustModeTransform(deps, options) {
16550
16935
  }
16551
16936
  }
16552
16937
  }
16938
+ if (moduleDecisionBusts) {
16939
+ releaseRustServedRaw(deps.db, sessionId, output.messages, rawRebuild?.identity);
16940
+ }
16553
16941
  } catch (error) {
16554
16942
  if (markerDefinitelyNoCut) {
16555
16943
  try {
@@ -16687,7 +17075,12 @@ function createRustModeTransform(deps, options) {
16687
17075
  const adapter = {
16688
17076
  run: async (sessionId, messages, output, sessionMeta) => {
16689
17077
  try {
16690
- await withSqliteTransformPass2(() => run(sessionId, messages, output, sessionMeta));
17078
+ await withReplayEnvelopeSnapshot(deps.db, sessionId, () => withSqliteTransformPass2(() => run(sessionId, messages, output, sessionMeta)));
17079
+ } catch (error) {
17080
+ if (error instanceof RustServedRawConflict) {
17081
+ recordRawConflict(sessionId, error);
17082
+ }
17083
+ throw error;
16691
17084
  } finally {
16692
17085
  try {
16693
17086
  withoutSqliteTransformPass2(() => resolveHostRunner()?.pump(sessionId));
@@ -18603,6 +18996,114 @@ function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, execu
18603
18996
  }) / 100) * historyBudgetPercentage);
18604
18997
  }
18605
18998
 
18999
+ // src/shared/recovery-reasoning-prefix.ts
19000
+ function recoveryReasoningPrefixes(messages, partsKey) {
19001
+ const prefixes = [];
19002
+ for (let index = 0;index < messages.length; index++) {
19003
+ const message = messages[index];
19004
+ const parts = message?.[partsKey];
19005
+ if (!Array.isArray(parts))
19006
+ continue;
19007
+ for (let p = 0;p < parts.length; p++) {
19008
+ const type = parts[p]?.type;
19009
+ if (type !== "thinking" && type !== "reasoning" && type !== "redactedThinking" && type !== "redacted_thinking")
19010
+ continue;
19011
+ prefixes.push(JSON.stringify([
19012
+ ...messages.slice(0, index),
19013
+ { ...message, [partsKey]: parts.slice(0, p + 1) }
19014
+ ]));
19015
+ }
19016
+ }
19017
+ return prefixes;
19018
+ }
19019
+
19020
+ // src/hooks/magic-context/replay-committed-drops.ts
19021
+ function replayCommittedDrops(db, sessionId, messages) {
19022
+ const original = messages;
19023
+ const reasoningPrefixes = recoveryReasoningPrefixes(messages, "parts");
19024
+ if (reasoningPrefixes.length)
19025
+ messages = structuredClone(messages);
19026
+ const tags = getTagsBySession2(db, sessionId).filter((tag) => tag.status === "dropped");
19027
+ const toolTags = new Map(tags.filter((tag) => tag.type === "tool").map((tag) => [makeToolCompositeKey(tag.toolOwnerMessageId ?? "", tag.messageId), tag]));
19028
+ const textTags = new Map(tags.filter((tag) => tag.type !== "tool").map((tag) => [tag.messageId, tag]));
19029
+ const byNumber = new Map(tags.map((tag) => [tag.tagNumber, tag]));
19030
+ const index = new Map;
19031
+ const owners = new Map;
19032
+ const resolvedOwners = new Map;
19033
+ const ambiguousCalls = new Set;
19034
+ const targets = new Map;
19035
+ let precedingThinking = [];
19036
+ const toolThinking = new Map;
19037
+ const batch = new ToolMutationBatch(messages);
19038
+ for (const message of messages) {
19039
+ if (message.info.role === "user")
19040
+ precedingThinking = [];
19041
+ const thinking = message.parts.filter(isThinkingPart);
19042
+ for (let p = 0;p < message.parts.length; p++) {
19043
+ const part = message.parts[p];
19044
+ const observation = extractToolCallObservation(part);
19045
+ if (observation) {
19046
+ const { callId, kind } = observation;
19047
+ const pending = owners.get(callId) ?? [];
19048
+ if (pending.length > 1)
19049
+ ambiguousCalls.add(callId);
19050
+ const hostingKey = makeToolCompositeKey(message.info.id ?? "", callId);
19051
+ const owner = kind === "invocation" || part.type === "tool" || toolTags.has(hostingKey) ? message.info.id ?? "" : pending.shift() ?? resolvedOwners.get(callId) ?? message.info.id ?? "";
19052
+ if (kind === "invocation")
19053
+ pending.push(owner);
19054
+ else
19055
+ resolvedOwners.set(callId, owner);
19056
+ owners.set(callId, pending);
19057
+ const key = makeToolCompositeKey(owner, callId);
19058
+ if (message.info.role === "tool" && !toolThinking.has(key))
19059
+ toolThinking.set(key, precedingThinking);
19060
+ const entry = index.get(key) ?? { occurrences: [], hasResult: false };
19061
+ entry.occurrences.push({ message, part, kind });
19062
+ entry.hasResult ||= kind === "result" && partHasCompletedResult(part);
19063
+ index.set(key, entry);
19064
+ } else {
19065
+ const prefix = isTextPart(part) ? /^§(\d+)§\s/.exec(part.text) : null;
19066
+ const tag = (prefix ? byNumber.get(Number(prefix[1])) : undefined) ?? textTags.get(`${message.info.id}:p${p}`) ?? textTags.get(`${message.info.id}:file${p}`);
19067
+ if (tag && tag.type !== "tool")
19068
+ targets.set(tag.tagNumber, {
19069
+ message,
19070
+ setContent(content) {
19071
+ if (isTextPart(part)) {
19072
+ if (part.text === content)
19073
+ return false;
19074
+ part.text = content;
19075
+ } else
19076
+ message.parts[p] = { type: "text", text: content };
19077
+ return true;
19078
+ }
19079
+ });
19080
+ }
19081
+ }
19082
+ if (message.info.role === "assistant" && !message.parts.some(isTextPart))
19083
+ precedingThinking = thinking;
19084
+ }
19085
+ const arcsByCall = new Map;
19086
+ for (const entry of index.values()) {
19087
+ const callId = extractToolCallObservation(entry.occurrences[0]?.part)?.callId ?? "";
19088
+ arcsByCall.set(callId, (arcsByCall.get(callId) ?? 0) + 1);
19089
+ }
19090
+ for (const [key, entry] of index) {
19091
+ const callId = extractToolCallObservation(entry.occurrences[0]?.part)?.callId ?? "";
19092
+ if (ambiguousCalls.has(callId))
19093
+ continue;
19094
+ const tag = toolTags.get(key) ?? (arcsByCall.get(callId) === 1 ? toolTags.get(makeToolCompositeKey("", callId)) : undefined);
19095
+ if (tag)
19096
+ targets.set(tag.tagNumber, createToolDropTarget(key, toolThinking.get(key) ?? [], index, batch, tag.tagNumber));
19097
+ }
19098
+ applyFlushedStatuses(sessionId, db, targets, tags);
19099
+ batch.finalize();
19100
+ if (JSON.stringify(recoveryReasoningPrefixes(messages, "parts")) !== JSON.stringify(reasoningPrefixes))
19101
+ return false;
19102
+ if (messages !== original)
19103
+ original.splice(0, original.length, ...messages);
19104
+ return true;
19105
+ }
19106
+
18606
19107
  // src/plugin/messages-transform.ts
18607
19108
  var ASSISTANT_TERMINAL_RETRY_MESSAGE = "The conversation ends with a completed assistant response and cannot be resubmitted as-is — send a new message to continue.";
18608
19109
 
@@ -18745,7 +19246,6 @@ function tryMessagesTransformLkgReplay(args) {
18745
19246
  sessionLog2(sessionId, "lkg_emergency_armed");
18746
19247
  } else {
18747
19248
  const inputMessages = output.messages;
18748
- const inputCount = inputMessages.length;
18749
19249
  const keys = resolveLkgModelKeys(inputMessages);
18750
19250
  const replay = replayLkg({
18751
19251
  sessionId,
@@ -18764,6 +19264,10 @@ function tryMessagesTransformLkgReplay(args) {
18764
19264
  sessionLog2(sessionId, "lkg_restart_rebuild_required");
18765
19265
  return false;
18766
19266
  }
19267
+ if (replay.ok && !rust && !replayCommittedDrops(db, sessionId, replay.messages)) {
19268
+ sessionLog2(sessionId, "lkg_reasoning_prefix_changed");
19269
+ return false;
19270
+ }
18767
19271
  let tsFit = true;
18768
19272
  if (replay.ok && !rust && keys.providerKey && keys.modelKey) {
18769
19273
  const model = {
@@ -18813,8 +19317,8 @@ function tryMessagesTransformLkgReplay(args) {
18813
19317
  return false;
18814
19318
  retirePendingSystemPromptDelivery(sessionId);
18815
19319
  args.onLkgReplay?.();
19320
+ noteExternalLkgReplay(rust, sessionId, inputMessages, replay.messages);
18816
19321
  replaceMessagesInPlace2(output, replay.messages);
18817
- noteExternalLkgReplay(rust, sessionId, inputCount);
18818
19322
  sessionLog2(sessionId, "lkg_replay_served");
18819
19323
  return true;
18820
19324
  } else {
@@ -20124,7 +20628,7 @@ function computeOpenCodeWorkMetricsIncremental(openCodeDb, sessionId, carry) {
20124
20628
  import { readFileSync, statSync } from "node:fs";
20125
20629
 
20126
20630
  // src/plugin/embedding-bootstrap-helpers.ts
20127
- import { createHash as createHash5 } from "node:crypto";
20631
+ import { createHash as createHash6 } from "node:crypto";
20128
20632
  var EMBEDDING_AFFECTING_KEYS = new Set([
20129
20633
  "embedding.api_key",
20130
20634
  "embedding.endpoint",
@@ -20172,7 +20676,7 @@ var EMBEDDING_WARNING_TERMS = [
20172
20676
  ];
20173
20677
  var loggedFailureSignatures = new Map;
20174
20678
  function sha256Prefix(value, length = 16) {
20175
- return createHash5("sha256").update(value).digest("hex").slice(0, length);
20679
+ return createHash6("sha256").update(value).digest("hex").slice(0, length);
20176
20680
  }
20177
20681
  function warningLooksEmbeddingRelated(message) {
20178
20682
  const lower = message.toLowerCase();
@@ -21662,8 +22166,8 @@ function registerRpcHandlers(rpcServer, args) {
21662
22166
  const db = readDatabase();
21663
22167
  if (!db)
21664
22168
  return { ok: false, error: "db unavailable" };
21665
- const { runManagedRecomp } = await importPluginModule(() => import("./recomp-orchestrator-v64r4s4r.js"));
21666
- const { sendIgnoredMessage } = await importPluginModule(() => import("./send-session-notification-5aj2j7vf.js"));
22169
+ const { runManagedRecomp } = await importPluginModule(() => import("./recomp-orchestrator-n11zv1f1.js"));
22170
+ const { sendIgnoredMessage } = await importPluginModule(() => import("./send-session-notification-ppgc8qdz.js"));
21667
22171
  log2(`[rpc] recomp requested for session ${sessionId}`);
21668
22172
  if (!args.client && !args.hiddenCompletionExecutor) {
21669
22173
  return {
@@ -21732,7 +22236,7 @@ function registerRpcHandlers(rpcServer, args) {
21732
22236
  const db = readDatabase();
21733
22237
  if (!db)
21734
22238
  return { ok: false, error: "db unavailable" };
21735
- const { runManagedWrapup } = await importPluginModule(() => import("./wrapup-orchestrator-cahk37n7.js"));
22239
+ const { runManagedWrapup } = await importPluginModule(() => import("./wrapup-orchestrator-s92ebfzv.js"));
21736
22240
  const model = liveSessionState.liveModelBySession.get(sessionId);
21737
22241
  const contextLimit = model ? resolveContextLimit(model.providerID, model.modelID, { db, sessionID: sessionId }) : 128000;
21738
22242
  const ctx = {
@@ -22721,8 +23225,8 @@ function hostMediaFromEncoded(value) {
22721
23225
  import { randomUUID as randomUUID4 } from "node:crypto";
22722
23226
 
22723
23227
  // src/v2/fold/owner.ts
22724
- import { createHash as createHash6 } from "node:crypto";
22725
- var foldDigest = (value) => createHash6("sha256").update(value).digest("hex");
23228
+ import { createHash as createHash7 } from "node:crypto";
23229
+ var foldDigest = (value) => createHash7("sha256").update(value).digest("hex");
22726
23230
 
22727
23231
  class FoldOwner {
22728
23232
  storage;
@@ -23063,12 +23567,12 @@ function placeCacheHint(content, hint) {
23063
23567
  const index = lastText >= 0 ? lastText : content.length - 1;
23064
23568
  content[index] = { ...content[index], cache: hint };
23065
23569
  }
23066
- function removeEmptyTextParts(content, rendered) {
23570
+ function removeEmptyTextParts(content, rendered, mayRemove) {
23067
23571
  let hint;
23068
23572
  let removed = 0;
23069
23573
  for (let index = content.length - 1;index >= 0; index -= 1) {
23070
23574
  const part = content[index];
23071
- if (part?.type !== "text" || part.text !== "")
23575
+ if (part?.type !== "text" || part.text !== "" || !mayRemove(part))
23072
23576
  continue;
23073
23577
  if (hint === undefined)
23074
23578
  hint = part.cache;
@@ -23144,12 +23648,14 @@ function contentValueAttachments(value) {
23144
23648
  ...typeof item.name === "string" ? { filename: item.name } : {}
23145
23649
  }));
23146
23650
  }
23147
- function rebuildFileContent(value, output) {
23651
+ function rebuildFileContent(value, output, attachments) {
23148
23652
  if (isDroppedToolOutput(output))
23149
23653
  return { type: "text", value: output };
23150
23654
  const rebuilt = [];
23151
23655
  let placed = false;
23152
23656
  for (const item of value) {
23657
+ if (item?.type === "file" && Array.isArray(attachments) && !attachments.some((attachment) => attachment?.url === item.uri && attachment?.mime === item.mime))
23658
+ continue;
23153
23659
  if (item?.type !== "text") {
23154
23660
  rebuilt.push(item);
23155
23661
  continue;
@@ -23292,14 +23798,16 @@ function adaptPayload(draft, admittedIDs = new Set) {
23292
23798
  let head = 0;
23293
23799
  const rendered = [];
23294
23800
  const emptyTextGuard = modelAcceptsEmptyContent(draft.model.providerID);
23801
+ const imageOmissions = getEmptyImageParts(messages);
23295
23802
  let omitted = 0;
23296
23803
  let emptyTextParts = 0;
23297
23804
  for (const message of messages) {
23298
23805
  const original = originals.get(message) ?? (typeof message.info.id === "string" ? originalsByID.get(message.info.id) : undefined);
23299
23806
  const id = message.info.syntheticHead ? HEAD_IDS[head++] : original?.id;
23300
23807
  const content = [];
23808
+ const emptyImages = new Set;
23301
23809
  const following = [];
23302
- for (const candidate of message.parts) {
23810
+ for (const [partIndex, candidate] of message.parts.entries()) {
23303
23811
  const part = candidate;
23304
23812
  const bridge = bridges.get(part) ?? (part.type === "tool" && typeof part.callID === "string" ? bridgesFor(message)?.get(part.callID) : undefined);
23305
23813
  if (!bridge) {
@@ -23355,7 +23863,10 @@ function adaptPayload(draft, admittedIDs = new Set) {
23355
23863
  }
23356
23864
  const { synthetic: _synthetic, ...clean } = part;
23357
23865
  const original = hostPartTypes.has(clean.type) && !containsHostInstance(clean) ? hostParts.get(contentKey(clean)) : undefined;
23358
- content.push(original ?? clean);
23866
+ const projected = original ?? clean;
23867
+ if (imageOmissions.get(message.info.id ?? "")?.has(partIndex))
23868
+ emptyImages.add(projected);
23869
+ content.push(projected);
23359
23870
  continue;
23360
23871
  }
23361
23872
  const state = part.state;
@@ -23387,7 +23898,7 @@ function adaptPayload(draft, admittedIDs = new Set) {
23387
23898
  if (bridge.result && bridge.resultMessage) {
23388
23899
  const result = {
23389
23900
  ...bridge.result,
23390
- result: state.output === bridge.output ? bridge.result.result : bridge.files && typeof state.output === "string" ? rebuildFileContent(bridge.files, state.output) : {
23901
+ result: state.output === bridge.output ? bridge.result.result : bridge.files && typeof state.output === "string" ? rebuildFileContent(bridge.files, state.output, state.attachments) : {
23391
23902
  type: state.status === "error" ? "error" : "text",
23392
23903
  value: state.output
23393
23904
  }
@@ -23398,7 +23909,7 @@ function adaptPayload(draft, admittedIDs = new Set) {
23398
23909
  following.push({ ...bridge.resultMessage, content: [result] });
23399
23910
  }
23400
23911
  }
23401
- const guarded = emptyTextGuard ? removeEmptyTextParts(content, rendered) : 0;
23912
+ const guarded = removeEmptyTextParts(content, rendered, (part) => emptyTextGuard || emptyImages.has(part));
23402
23913
  if (content.length || !original?.content.length && guarded === 0) {
23403
23914
  const value = {
23404
23915
  ...original,
@@ -23408,7 +23919,7 @@ function adaptPayload(draft, admittedIDs = new Set) {
23408
23919
  };
23409
23920
  const existing = id ? existingHeads.get(id) : undefined;
23410
23921
  rendered.push(existing ? Object.assign(existing, { content }) : value);
23411
- } else if (emptyTextGuard) {
23922
+ } else if (emptyTextGuard || guarded > 0) {
23412
23923
  omitted += 1;
23413
23924
  }
23414
23925
  emptyTextParts += guarded;
@@ -23416,7 +23927,7 @@ function adaptPayload(draft, admittedIDs = new Set) {
23416
23927
  }
23417
23928
  if (omitted + emptyTextParts > 0 && !emptyTextOmissionLogged.has(draft.sessionID)) {
23418
23929
  emptyTextOmissionLogged.add(draft.sessionID);
23419
- sessionLog2(draft.sessionID, `v2 payload: left out ${omitted} emptied messages and ${emptyTextParts} empty text parts, because Anthropic rejects an empty text block (logged once per session)`);
23930
+ sessionLog2(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)`);
23420
23931
  }
23421
23932
  draft.messages.splice(0, draft.messages.length, ...rendered);
23422
23933
  }
@@ -25316,11 +25827,11 @@ class DeletedSessionTombstones {
25316
25827
  }
25317
25828
 
25318
25829
  // src/features/magic-context/dreamer/task-executor.ts
25319
- import { createHash as createHash13 } from "node:crypto";
25830
+ import { createHash as createHash14 } from "node:crypto";
25320
25831
  import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
25321
25832
 
25322
25833
  // src/features/magic-context/mural/compress-cues.ts
25323
- import { createHash as createHash7 } from "node:crypto";
25834
+ import { createHash as createHash8 } from "node:crypto";
25324
25835
 
25325
25836
  // src/features/magic-context/dreamer/storage-dream-state.ts
25326
25837
  var getDreamStateStatements = new WeakMap;
@@ -26050,7 +26561,7 @@ async function applyCuesThroughModule(args, chunk, manifestText, signal) {
26050
26561
  log2(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason}; rejection ${rejectionCount}/${CUE_REJECTION_LATCH_THRESHOLD})`);
26051
26562
  }
26052
26563
  }
26053
- const commandId = `mural-cues:${route.moduleCommandId}:${createHash7("sha256").update(chunk.map((candidate) => candidate.memory.id).join(",")).digest("hex").slice(0, 24)}`;
26564
+ const commandId = `mural-cues:${route.moduleCommandId}:${createHash8("sha256").update(chunk.map((candidate) => candidate.memory.id).join(",")).digest("hex").slice(0, 24)}`;
26054
26565
  let response;
26055
26566
  try {
26056
26567
  response = await route.moduleClient.call({
@@ -26149,6 +26660,25 @@ async function runHiddenSingleShotPrompt(args) {
26149
26660
  });
26150
26661
  promptSettled = true;
26151
26662
  return { validated: run.validated, completion: run.output, childSessionId: handle.id };
26663
+ } catch (error) {
26664
+ if (args.onFailureOutput) {
26665
+ let timer;
26666
+ try {
26667
+ const completion = await Promise.race([
26668
+ args.executor.collect(handle, 200),
26669
+ new Promise((resolve) => {
26670
+ timer = setTimeout(() => resolve(null), 5000);
26671
+ })
26672
+ ]);
26673
+ if (completion)
26674
+ args.onFailureOutput(completion);
26675
+ } catch (readError) {
26676
+ log2(`${args.callContext}: failed to collect interrupted output: ${String(readError)}`);
26677
+ } finally {
26678
+ clearTimeout(timer);
26679
+ }
26680
+ }
26681
+ throw error;
26152
26682
  } finally {
26153
26683
  await args.executor.close(handle, {
26154
26684
  promptSettled,
@@ -26619,7 +27149,7 @@ function applyReviewVerdict(args, leaseKey, parsed, result) {
26619
27149
  }
26620
27150
 
26621
27151
  // src/features/magic-context/dreamer/classify.ts
26622
- import { createHash as createHash8 } from "node:crypto";
27152
+ import { createHash as createHash9 } from "node:crypto";
26623
27153
 
26624
27154
  // src/plugin/rust-tool-backends.ts
26625
27155
  function isRustAuthorityDrainingError(error) {
@@ -27056,7 +27586,7 @@ async function runClassifyThroughModule(args, chunk, anchors, signal, runOnHost)
27056
27586
  if (entry.qualifier)
27057
27587
  modelVariants[entry.model] = entry.qualifier;
27058
27588
  }
27059
- const commandId = `classify:${args.moduleCommandId ?? Date.now()}:${createHash8("sha256").update(chunk.map((candidate) => candidate.id).join(",")).digest("hex").slice(0, 24)}`;
27589
+ const commandId = `classify:${args.moduleCommandId ?? Date.now()}:${createHash9("sha256").update(chunk.map((candidate) => candidate.id).join(",")).digest("hex").slice(0, 24)}`;
27060
27590
  const runTask = (hostCompletion) => args.moduleClient?.call({
27061
27591
  sessionId: args.moduleSessionId,
27062
27592
  projectRoot: args.moduleProjectRoot,
@@ -27331,7 +27861,7 @@ function takeCurateSafetyRefusalCount(sessionId) {
27331
27861
 
27332
27862
  // src/features/magic-context/dreamer/docs-proposals.ts
27333
27863
  import { execFileSync } from "node:child_process";
27334
- import { createHash as createHash9 } from "node:crypto";
27864
+ import { createHash as createHash10 } from "node:crypto";
27335
27865
  import {
27336
27866
  existsSync as existsSync4,
27337
27867
  mkdirSync,
@@ -27346,7 +27876,7 @@ import {
27346
27876
  import { tmpdir } from "node:os";
27347
27877
  import { join as join7 } from "node:path";
27348
27878
  var FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
27349
- var hash = (text) => createHash9("sha256").update(text).digest("hex");
27879
+ var hash = (text) => createHash10("sha256").update(text).digest("hex");
27350
27880
  var proposalDir = (projectDir) => join7(projectDir, ".cortexkit/magic-context/docs-update-proposals");
27351
27881
  function docsBaseHashes(projectDir) {
27352
27882
  return Object.fromEntries(FILES.map((name) => [
@@ -27882,7 +28412,7 @@ function throwIfAborted2(signal) {
27882
28412
  }
27883
28413
 
27884
28414
  // src/features/magic-context/smart-notes/compiler.ts
27885
- import { createHash as createHash10 } from "node:crypto";
28415
+ import { createHash as createHash11 } from "node:crypto";
27886
28416
 
27887
28417
  // src/features/magic-context/smart-notes/compiler-prompt.ts
27888
28418
  var SMART_NOTE_COMPILER_SYSTEM_PROMPT = `You are the Magic Context smart-note compiler for the magic-context system.
@@ -28213,7 +28743,7 @@ function manifestAdvisoryWarnings(code, manifest) {
28213
28743
  return warnings;
28214
28744
  }
28215
28745
  function hashCheck(surfaceCondition, compiledCheck, manifest, checkCron) {
28216
- return createHash10("sha256").update(surfaceCondition ?? "").update("\x00").update(compiledCheck).update("\x00").update(JSON.stringify(manifest)).update("\x00").update(checkCron).digest("hex");
28746
+ return createHash11("sha256").update(surfaceCondition ?? "").update("\x00").update(compiledCheck).update("\x00").update(JSON.stringify(manifest)).update("\x00").update(checkCron).digest("hex");
28217
28747
  }
28218
28748
  function extractJsonObject(output) {
28219
28749
  const fenced = output.match(/```(?:json)?\s*([\s\S]*?)```/i);
@@ -28979,34 +29509,36 @@ function snapshotMaintainDocsFiles(docsDir) {
28979
29509
  }
28980
29510
 
28981
29511
  // src/features/magic-context/dreamer/map-memories.ts
28982
- import { createHash as createHash11 } from "node:crypto";
29512
+ import { createHash as createHash12 } from "node:crypto";
28983
29513
 
28984
29514
  // src/features/magic-context/dreamer/map-memories-prompt.ts
29515
+ import { execFile as execFile2 } from "node:child_process";
28985
29516
  import { existsSync as existsSync6, statSync as statSync3 } from "node:fs";
28986
29517
  import path4 from "node:path";
29518
+ import { promisify as promisify2 } from "node:util";
28987
29519
  var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context system. You map project memories to the repository files that back them.
28988
29520
 
28989
29521
  A memory's BACKING FILES are the file(s) whose code the memory makes a claim about — the files you would open to check whether the memory is accurate. You do NOT judge accuracy, rewrite, or remove anything. You only LOCATE backing files.
28990
29522
 
28991
- Tools (read-only): read, grep, glob. Request line ranges rather than full files; use offset/limit for longer files. Each memory may come with "Likely files" already named in it and confirmed to exist — confirm those FIRST (cheap) instead of searching. Use search/grep to FIND code only when no likely files are given. Do not guess — confirm a file exists and genuinely backs the memory before listing it. Keep reads minimal: you do not need to read a whole file to confirm it backs a one-line claim.
29523
+ Tools (read-only): read, grep, glob. Use at most 3 tool calls per memory, including searches and reads. Never read the same file twice in a batch; reuse what you already learned. Never repeat an identical search or use broad searches such as "fn " or ".*". Request one useful line range, not repeated slices. Each memory may come with "Likely files" from paths it names or a literal identifier search, confirmed to exist — confirm those FIRST (cheap) instead of searching. Use search/grep to FIND code only when no likely files are given. Do not guess — confirm a file exists and genuinely backs the memory before listing it. You are locating code, not exhaustively verifying the claim. If the call budget runs out before you can decide, leave that id unmapped and move on; uncertainty is not evidence of file-independence.
28992
29524
 
28993
29525
  For each memory decide ONE of:
28994
- - Backing files found → the COMPLETE set of repo-relative paths whose code the memory is about.
29526
+ - Backing files found → the specific repo-relative paths whose code the memory is about; do not chase every caller or related implementation.
28995
29527
  - File-independent → the memory describes EXTERNAL behavior (a provider / API / platform / protocol limit, e.g. "Anthropic returns 400 on empty content"), or a pure process / workflow / philosophy rule, with NO specific local file that backs it. A BEHAVIORAL claim (when to act, how to work, who decides, or tool-usage discipline) is file-independent even when it cites file paths or commands as examples: a named file is not the file backing the rule.
28996
29528
 
28997
- Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
29529
+ Output ONE XML manifest and NOTHING else — no narration, no per-memory commentary, no reasoning. Open <mappings> before investigating. Emit each <memory> element as soon as you decide its mapping, before investigating the next memory, so an interruption cannot lose completed work. Close </mappings> when finished:
28998
29530
  <mappings>
28999
29531
  <memory id="N" files="path/a.ts,path/b.ts"/>
29000
29532
  <memory id="M" independent="true"/>
29001
29533
  </mappings>
29002
29534
 
29003
29535
  Rules:
29004
- - Every input memory id MUST appear exactly once.
29536
+ - Every decided input memory id MUST appear exactly once. Omit uncertain ids rather than inventing backing files or declaring them independent.
29005
29537
  - files: repo-relative, comma-separated, no spaces inside a path. Only files that actually exist and genuinely back the memory.
29006
29538
  - A BACKING FILE is CODE that implements or handles the claim — not a file that merely mentions it. A path named inside a process directive is an action target or example, not evidence that the file backs the directive. A markdown doc (.md), a PARITY/notes file, or a test that only DESCRIBES an external fact is NOT a backing file. If the only place a memory's fact appears is prose/docs/a test (no code implements or handles it), mark it independent="true".
29007
29539
  - Many CONSTRAINTS are HYBRID: "external system does X, and OUR code handles it here." Map those to the HANDLING code (you can verify the handling, even though you can't verify the external behavior). Only mark independent when there is NO local code that implements or handles the fact.
29008
29540
  - Prefer the most specific file(s); do not pad with tangential files. Most memories map to one file; some to a few.
29009
- - When you genuinely cannot find any local backing and it is not clearly external, still emit the memory with independent="true" (do not drop it).`;
29541
+ - Mark independent="true" only for the file-independent cases above; an investigation that runs out of calls stays unmapped.`;
29010
29542
  var MAX_SEED_PATHS_PER_MEMORY = 3;
29011
29543
  var PATH_PATTERN = "`?((?:[\\w.-]+\\/)+[\\w.-]+\\.(?:ts|tsx|js|jsx|mjs|cjs|rs|go|py|json|jsonc|sql|toml|sh))`?";
29012
29544
  function extractMemoryCandidatePaths(content, repoDir) {
@@ -29028,10 +29560,65 @@ function extractMemoryCandidatePaths(content, repoDir) {
29028
29560
  }
29029
29561
  return [...found];
29030
29562
  }
29563
+ var execFileAsync2 = promisify2(execFile2);
29564
+ async function seedIdentifierCandidates(memories, repoDir) {
29565
+ const identifiers = new Map(memories.filter((memory) => memory.candidates.length === 0).map((memory) => [
29566
+ memory.id,
29567
+ [
29568
+ ...new Set(memory.content.match(/\b(?:[A-Z][A-Za-z0-9_]{3,}|[a-z][a-z0-9]*[A-Z][A-Za-z0-9_]*|[a-z][a-z0-9]*_[A-Za-z0-9_]{2,})\b/g) ?? [])
29569
+ ].slice(0, 3)
29570
+ ]));
29571
+ const terms = [...new Set([...identifiers.values()].flat())];
29572
+ if (terms.length === 0)
29573
+ return memories;
29574
+ let stdout;
29575
+ try {
29576
+ const result = await execFileAsync2("git", [
29577
+ "-c",
29578
+ "core.quotePath=false",
29579
+ "grep",
29580
+ "--no-color",
29581
+ "-n",
29582
+ "-I",
29583
+ "-F",
29584
+ ...terms.flatMap((term) => ["-e", term]),
29585
+ "--",
29586
+ ...["ts", "tsx", "js", "jsx", "mjs", "cjs", "rs", "go", "py", "sql", "sh"].map((extension) => `*.${extension}`)
29587
+ ], {
29588
+ cwd: repoDir,
29589
+ encoding: "utf8",
29590
+ timeout: 2000,
29591
+ maxBuffer: 256 * 1024,
29592
+ windowsHide: true,
29593
+ killSignal: "SIGKILL"
29594
+ });
29595
+ stdout = result.stdout;
29596
+ } catch {
29597
+ return memories;
29598
+ }
29599
+ const matches = stdout.split(`
29600
+ `).flatMap((line) => {
29601
+ const match = line.match(/^([^:]+):\d+:(.*)$/);
29602
+ return match ? [{ file: match[1], text: match[2] }] : [];
29603
+ });
29604
+ return memories.map((memory) => {
29605
+ const names = identifiers.get(memory.id);
29606
+ if (!names?.length)
29607
+ return memory;
29608
+ const files = new Map;
29609
+ for (const match of matches) {
29610
+ const score = names.filter((name) => match.text.includes(name)).length;
29611
+ if (score)
29612
+ files.set(match.file, Math.max(files.get(match.file) ?? 0, score));
29613
+ }
29614
+ const candidates = [...files].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_SEED_PATHS_PER_MEMORY).map(([file]) => file);
29615
+ return candidates.length ? { ...memory, candidates } : memory;
29616
+ });
29617
+ }
29031
29618
  function buildMapMemoriesPrompt(projectPath, memories) {
29032
29619
  const list = memories.map((m) => {
29033
29620
  const seed = m.candidates.length ? `
29034
- Likely files (named in the memory, confirmed to exist): ${m.candidates.join(", ")}` : "";
29621
+ Likely files (candidate paths, confirmed to exist): ${m.candidates.join(", ")}` : "";
29035
29622
  return `[${m.id}] ${m.category}
29036
29623
  ${m.content}${seed}`;
29037
29624
  }).join(`
@@ -29041,13 +29628,13 @@ ${m.content}${seed}`;
29041
29628
 
29042
29629
  Project: ${projectPath}
29043
29630
 
29044
- For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths are named in the memory and confirmed to exist — START there only for code claims: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Then output ONE <mappings> manifest covering every id.
29631
+ For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths either occur in the memory or match its identifiers in a bounded literal source search — START there only for code claims: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Use at most 3 tool calls per memory, reuse prior reads, and emit each decided mapping immediately in ONE <mappings> manifest. Leave uncertain ids unmapped and move on.
29045
29632
 
29046
29633
  <memories>
29047
29634
  ${list}
29048
29635
  </memories>`;
29049
29636
  }
29050
- var MEMORY_ELEMENT_PATTERN = "<memory\\b([^>]*)(?:\\/>|>([\\s\\S]*?)<\\/memory>)";
29637
+ var MEMORY_ELEMENT_PATTERN = "<memory\\b([^<>]*)(?:\\/>|>([\\s\\S]*?)<\\/memory>)";
29051
29638
  var NESTED_FILE_PATTERN = "<file\\b([^>]*)\\/?>";
29052
29639
  function extractNestedFilePaths(inner) {
29053
29640
  const files = [];
@@ -29105,12 +29692,78 @@ function validateMapMemoriesManifest(text, expectedIds) {
29105
29692
  assertNoDuplicateManifestIds(parsed.filter((entry) => expectedIds.has(entry.id)).map((entry) => entry.id), "mappings");
29106
29693
  return parsed;
29107
29694
  }
29695
+ function parsePartialMapMemoriesManifest(text, expectedIds) {
29696
+ const start = text.indexOf("<mappings>");
29697
+ if (start < 0)
29698
+ return [];
29699
+ const entries = new Map;
29700
+ const conflicts = new Set;
29701
+ for (const match of text.slice(start).matchAll(new RegExp(MEMORY_ELEMENT_PATTERN, "gi"))) {
29702
+ try {
29703
+ const entry = parseMapMemoriesManifest(`<mappings>${match[0]}</mappings>`)[0];
29704
+ if (!entry || !expectedIds.has(entry.id) || conflicts.has(entry.id))
29705
+ continue;
29706
+ const previous = entries.get(entry.id);
29707
+ if (previous && JSON.stringify(previous) !== JSON.stringify(entry)) {
29708
+ entries.delete(entry.id);
29709
+ conflicts.add(entry.id);
29710
+ } else {
29711
+ entries.set(entry.id, entry);
29712
+ }
29713
+ } catch {}
29714
+ }
29715
+ return [...entries.values()];
29716
+ }
29108
29717
 
29109
29718
  // src/features/magic-context/dreamer/map-memories.ts
29110
- var MAP_BATCH_SIZE = 20;
29719
+ var MAP_BATCH_SIZE = 5;
29111
29720
  var MAP_BATCH_FLOOR_MS = 240000;
29112
29721
  var CONSECUTIVE_TIMEOUT_LIMIT2 = 2;
29113
29722
  var MAX_INDEPENDENT_REQUEUE_PER_RUN = MAP_BATCH_SIZE;
29723
+ function retryKey(project) {
29724
+ return `map-memories:retry:${project}`;
29725
+ }
29726
+ function readRetries(db, project) {
29727
+ try {
29728
+ const parsed = JSON.parse(getDreamState(db, retryKey(project)) ?? "{}");
29729
+ if (!isRecord(parsed))
29730
+ return {};
29731
+ const retries = {};
29732
+ for (const [id, value] of Object.entries(parsed)) {
29733
+ if (isRecord(value) && typeof value.content === "string" && typeof value.failures === "number" && typeof value.deferUntil === "number") {
29734
+ retries[id] = {
29735
+ content: value.content,
29736
+ failures: value.failures,
29737
+ deferUntil: value.deferUntil
29738
+ };
29739
+ }
29740
+ }
29741
+ return retries;
29742
+ } catch {
29743
+ return {};
29744
+ }
29745
+ }
29746
+ function contentKey2(memory) {
29747
+ return createHash12("sha256").update(`${memory.category}
29748
+ ${memory.content}`).digest("hex");
29749
+ }
29750
+ function isDeferred(memory, retries) {
29751
+ const retry = retries[memory.id];
29752
+ return !!retry && retry.content === contentKey2(memory) && retry.deferUntil > Date.now();
29753
+ }
29754
+ function assistantText2(messages) {
29755
+ return messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).map((message) => extractLatestAssistantText([message]) ?? (isRecord(message) && Array.isArray(message.content) ? message.content.filter(isRecord).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
29756
+ `) : "")).join(`
29757
+ `);
29758
+ }
29759
+ function currentManifest(text) {
29760
+ const start = text.lastIndexOf("<mappings>");
29761
+ return start < 0 ? text : text.slice(start);
29762
+ }
29763
+ function logBatchUsage(messages, committed, startedAt) {
29764
+ const steps = messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).length;
29765
+ log2(`[dreamer] map-memories batch: steps=${steps} committed=${committed} steps_per_memory=${committed ? (steps / committed).toFixed(2) : "n/a"} elapsed_ms=${Date.now() - startedAt}`);
29766
+ }
29114
29767
  function computeMapBatchSliceMs(remainingMs, batchesRemaining) {
29115
29768
  return Math.min(remainingMs, Math.max(MAP_BATCH_FLOOR_MS, Math.floor(remainingMs / batchesRemaining)));
29116
29769
  }
@@ -29132,7 +29785,7 @@ function toMapInput(memory, repoDir) {
29132
29785
  candidates: extractMemoryCandidatePaths(memory.content, repoDir)
29133
29786
  };
29134
29787
  }
29135
- function selectMapMemoryInputs(db, projectIdentity, repoDir) {
29788
+ function selectMapMemoryInputs(db, projectIdentity, repoDir, includeDeferred = false) {
29136
29789
  const active = getMemoriesByProject2(db, projectIdentity);
29137
29790
  const activeIds = active.map((m) => m.id);
29138
29791
  const unmapped = new Set(getUnmappedMemoryIds(db, activeIds));
@@ -29151,7 +29804,8 @@ function selectMapMemoryInputs(db, projectIdentity, repoDir) {
29151
29804
  if (requeue.length >= MAX_INDEPENDENT_REQUEUE_PER_RUN)
29152
29805
  break;
29153
29806
  }
29154
- return [...unmappedInputs, ...requeue];
29807
+ const retries = readRetries(db, projectIdentity);
29808
+ return [...unmappedInputs, ...requeue].filter((memory) => includeDeferred || !isDeferred(memory, retries));
29155
29809
  }
29156
29810
  async function mapMemories(args) {
29157
29811
  const result = {
@@ -29161,14 +29815,21 @@ async function mapMemories(args) {
29161
29815
  remaining: 0,
29162
29816
  complete: true
29163
29817
  };
29164
- const inputs = selectMapMemoryInputs(args.db, args.projectIdentity, args.sessionDirectory);
29818
+ const allInputs = selectMapMemoryInputs(args.db, args.projectIdentity, args.sessionDirectory, true);
29819
+ const retries = readRetries(args.db, args.projectIdentity);
29820
+ const activeIds = new Set(allInputs.map((memory) => String(memory.id)));
29821
+ for (const id of Object.keys(retries))
29822
+ if (!activeIds.has(id))
29823
+ delete retries[id];
29824
+ const inputs = allInputs.filter((memory) => !isDeferred(memory, retries));
29825
+ result.remaining = allInputs.length;
29826
+ result.complete = result.remaining === 0;
29165
29827
  if (inputs.length === 0)
29166
29828
  return result;
29167
29829
  const batches = [];
29168
29830
  for (let i = 0;i < inputs.length; i += MAP_BATCH_SIZE) {
29169
29831
  batches.push({ inputs: inputs.slice(i, i + MAP_BATCH_SIZE), isOmissionRetry: false });
29170
29832
  }
29171
- result.remaining = inputs.length;
29172
29833
  const abortController = new AbortController;
29173
29834
  const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, (reason) => abortController.abort(new Error(reason)), args.leaseAcquisition);
29174
29835
  try {
@@ -29191,6 +29852,24 @@ async function mapMemories(args) {
29191
29852
  break;
29192
29853
  const outcome = await mapOneBatch(args, batch.inputs, sliceMs, abortController.signal);
29193
29854
  const committed = outcome.mapped + outcome.independent;
29855
+ runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
29856
+ const committedIds = new Set(outcome.committedIds);
29857
+ for (const memory of batch.inputs) {
29858
+ if (committedIds.has(memory.id)) {
29859
+ delete retries[memory.id];
29860
+ continue;
29861
+ }
29862
+ const content = contentKey2(memory);
29863
+ const prior = retries[memory.id];
29864
+ const failures = (prior?.content === content ? prior.failures : 0) + 1;
29865
+ retries[memory.id] = {
29866
+ content,
29867
+ failures,
29868
+ deferUntil: failures < 2 ? 0 : Date.now() + Math.min(24, 2 ** (failures - 2)) * 3600000
29869
+ };
29870
+ }
29871
+ setDreamState(args.db, retryKey(args.projectIdentity), JSON.stringify(retries));
29872
+ });
29194
29873
  result.mapped += outcome.mapped;
29195
29874
  result.independent += outcome.independent;
29196
29875
  result.remaining -= committed;
@@ -29200,8 +29879,8 @@ async function mapMemories(args) {
29200
29879
  }
29201
29880
  if (outcome.requeue?.length) {
29202
29881
  if (!batch.isOmissionRetry) {
29203
- batches.splice(i + 1, 0, {
29204
- inputs: outcome.requeue,
29882
+ batches.push({
29883
+ inputs: outcome.requeue.filter((memory) => !isDeferred(memory, retries)),
29205
29884
  isOmissionRetry: true
29206
29885
  });
29207
29886
  log2(`[dreamer] map-memories: committed ${committed}/${batch.inputs.length} mapping(s); requeueing ${outcome.requeue.length} omitted id(s) in a targeted retry`);
@@ -29209,12 +29888,12 @@ async function mapMemories(args) {
29209
29888
  log2(`[dreamer] map-memories: targeted retry still omitted ${outcome.requeue.length} id(s); leaving them unmapped for the next run`);
29210
29889
  }
29211
29890
  }
29212
- if (outcome.failure?.class === "timeout") {
29891
+ if (outcome.failure?.class === "timeout" && committed === 0) {
29213
29892
  consecutiveTimeouts += 1;
29214
29893
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
29215
29894
  if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUT_LIMIT2) {
29216
29895
  result.stopReason = "timeout-circuit-breaker";
29217
- log2(`[dreamer] map-memories starvation: circuit breaker tripped after ${consecutiveTimeouts} consecutive batch timeouts (model too slow for its time slice); per-batch elapsed [${timeoutStreakElapsedMs.join("ms, ")}ms] vs ${sliceMs}ms slice; stopping with ${result.remaining} mapping(s) remaining`);
29896
+ log2(`[dreamer] map-memories starvation: circuit breaker tripped after ${consecutiveTimeouts} consecutive batch timeouts with no committed mappings (investigation made no durable progress); per-batch elapsed [${timeoutStreakElapsedMs.join("ms, ")}ms] vs ${sliceMs}ms slice; stopping with ${result.remaining} mapping(s) remaining`);
29218
29897
  break;
29219
29898
  }
29220
29899
  } else {
@@ -29234,12 +29913,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29234
29913
  let promptSettled = false;
29235
29914
  const startedAt = Date.now();
29236
29915
  let budgetFinalized = false;
29916
+ let failedCompletion;
29237
29917
  const onBudgetUpdate = (state) => {
29238
29918
  budgetFinalized ||= state.finalizeFired;
29239
29919
  args.onBudgetUpdate?.(state);
29240
29920
  };
29241
29921
  try {
29242
- const prompt = buildMapMemoriesPrompt(args.projectIdentity, batch);
29922
+ const prompt = buildMapMemoriesPrompt(args.projectIdentity, await seedIdentifierCandidates(batch, args.sessionDirectory));
29243
29923
  if (args.hiddenCompletionExecutor) {
29244
29924
  const run = await runHiddenSingleShotPrompt({
29245
29925
  executor: args.hiddenCompletionExecutor,
@@ -29255,14 +29935,18 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29255
29935
  fallbackModels: args.fallbackModels,
29256
29936
  timeoutMs: sliceMs,
29257
29937
  signal,
29258
- parse: (text) => validateMapMemoriesManifest(text, new Set(batch.map((memory) => memory.id)))
29938
+ onFailureOutput: (completion) => {
29939
+ failedCompletion = completion;
29940
+ },
29941
+ parse: (text, completion) => validateMapMemoriesManifest(currentManifest(assistantText2(completion.messages ?? []) || text), new Set(batch.map((memory) => memory.id)))
29259
29942
  });
29260
29943
  recordInvocation2(args, startedAt, {
29261
29944
  status: "completed",
29262
29945
  messages: run.completion.messages ?? []
29263
29946
  });
29264
- const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized);
29265
- const returnedIds = new Set(run.validated.map((entry) => entry.id));
29947
+ const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
29948
+ logBatchUsage(run.completion.messages ?? [], outcome.mapped + outcome.independent, startedAt);
29949
+ const returnedIds = new Set(outcome.committedIds);
29266
29950
  return {
29267
29951
  ...outcome,
29268
29952
  requeue: budgetFinalized ? [] : batch.filter((memory) => !returnedIds.has(memory.id))
@@ -29305,7 +29989,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29305
29989
  fetchOutput: async () => {
29306
29990
  const messagesResponse = await client.session.messages({
29307
29991
  path: { id: agentSessionId },
29308
- query: { directory: args.sessionDirectory, limit: 100 }
29992
+ query: { directory: args.sessionDirectory, limit: 200 }
29309
29993
  });
29310
29994
  return normalizeSDKResponse(messagesResponse, [], {
29311
29995
  preferResponseOnMissingData: true
@@ -29315,7 +29999,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29315
29999
  if (hasLengthCappedOutput(messages)) {
29316
30000
  throw new Error("map-memories returned length-capped output");
29317
30001
  }
29318
- const text = extractLatestAssistantText(messages);
30002
+ const text = currentManifest(assistantText2(messages));
29319
30003
  if (!text)
29320
30004
  throw new Error("map-memories returned no output");
29321
30005
  return validateMapMemoriesManifest(text, new Set(batch.map((memory) => memory.id)));
@@ -29323,8 +30007,9 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29323
30007
  });
29324
30008
  promptSettled = true;
29325
30009
  recordInvocation2(args, startedAt, { status: "completed", messages: run.output });
29326
- const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized);
29327
- const returnedIds = new Set(run.validated.filter((entry) => batch.some((memory) => memory.id === entry.id)).map((entry) => entry.id));
30010
+ const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
30011
+ logBatchUsage(run.output, outcome.mapped + outcome.independent, startedAt);
30012
+ const returnedIds = new Set(outcome.committedIds);
29328
30013
  return {
29329
30014
  ...outcome,
29330
30015
  requeue: budgetFinalized ? [] : batch.filter((memory) => !returnedIds.has(memory.id))
@@ -29332,18 +30017,23 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29332
30017
  } catch (error) {
29333
30018
  const desc = describeError(error);
29334
30019
  log2(`[dreamer] map-memories batch failed: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
30020
+ const messages = failedCompletion?.messages ?? await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory) ?? [];
29335
30021
  recordInvocation2(args, startedAt, {
29336
30022
  status: failedInvocationStatus(error),
29337
30023
  error,
29338
- messages: await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory)
30024
+ messages
29339
30025
  });
29340
- if (error instanceof DreamerModuleFailureError || error instanceof DreamTokenBudgetExceeded)
30026
+ if (error instanceof DreamerModuleFailureError)
29341
30027
  throw error;
29342
30028
  if (signal.aborted)
29343
30029
  throw error;
30030
+ const recovered = parsePartialMapMemoriesManifest(assistantText2(messages) || failedCompletion?.text || "", new Set(batch.map((memory) => memory.id)));
30031
+ const outcome = await applyParsedBatchMappings(args, batch, recovered, budgetFinalized, true);
30032
+ logBatchUsage(messages, outcome.mapped + outcome.independent, startedAt);
30033
+ if (error instanceof DreamTokenBudgetExceeded)
30034
+ throw error;
29344
30035
  return {
29345
- mapped: 0,
29346
- independent: 0,
30036
+ ...outcome,
29347
30037
  failure: {
29348
30038
  class: isTimeoutClassError2(error) ? "timeout" : "other",
29349
30039
  elapsedMs: Date.now() - startedAt
@@ -29362,7 +30052,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
29362
30052
  });
29363
30053
  }
29364
30054
  }
29365
- async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = false) {
30055
+ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = false, allowPartial = false) {
29366
30056
  const batchIds = new Set(batch.map((memory) => memory.id));
29367
30057
  const valid = parsed.filter((entry) => batchIds.has(entry.id));
29368
30058
  const unknown = parsed.filter((entry) => !batchIds.has(entry.id));
@@ -29370,14 +30060,14 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
29370
30060
  log2(`[dreamer] map-memories warning: dropping ${unknown.length} unknown mapping entr${unknown.length === 1 ? "y" : "ies"} outside the current batch (${unknown.map((entry) => entry.id).join(", ")})`);
29371
30061
  }
29372
30062
  assertNoDuplicateManifestIds(valid.map((entry) => entry.id), "mappings");
29373
- if (!budgetFinalized && valid.length * 2 < batch.length) {
30063
+ if (!allowPartial && !budgetFinalized && valid.length * 2 < batch.length) {
29374
30064
  throw new Error(`mappings manifest covers ${valid.length}/${batch.length} batch ids after filtering unknown entries; rejecting mostly-wrong manifest`);
29375
30065
  }
29376
30066
  if (budgetFinalized && valid.length < batch.length) {
29377
30067
  log2(`[dreamer] map-memories: accepted partial manifest after token budget: ${valid.length}/${batch.length}`);
29378
30068
  }
29379
30069
  if (valid.length === 0)
29380
- return { mapped: 0, independent: 0 };
30070
+ return { mapped: 0, independent: 0, committedIds: [] };
29381
30071
  const planned = [];
29382
30072
  const batchById = new Map(batch.map((memory) => [memory.id, memory]));
29383
30073
  for (const p of valid) {
@@ -29426,9 +30116,10 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
29426
30116
  });
29427
30117
  }
29428
30118
  if (planned.length === 0)
29429
- return { mapped: 0, independent: 0 };
30119
+ return { mapped: 0, independent: 0, committedIds: [] };
29430
30120
  let mapped = 0;
29431
30121
  let independent = 0;
30122
+ const committedIds = [];
29432
30123
  if (args.moduleRoute) {
29433
30124
  const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, planned.map((item) => item.id));
29434
30125
  const rows = planned.map((item) => {
@@ -29452,7 +30143,7 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
29452
30143
  name: "memory.set_mapping",
29453
30144
  arguments: {
29454
30145
  memory_project: args.projectIdentity,
29455
- command_id: `${args.moduleRoute.moduleCommandId}:${createHash11("sha256").update(rows.map((row) => row.memory_id).join(",")).digest("hex").slice(0, 16)}`,
30146
+ command_id: `${args.moduleRoute.moduleCommandId}:${createHash12("sha256").update(rows.map((row) => row.memory_id).join(",")).digest("hex").slice(0, 16)}`,
29456
30147
  rows
29457
30148
  }
29458
30149
  }
@@ -29466,19 +30157,22 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
29466
30157
  const accepted = new Set(result.accepted.filter((id) => typeof id === "number"));
29467
30158
  for (const item of planned) {
29468
30159
  const identity = identities.get(item.id);
29469
- if (identity && accepted.has(identity.moduleId))
30160
+ if (identity && accepted.has(identity.moduleId)) {
29470
30161
  item.independent ? independent += 1 : mapped += 1;
30162
+ committedIds.push(item.id);
30163
+ }
29471
30164
  }
29472
- return { mapped, independent };
30165
+ return { mapped, independent, committedIds };
29473
30166
  }
29474
30167
  const now = Date.now();
29475
30168
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
29476
30169
  for (const item of planned) {
29477
30170
  recordMemoryMapping(args.db, item.id, item.files, now, item.mappingOrigin);
29478
30171
  item.independent ? independent += 1 : mapped += 1;
30172
+ committedIds.push(item.id);
29479
30173
  }
29480
30174
  });
29481
- return { mapped, independent };
30175
+ return { mapped, independent, committedIds };
29482
30176
  }
29483
30177
  function recordInvocation2(args, startedAt, params) {
29484
30178
  if (!args.parentSessionId)
@@ -29746,32 +30440,41 @@ async function promotePrimers(args) {
29746
30440
  // src/features/magic-context/dreamer/primer-seed.ts
29747
30441
  var PRIMER_SEED_CAP_TOKENS = 4000;
29748
30442
  function renderUserAndToolOrientation(sessionId, startOrdinal, endOrdinal, capTokens) {
30443
+ const renderer = orientationRenderer(capTokens);
30444
+ visitRawSessionMessages2(sessionId, startOrdinal, endOrdinal, renderer.visit, {
30445
+ summary: true
30446
+ });
30447
+ return renderer.result();
30448
+ }
30449
+ function orientationRenderer(capTokens) {
29749
30450
  const lines = [];
29750
30451
  let tokens = 0;
29751
30452
  let sawMessage = false;
29752
- visitRawSessionMessages2(sessionId, startOrdinal, endOrdinal, (msg) => {
29753
- sawMessage = true;
29754
- const out = [];
29755
- if (msg.role === "user" && hasMeaningfulUserText2(msg.parts)) {
29756
- const text = extractTexts2(msg.parts).map((t) => cleanUserText2(t)).map(normalizeText).filter((t) => t.length > 0).join(" / ");
29757
- if (text)
29758
- out.push(`U: ${text}`);
29759
- }
29760
- for (const tc of extractToolCallSummaries(msg.parts))
29761
- out.push(tc);
29762
- for (const line of out) {
29763
- const lineTokens = estimateTokens(line);
29764
- if (tokens + lineTokens > capTokens && lines.length > 0) {
29765
- lines.push("… (orientation truncated; investigate the current source directly)");
29766
- return false;
30453
+ return {
30454
+ visit: (msg) => {
30455
+ sawMessage = true;
30456
+ const out = [];
30457
+ if (msg.role === "user" && hasMeaningfulUserText2(msg.parts)) {
30458
+ const text = extractTexts2(msg.parts).map((t) => cleanUserText2(t)).map(normalizeText).filter((t) => t.length > 0).join(" / ");
30459
+ if (text)
30460
+ out.push(`U: ${text}`);
30461
+ }
30462
+ for (const tc of extractToolCallSummaries(msg.parts))
30463
+ out.push(tc);
30464
+ for (const line of out) {
30465
+ const lineTokens = estimateTokens(line);
30466
+ if (tokens + lineTokens > capTokens && lines.length > 0) {
30467
+ lines.push("… (orientation truncated; investigate the current source directly)");
30468
+ return false;
30469
+ }
30470
+ lines.push(line);
30471
+ tokens += lineTokens;
29767
30472
  }
29768
- lines.push(line);
29769
- tokens += lineTokens;
29770
- }
29771
- return true;
29772
- }, { summary: true });
29773
- return sawMessage ? lines.join(`
29774
- `) : null;
30473
+ return true;
30474
+ },
30475
+ result: () => sawMessage ? lines.join(`
30476
+ `) : null
30477
+ };
29775
30478
  }
29776
30479
  function selectPrimerOriginCandidate(db, primer) {
29777
30480
  return getPrimerCandidatesByIds(db, primer.sourceCandidateIds).filter((candidate) => candidate.projectPath === primer.projectPath).sort((a, b) => b.sourceMessageTime - a.sourceMessageTime || b.id - a.id)[0];
@@ -29805,16 +30508,37 @@ function closedBookOriginP1(db, sessionId, originStartMessage) {
29805
30508
  return { orientation, sessionId };
29806
30509
  }
29807
30510
  function buildPrimerSeed(db, primer) {
30511
+ return buildPrimerSeedUsing(db, selectPrimerOriginCandidate(db, primer), renderUserAndToolOrientation);
30512
+ }
30513
+ async function buildPrimerSeedAsync(db, primer) {
29808
30514
  const mostRecent = selectPrimerOriginCandidate(db, primer);
30515
+ let orientation = null;
30516
+ if (mostRecent && typeof mostRecent.sourceCompartmentStart === "number" && typeof mostRecent.sourceCompartmentEnd === "number") {
30517
+ try {
30518
+ const renderer = orientationRenderer(PRIMER_SEED_CAP_TOKENS);
30519
+ await visitRawSessionMessagesAsync2(mostRecent.sessionId, mostRecent.sourceCompartmentStart, mostRecent.sourceCompartmentEnd, renderer.visit, { summary: true });
30520
+ orientation = renderer.result();
30521
+ } catch {
30522
+ orientation = null;
30523
+ }
30524
+ }
30525
+ return buildPrimerSeedUsing(db, mostRecent, () => orientation);
30526
+ }
30527
+ function buildPrimerSeedUsing(db, mostRecent, render) {
29809
30528
  if (!mostRecent || typeof mostRecent.sourceCompartmentStart !== "number" || typeof mostRecent.sourceCompartmentEnd !== "number") {
29810
- return { kind: "closed-book", orientation: "", prePost: "", sessionId: null };
30529
+ return {
30530
+ kind: "closed-book",
30531
+ orientation: "",
30532
+ prePost: "",
30533
+ sessionId: null
30534
+ };
29811
30535
  }
29812
30536
  const sessionId = mostRecent.sessionId;
29813
30537
  const start = mostRecent.sourceCompartmentStart;
29814
30538
  const end = mostRecent.sourceCompartmentEnd;
29815
30539
  let orientation = null;
29816
30540
  try {
29817
- orientation = renderUserAndToolOrientation(sessionId, start, end, PRIMER_SEED_CAP_TOKENS);
30541
+ orientation = render(sessionId, start, end, PRIMER_SEED_CAP_TOKENS);
29818
30542
  } catch {
29819
30543
  orientation = null;
29820
30544
  }
@@ -29945,14 +30669,14 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
29945
30669
  provider = null;
29946
30670
  }
29947
30671
  }
29948
- const seed = withRawSessionMessageCache2(() => {
30672
+ const seed = await (async () => {
29949
30673
  const unregister = provider && originSessionId ? setRawMessageProvider2(originSessionId, provider) : null;
29950
30674
  try {
29951
- return buildPrimerSeed(args.db, primer);
30675
+ return provider?.iterateMessageRangeAsync ? await buildPrimerSeedAsync(args.db, primer) : withRawSessionMessageCache2(() => buildPrimerSeed(args.db, primer));
29952
30676
  } finally {
29953
30677
  unregister?.();
29954
30678
  }
29955
- });
30679
+ })();
29956
30680
  let agentSessionId = null;
29957
30681
  let promptSettled = false;
29958
30682
  const startedAt = Date.now();
@@ -29968,7 +30692,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
29968
30692
  prompt,
29969
30693
  title: "magic-context-dream-refresh-primers",
29970
30694
  callContext: "dreamer:refresh-primers",
29971
- metadata: { tokenBudget: args.tokenBudget, onBudgetUpdate: args.onBudgetUpdate },
30695
+ metadata: {
30696
+ tokenBudget: args.tokenBudget,
30697
+ onBudgetUpdate: args.onBudgetUpdate
30698
+ },
29972
30699
  model: args.model,
29973
30700
  fallbackModels: args.fallbackModels,
29974
30701
  language: args.language,
@@ -30034,7 +30761,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
30034
30761
  validateOutput: (messages) => parseAnswer(messages, primer.answer)
30035
30762
  });
30036
30763
  promptSettled = true;
30037
- recordInvocation3(args, startedAt, { status: "completed", messages: run.output });
30764
+ recordInvocation3(args, startedAt, {
30765
+ status: "completed",
30766
+ messages: run.output
30767
+ });
30038
30768
  const answer = run.validated.trim();
30039
30769
  if (!answer)
30040
30770
  return false;
@@ -30049,7 +30779,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
30049
30779
  } catch (error) {
30050
30780
  const desc = describeError(error);
30051
30781
  log2(`[dreamer] refresh-primers failed (primer #${primer.id}): ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
30052
- recordInvocation3(args, startedAt, { status: failedInvocationStatus(error), error });
30782
+ recordInvocation3(args, startedAt, {
30783
+ status: failedInvocationStatus(error),
30784
+ error
30785
+ });
30053
30786
  throw error;
30054
30787
  } finally {
30055
30788
  if (agentSessionId && args.client)
@@ -30687,12 +31420,12 @@ function insertDreamRun(db, run) {
30687
31420
  }
30688
31421
 
30689
31422
  // src/features/magic-context/dreamer/verify.ts
30690
- import { createHash as createHash12 } from "node:crypto";
31423
+ import { createHash as createHash13 } from "node:crypto";
30691
31424
 
30692
31425
  // src/features/magic-context/dreamer/verify-diff.ts
30693
- import { execFile as execFile2 } from "node:child_process";
30694
- import { promisify as promisify2 } from "node:util";
30695
- var exec = promisify2(execFile2);
31426
+ import { execFile as execFile3 } from "node:child_process";
31427
+ import { promisify as promisify3 } from "node:util";
31428
+ var exec = promisify3(execFile3);
30696
31429
  var VERIFY_DIFF_BUDGET_TOKENS = 12000;
30697
31430
  var MAX_DIFF_BYTES = 4 * 1024 * 1024;
30698
31431
  async function git2(root, args) {
@@ -31431,7 +32164,7 @@ async function applyParsedVerifyManifest(args, batch, parsed, headAtPrompt, budg
31431
32164
  name: "memory.set_verification",
31432
32165
  arguments: {
31433
32166
  memory_project: args.projectIdentity,
31434
- command_id: `${args.moduleRoute.moduleCommandId}:${createHash12("sha256").update(rows.map((row) => row.memory_id).join(",")).digest("hex").slice(0, 16)}`,
32167
+ command_id: `${args.moduleRoute.moduleCommandId}:${createHash13("sha256").update(rows.map((row) => row.memory_id).join(",")).digest("hex").slice(0, 16)}`,
31435
32168
  rows
31436
32169
  }
31437
32170
  }
@@ -32237,7 +32970,7 @@ function parseFrictionGateVerdict(verdict) {
32237
32970
  }
32238
32971
  function computeRetrospectiveWindowKey(flagged) {
32239
32972
  const anchors = flagged.map((message) => `${message.sessionId}:${message.ts}`).sort().join("|");
32240
- return createHash13("sha256").update(anchors).digest("hex").slice(0, 32);
32973
+ return createHash14("sha256").update(anchors).digest("hex").slice(0, 32);
32241
32974
  }
32242
32975
  function renderFrictionWindow(messages, flaggedOrdinals, radius = 2) {
32243
32976
  const flagged = new Set(flaggedOrdinals);
@@ -34191,9 +34924,9 @@ function hiddenTerminalError(event) {
34191
34924
  }
34192
34925
 
34193
34926
  // src/v2/hooks/lkg-system.ts
34194
- import { createHash as createHash14 } from "node:crypto";
34927
+ import { createHash as createHash15 } from "node:crypto";
34195
34928
  function identity(slot) {
34196
- return createHash14("sha256").update(JSON.stringify({
34929
+ return createHash15("sha256").update(JSON.stringify({
34197
34930
  capturedAt: slot.capturedAt,
34198
34931
  captureSequence: slot.captureSequence,
34199
34932
  rowVersion: slot.rowVersion,
@@ -47010,7 +47743,7 @@ function resolveCtxExpandMode(args, domain) {
47010
47743
  }
47011
47744
 
47012
47745
  // src/tools/ctx-expand/render.ts
47013
- import { createHash as createHash15 } from "node:crypto";
47746
+ import { createHash as createHash16 } from "node:crypto";
47014
47747
  function isRecord6(value) {
47015
47748
  return value !== null && typeof value === "object" && !Array.isArray(value);
47016
47749
  }
@@ -47234,11 +47967,61 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
47234
47967
  const index = scoped ? Number(scoped[2]) : 0;
47235
47968
  const piText = message.parts.filter((part) => isRecord6(part) && part.type === "text");
47236
47969
  const part = message.parts[index];
47237
- const matching = derived ? piText.filter((part) => isRecord6(part) && typeof part.text === "string" && createHash15("sha256").update(part.text).digest("hex") === derived[3]) : [];
47970
+ const matching = derived ? piText.filter((part) => isRecord6(part) && typeof part.text === "string" && createHash16("sha256").update(part.text).digest("hex") === derived[3]) : [];
47238
47971
  const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" ? piText[index] : part;
47239
47972
  return selected ? renderPartFull(selected) ?? "" : `Tag ${number}'s original text part is no longer in stored history.`;
47240
47973
  }
47241
47974
 
47975
+ // src/tools/ctx-expand/search-archive.ts
47976
+ function renderSearchArchive(db, sessionId, start, end, tokenBudget, archiveOnly = false) {
47977
+ return withMessageFtsSessionFilter(db, sessionId, (sessionFirst) => {
47978
+ let rawIds;
47979
+ const rows = db.prepare(sessionFirst ? `SELECT f.message_ordinal AS ordinal, f.message_id AS id, f.role, f.content
47980
+ FROM message_fts_rowid_map AS m
47981
+ JOIN message_history_fts AS f ON f.rowid = m.fts_rowid
47982
+ WHERE m.session_id = ?1 AND m.message_ordinal BETWEEN ?2 AND ?3
47983
+ AND f.session_id = ?1 AND f.message_ordinal BETWEEN ?2 AND ?3
47984
+ ORDER BY m.message_ordinal` : `SELECT message_ordinal AS ordinal, message_id AS id, role, content
47985
+ FROM message_history_fts
47986
+ WHERE session_id = ?1 AND message_ordinal BETWEEN ?2 AND ?3
47987
+ ORDER BY CAST(message_ordinal AS INTEGER)`);
47988
+ const blocks = [];
47989
+ let tokens = 0;
47990
+ let lastOrdinal = start - 1;
47991
+ let truncated = false;
47992
+ for (const row of rows.iterate(sessionId, start, end)) {
47993
+ if (!row.content)
47994
+ continue;
47995
+ if (!archiveOnly) {
47996
+ rawIds ??= readRawSessionMessageIdOrdinalsForRange2(sessionId, start, end);
47997
+ if (rawIds.get(row.id) === row.ordinal)
47998
+ continue;
47999
+ }
48000
+ const block = `[${row.ordinal}] ${row.role === "user" ? "U" : "A"}: ${row.content}`;
48001
+ const cost = estimateTokens(block);
48002
+ if (blocks.length > 0 && tokens + cost > tokenBudget) {
48003
+ truncated = true;
48004
+ break;
48005
+ }
48006
+ blocks.push(block);
48007
+ tokens += cost;
48008
+ lastOrdinal = row.ordinal;
48009
+ }
48010
+ if (blocks.length === 0)
48011
+ return null;
48012
+ const text = [
48013
+ "From the search archive: indexed text only; no surrounding messages or tool output.",
48014
+ "",
48015
+ ...blocks
48016
+ ];
48017
+ if (truncated) {
48018
+ text.push("", `Truncated at message ${lastOrdinal} (budget: ~${tokenBudget} tokens). Call ctx_expand(start=${lastOrdinal + 1}, end=${end}) for more archived text.`);
48019
+ }
48020
+ return text.join(`
48021
+ `);
48022
+ });
48023
+ }
48024
+
47242
48025
  // src/tools/ctx-expand/tools.ts
47243
48026
  var ctxExpandArgsShape = {
47244
48027
  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."),
@@ -47272,7 +48055,7 @@ function createCtxExpandTool(deps) {
47272
48055
  return renderItemByTag(deps.db, sessionId, mode.tag);
47273
48056
  }
47274
48057
  if (mode.kind === "message") {
47275
- return renderMessageByOrdinal(sessionId, mode.message);
48058
+ return renderSearchArchive(deps.db, sessionId, mode.message, mode.message, CTX_EXPAND_TOKEN_BUDGET) ?? renderMessageByOrdinal(sessionId, mode.message);
47276
48059
  }
47277
48060
  const { start, end, verbose } = mode;
47278
48061
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
@@ -47280,6 +48063,9 @@ function createCtxExpandTool(deps) {
47280
48063
  return `Range ${start}-${end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`;
47281
48064
  }
47282
48065
  const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(end, lastCompartmentEnd) : end;
48066
+ const archived = renderSearchArchive(deps.db, sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
48067
+ if (archived)
48068
+ return archived;
47283
48069
  if (verbose) {
47284
48070
  const v = renderVerboseRange(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET, deps.expandTools);
47285
48071
  if (!v.text) {
@@ -48106,9 +48892,9 @@ function isMissingError(error) {
48106
48892
  }
48107
48893
 
48108
48894
  // ../retina-local-fs/src/provider.ts
48109
- import { execFile as execFile3 } from "node:child_process";
48110
- import { promisify as promisify3 } from "node:util";
48111
- var execFileAsync2 = promisify3(execFile3);
48895
+ import { execFile as execFile4 } from "node:child_process";
48896
+ import { promisify as promisify4 } from "node:util";
48897
+ var execFileAsync3 = promisify4(execFile4);
48112
48898
  function parseSemver(value) {
48113
48899
  const match = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
48114
48900
  if (!match) {
@@ -48757,7 +49543,7 @@ function formatCtxReduceReply(stampedCount, selfStampCount = 0) {
48757
49543
  return parts.join(" ");
48758
49544
  }
48759
49545
  // src/tools/ctx-reduce/tools.ts
48760
- import { createHash as createHash16 } from "node:crypto";
49546
+ import { createHash as createHash17 } from "node:crypto";
48761
49547
 
48762
49548
  // src/features/magic-context/range-parser.ts
48763
49549
  function parseRangeString(input) {
@@ -48820,11 +49606,11 @@ function createCtxReduceTool(deps) {
48820
49606
  const stableId = `oc-${sessionId}-${callId}`;
48821
49607
  if (Buffer.byteLength(stableId) <= 128)
48822
49608
  return stableId;
48823
- return `oc-${createHash16("sha256").update(stableId).digest("hex")}`;
49609
+ return `oc-${createHash17("sha256").update(stableId).digest("hex")}`;
48824
49610
  }
48825
49611
  fallbackCommandSequence += 1;
48826
49612
  const monotonicId = `oc-${sessionId}-${fallbackCommandSequence}`;
48827
- return Buffer.byteLength(monotonicId) <= 128 ? monotonicId : `oc-${createHash16("sha256").update(monotonicId).digest("hex")}`;
49613
+ return Buffer.byteLength(monotonicId) <= 128 ? monotonicId : `oc-${createHash17("sha256").update(monotonicId).digest("hex")}`;
48828
49614
  };
48829
49615
  return tool({
48830
49616
  description: CTX_REDUCE_DESCRIPTION,