@cortexkit/opencode-magic-context 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/README.md +26 -0
  2. package/dist/{announcement-f75t58v9.js → announcement-y35wzg2r.js} +1 -1
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/live-run-config.d.ts.map +1 -1
  5. package/dist/config/migrate-config-location.d.ts.map +1 -1
  6. package/dist/config/schema/magic-context.d.ts +10 -4
  7. package/dist/config/schema/magic-context.d.ts.map +1 -1
  8. package/dist/{conflict-warning-hook-5yrz2r24.js → conflict-warning-hook-7fj2b9jw.js} +6 -5
  9. package/dist/context-8dkpszy5.js +4483 -0
  10. package/dist/{derive-budgets-a63zsm5c.js → derive-budgets-7nj7r1rv.js} +2 -2
  11. package/dist/embedding-worker.js +4 -4
  12. package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts +45 -0
  13. package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts.map +1 -0
  14. package/dist/features/magic-context/checkout-claim.d.ts +155 -0
  15. package/dist/features/magic-context/checkout-claim.d.ts.map +1 -0
  16. package/dist/features/magic-context/compaction-marker.d.ts +20 -3
  17. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  18. package/dist/features/magic-context/compartment-storage.d.ts +23 -2
  19. package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
  20. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  21. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/provider-output-failure.d.ts +6 -2
  23. package/dist/features/magic-context/dreamer/provider-output-failure.d.ts.map +1 -1
  24. package/dist/features/magic-context/dreamer/provider-output-failure.test-support.d.ts +30 -0
  25. package/dist/features/magic-context/dreamer/provider-output-failure.test-support.d.ts.map +1 -0
  26. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
  27. package/dist/features/magic-context/dreamer/task-executor.d.ts +9 -0
  28. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  29. package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
  30. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  31. package/dist/features/magic-context/dreamer/verify-diff.d.ts.map +1 -1
  32. package/dist/features/magic-context/dreamer/verify-gate.d.ts.map +1 -1
  33. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  34. package/dist/features/magic-context/git-commits/search-git-commits.d.ts.map +1 -1
  35. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  36. package/dist/features/magic-context/memory/project-identity-cache.d.ts.map +1 -1
  37. package/dist/features/magic-context/memory/storage-memory-verifications.d.ts.map +1 -1
  38. package/dist/features/magic-context/memory/storage-memory.d.ts +3 -0
  39. package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
  40. package/dist/features/magic-context/memory/verification-paths.d.ts +2 -2
  41. package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -1
  42. package/dist/features/magic-context/merged-reasoning-decisions.d.ts.map +1 -1
  43. package/dist/features/magic-context/message-index-async.d.ts +3 -3
  44. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  45. package/dist/features/magic-context/message-index.d.ts +2 -0
  46. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  47. package/dist/features/magic-context/migration-v95-perf-indexes.d.ts +12 -0
  48. package/dist/features/magic-context/migration-v95-perf-indexes.d.ts.map +1 -0
  49. package/dist/features/magic-context/migration-worker-client.d.ts +4 -4
  50. package/dist/features/magic-context/migration-worker-client.d.ts.map +1 -1
  51. package/dist/features/magic-context/migrations.d.ts +1 -0
  52. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  53. package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
  54. package/dist/features/magic-context/mural/resolve-mural.d.ts +15 -4
  55. package/dist/features/magic-context/mural/resolve-mural.d.ts.map +1 -1
  56. package/dist/features/magic-context/mural/source-revision.d.ts +7 -0
  57. package/dist/features/magic-context/mural/source-revision.d.ts.map +1 -0
  58. package/dist/features/magic-context/mural/storage-mural-cues.d.ts.map +1 -1
  59. package/dist/features/magic-context/reclaim-protection.d.ts +15 -10
  60. package/dist/features/magic-context/reclaim-protection.d.ts.map +1 -1
  61. package/dist/features/magic-context/session-cache-ttl.d.ts +4 -2
  62. package/dist/features/magic-context/session-cache-ttl.d.ts.map +1 -1
  63. package/dist/features/magic-context/smart-notes/capabilities.d.ts +3 -0
  64. package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
  65. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  66. package/dist/features/magic-context/smart-notes/github-token.d.ts +5 -0
  67. package/dist/features/magic-context/smart-notes/github-token.d.ts.map +1 -0
  68. package/dist/features/magic-context/smart-notes/runner.d.ts +1 -0
  69. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  70. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +12 -0
  71. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
  72. package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts +6 -0
  73. package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts.map +1 -1
  74. package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -1
  75. package/dist/features/magic-context/smart-notes/types.d.ts +2 -0
  76. package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -1
  77. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  78. package/dist/features/magic-context/storage-db.d.ts +6 -9
  79. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  80. package/dist/features/magic-context/storage-meta-shared.d.ts +1 -1
  81. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  82. package/dist/features/magic-context/storage-ops.d.ts +8 -1
  83. package/dist/features/magic-context/storage-ops.d.ts.map +1 -1
  84. package/dist/features/magic-context/storage-replay-document.d.ts +7 -0
  85. package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -1
  86. package/dist/features/magic-context/storage-schema-helpers.d.ts +2 -0
  87. package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
  88. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  89. package/dist/features/magic-context/storage-source.d.ts.map +1 -1
  90. package/dist/features/magic-context/storage-tags.d.ts +11 -3
  91. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  92. package/dist/features/magic-context/storage.d.ts +2 -2
  93. package/dist/features/magic-context/storage.d.ts.map +1 -1
  94. package/dist/features/magic-context/temporal-decisions.d.ts +10 -0
  95. package/dist/features/magic-context/temporal-decisions.d.ts.map +1 -0
  96. package/dist/features/magic-context/tool-definition-tokens.d.ts +2 -0
  97. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  98. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  99. package/dist/features/magic-context/types.d.ts +7 -6
  100. package/dist/features/magic-context/types.d.ts.map +1 -1
  101. package/dist/features/magic-context/window-report-ledger.d.ts.map +1 -1
  102. package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
  103. package/dist/hooks/magic-context/apply-operations.d.ts +8 -0
  104. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  105. package/dist/hooks/magic-context/cache-busting-signals.d.ts +6 -3
  106. package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
  107. package/dist/hooks/magic-context/channel2-cycle.d.ts +2 -0
  108. package/dist/hooks/magic-context/channel2-cycle.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +7 -0
  110. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  111. package/dist/hooks/magic-context/compartment-prompt.d.ts +2 -2
  112. package/dist/hooks/magic-context/compartment-prompt.d.ts.map +1 -1
  113. package/dist/hooks/magic-context/compartment-render-epoch.d.ts +8 -0
  114. package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -1
  115. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +18 -4
  116. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
  117. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  118. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  119. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  120. package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +12 -0
  121. package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
  122. package/dist/hooks/magic-context/decision-calibration.d.ts +3 -0
  123. package/dist/hooks/magic-context/decision-calibration.d.ts.map +1 -1
  124. package/dist/hooks/magic-context/default-protection-pressure.test-support.d.ts +37 -0
  125. package/dist/hooks/magic-context/default-protection-pressure.test-support.d.ts.map +1 -0
  126. package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts +4 -0
  127. package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts.map +1 -1
  128. package/dist/hooks/magic-context/edit-marker.d.ts.map +1 -1
  129. package/dist/hooks/magic-context/emergency-drop.d.ts +2 -0
  130. package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
  131. package/dist/hooks/magic-context/emergency-fail-closed.d.ts +21 -1
  132. package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
  133. package/dist/hooks/magic-context/event-handler.d.ts +5 -0
  134. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  135. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +30 -1
  136. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
  137. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
  138. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  139. package/dist/hooks/magic-context/historian-prompt-fit.d.ts +4 -2
  140. package/dist/hooks/magic-context/historian-prompt-fit.d.ts.map +1 -1
  141. package/dist/hooks/magic-context/hook.d.ts +2 -1
  142. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  143. package/dist/hooks/magic-context/inject-compartments.d.ts +9 -10
  144. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  145. package/dist/hooks/magic-context/lkg-measured-request.d.ts +69 -0
  146. package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -0
  147. package/dist/hooks/magic-context/lkg-persist.d.ts +2 -0
  148. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  149. package/dist/hooks/magic-context/lkg-replay-fit.d.ts +14 -0
  150. package/dist/hooks/magic-context/lkg-replay-fit.d.ts.map +1 -1
  151. package/dist/hooks/magic-context/lkg-replay.d.ts +10 -1
  152. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  153. package/dist/hooks/magic-context/lkg-slot.d.ts +4 -0
  154. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  155. package/dist/hooks/magic-context/module-state-sync.d.ts +2 -0
  156. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  157. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  158. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  159. package/dist/hooks/magic-context/note-nudger.d.ts +17 -5
  160. package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
  161. package/dist/hooks/magic-context/read-session-chunk.d.ts +1 -1
  162. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  163. package/dist/hooks/magic-context/read-session-db.d.ts +1 -0
  164. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  165. package/dist/hooks/magic-context/read-session-formatting.d.ts +7 -0
  166. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  167. package/dist/hooks/magic-context/read-session-raw.d.ts +4 -0
  168. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  169. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  170. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +2 -0
  171. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  172. package/dist/hooks/magic-context/reference-retrieval.d.ts +27 -17
  173. package/dist/hooks/magic-context/reference-retrieval.d.ts.map +1 -1
  174. package/dist/hooks/magic-context/request-hook-order.d.ts +56 -0
  175. package/dist/hooks/magic-context/request-hook-order.d.ts.map +1 -0
  176. package/dist/hooks/magic-context/rust-mode-transform.d.ts +22 -3
  177. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  178. package/dist/hooks/magic-context/strip-content.d.ts +4 -2
  179. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  180. package/dist/hooks/magic-context/supersession-reclaim.d.ts +4 -0
  181. package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
  182. package/dist/hooks/magic-context/system-prompt-hash.d.ts +7 -0
  183. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  184. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +12 -3
  185. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  186. package/dist/hooks/magic-context/temporal-awareness.d.ts +6 -3
  187. package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -1
  188. package/dist/hooks/magic-context/temporal-served-projection.d.ts +4 -0
  189. package/dist/hooks/magic-context/temporal-served-projection.d.ts.map +1 -0
  190. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  191. package/dist/hooks/magic-context/tool-reclaim.d.ts +2 -0
  192. package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -1
  193. package/dist/hooks/magic-context/transform-message-helpers.d.ts +5 -0
  194. package/dist/hooks/magic-context/transform-message-helpers.d.ts.map +1 -1
  195. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +41 -17
  196. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  197. package/dist/hooks/magic-context/transform.d.ts +12 -0
  198. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  199. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  200. package/dist/{index-vvtfyw1z.js → index-0d3g3461.js} +5 -13
  201. package/dist/index-1bc61ans.js +154 -0
  202. package/dist/{index-n96ag4rk.js → index-1zx87sdm.js} +5 -1
  203. package/dist/{index-4krbw8z9.js → index-3h0ybwah.js} +608 -102
  204. package/dist/{index-gd5qx1j9.js → index-5eqk6z3s.js} +62 -1
  205. package/dist/index-5gq3b2ya.js +494 -0
  206. package/dist/{index-75zjgwga.js → index-7qw742ev.js} +1 -1
  207. package/dist/{index-f85teqck.js → index-8da2zjhz.js} +3 -256
  208. package/dist/{index-w9jwmntv.js → index-b35pxds2.js} +9 -1
  209. package/dist/{index-8fgkmxmy.js → index-e2dtv0ra.js} +4 -3
  210. package/dist/{v2/server-a4m2b1yv.js → index-fg34g6a5.js} +55 -1
  211. package/dist/{index-xgt9k6c6.js → index-j7fvnjye.js} +2 -1
  212. package/dist/{index-2aq21j41.js → index-k4404na5.js} +418 -127
  213. package/dist/{index-2ntg5sd4.js → index-k5bdx1bx.js} +9 -5
  214. package/dist/{index-bmzf7zxv.js → index-majxda46.js} +23 -5
  215. package/dist/{index-16ny736w.js → index-p821bjdb.js} +14406 -13726
  216. package/dist/{index-remp7e2g.js → index-q4yp4b52.js} +16 -1
  217. package/dist/index-qr6r7akd.js +28505 -0
  218. package/dist/{index-fr2z4qy8.js → index-r5hmbx3t.js} +132 -169
  219. package/dist/index-sfnj6h8h.js +259 -0
  220. package/dist/index-sj29b3z2.js +30 -0
  221. package/dist/{index-q7yesyk9.js → index-yasgb8ej.js} +23 -24
  222. package/dist/index.d.ts.map +1 -1
  223. package/dist/index.js +1983 -33118
  224. package/dist/{message-time-backfill-an311pmk.js → message-time-backfill-68gj6t3e.js} +2 -2
  225. package/dist/migration-worker.js +12 -7
  226. package/dist/plugin/dream-timer.d.ts +25 -1
  227. package/dist/plugin/dream-timer.d.ts.map +1 -1
  228. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -1
  229. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  230. package/dist/plugin/messages-transform.d.ts +13 -0
  231. package/dist/plugin/messages-transform.d.ts.map +1 -1
  232. package/dist/plugin/rpc-handlers.d.ts +4 -0
  233. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  234. package/dist/plugin/subagent-tool-policy.d.ts +14 -0
  235. package/dist/plugin/subagent-tool-policy.d.ts.map +1 -0
  236. package/dist/plugin/tool-registry.d.ts +1 -0
  237. package/dist/plugin/tool-registry.d.ts.map +1 -1
  238. package/dist/{read-session-chunk-8rxs0tc9.js → read-session-chunk-egz7r68d.js} +8 -8
  239. package/dist/{recomp-orchestrator-nzamtw29.js → recomp-orchestrator-ejq7ex5d.js} +15 -15
  240. package/dist/{send-session-notification-nqpv7cne.js → send-session-notification-dyw42dtr.js} +4 -4
  241. package/dist/shared/announcement.d.ts.map +1 -1
  242. package/dist/shared/cache-ttl-display.d.ts +6 -4
  243. package/dist/shared/cache-ttl-display.d.ts.map +1 -1
  244. package/dist/shared/model-cache-ttl.d.ts +1 -1
  245. package/dist/shared/model-cache-ttl.d.ts.map +1 -1
  246. package/dist/shared/model-suggestion-retry.d.ts +2 -0
  247. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  248. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  249. package/dist/shared/protected-tools-policy.d.ts +6 -0
  250. package/dist/shared/protected-tools-policy.d.ts.map +1 -0
  251. package/dist/shared/quota-diagnostic.d.ts +4 -0
  252. package/dist/shared/quota-diagnostic.d.ts.map +1 -0
  253. package/dist/shared/rpc-server.d.ts.map +1 -1
  254. package/dist/shared/sqlite-context-pragmas.d.ts +12 -0
  255. package/dist/shared/sqlite-context-pragmas.d.ts.map +1 -0
  256. package/dist/shared/sqlite-helpers.d.ts +9 -0
  257. package/dist/shared/sqlite-helpers.d.ts.map +1 -1
  258. package/dist/shared/sqlite.d.ts +10 -0
  259. package/dist/shared/sqlite.d.ts.map +1 -1
  260. package/dist/shared/status-summary.d.ts +1 -0
  261. package/dist/shared/status-summary.d.ts.map +1 -1
  262. package/dist/shared/status-view-check.d.ts.map +1 -1
  263. package/dist/shared/status-view.d.ts +4 -0
  264. package/dist/shared/status-view.d.ts.map +1 -1
  265. package/dist/shared/storage-permissions.d.ts +24 -0
  266. package/dist/shared/storage-permissions.d.ts.map +1 -1
  267. package/dist/shared/temporal-upgrade-fixture.d.ts +19 -0
  268. package/dist/shared/temporal-upgrade-fixture.d.ts.map +1 -0
  269. package/dist/shared/user-facing-codes.d.ts +5 -0
  270. package/dist/shared/user-facing-codes.d.ts.map +1 -1
  271. package/dist/shared/write-transaction-timing.d.ts +8 -1
  272. package/dist/shared/write-transaction-timing.d.ts.map +1 -1
  273. package/dist/tools/ctx-expand/render.d.ts.map +1 -1
  274. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  275. package/dist/tools/ctx-reduce/constants.d.ts +2 -0
  276. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  277. package/dist/tools/ctx-reduce/tools.d.ts +4 -4
  278. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  279. package/dist/{transform-postprocess-phase-414phndq.js → transform-postprocess-phase-tz3pat2p.js} +15 -15
  280. package/dist/v2/context-8vgtb6sz.js +52166 -0
  281. package/dist/v2/{derive-budgets-v0krf3t7.js → derive-budgets-kdgp4s3r.js} +4 -3
  282. package/dist/v2/embedding-worker.js +6 -5
  283. package/dist/v2/fold/boundary.d.ts +2 -0
  284. package/dist/v2/fold/boundary.d.ts.map +1 -1
  285. package/dist/v2/fold/owner.d.ts.map +1 -1
  286. package/dist/v2/hooks/channel2.d.ts +2 -0
  287. package/dist/v2/hooks/channel2.d.ts.map +1 -1
  288. package/dist/v2/hooks/context.d.ts +2 -2
  289. package/dist/v2/hooks/context.d.ts.map +1 -1
  290. package/dist/v2/hooks/dream-timer.d.ts +39 -0
  291. package/dist/v2/hooks/dream-timer.d.ts.map +1 -0
  292. package/dist/v2/hooks/payload.d.ts.map +1 -1
  293. package/dist/v2/hooks/store.d.ts.map +1 -1
  294. package/dist/v2/hooks/tools.d.ts +1 -1
  295. package/dist/v2/hooks/tools.d.ts.map +1 -1
  296. package/dist/v2/hooks/update-check.d.ts.map +1 -1
  297. package/dist/v2/{message-time-backfill-0n93vrd7.js → message-time-backfill-r6qkem1x.js} +3 -3
  298. package/dist/v2/migration-worker.js +13 -8
  299. package/dist/v2/{read-session-chunk-hjza38hr.js → read-session-chunk-8vjp6581.js} +9 -9
  300. package/dist/v2/recomp-orchestrator-5xj0yzyk.js +39 -0
  301. package/dist/v2/{safe-notification-target-wtj0bbc2.js → safe-notification-target-eb1n1vfb.js} +2 -2
  302. package/dist/v2/{send-session-notification-mq9frrtm.js → send-session-notification-qs0j78v6.js} +6 -6
  303. package/dist/v2/server-14vq4kpm.js +30 -0
  304. package/dist/v2/server-1bc61ans.js +154 -0
  305. package/dist/v2/{server-n96ag4rk.js → server-1zx87sdm.js} +5 -1
  306. package/dist/v2/server-3n4q9xme.js +79 -0
  307. package/dist/v2/server-4663ejam.js +3351 -0
  308. package/dist/v2/{server-rhdgxp1t.js → server-51dv1pr5.js} +63 -2
  309. package/dist/v2/{server-5317ykaf.js → server-6r257mf5.js} +1 -1
  310. package/dist/v2/server-84w6rvvf.js +14 -0
  311. package/dist/v2/{server-qpg6wf72.js → server-ajzeteqw.js} +9 -1
  312. package/dist/{index-a4m2b1yv.js → v2/server-fg34g6a5.js} +55 -1
  313. package/dist/v2/{server-e36nf2p1.js → server-frbm99q5.js} +1595 -1599
  314. package/dist/v2/{server-remp7e2g.js → server-g0fjrkta.js} +16 -1
  315. package/dist/v2/{server-z19x7eqz.js → server-mcfawjgc.js} +4 -4
  316. package/dist/v2/server-p2en78c8.js +3892 -0
  317. package/dist/v2/{server-hfjfbg59.js → server-qp5hx1ad.js} +608 -105
  318. package/dist/v2/{server-c4g2y1z8.js → server-vnh7r35f.js} +2778 -5383
  319. package/dist/v2/server-vs5gpnd4.js +493 -0
  320. package/dist/v2/{server-w99cyt6a.js → server-wvjvfhck.js} +14 -6
  321. package/dist/v2/{server-v5xgft5v.js → server-xtp687da.js} +186 -3533
  322. package/dist/v2/{server-0jqw05sa.js → server-y8maxeft.js} +6 -84
  323. package/dist/v2/server.d.ts.map +1 -1
  324. package/dist/v2/server.js +104 -49288
  325. package/dist/v2/store-reader.d.ts +39 -1
  326. package/dist/v2/store-reader.d.ts.map +1 -1
  327. package/dist/v2/tool-result.d.ts +8 -0
  328. package/dist/v2/tool-result.d.ts.map +1 -0
  329. package/dist/v2/tui/index.d.ts.map +1 -1
  330. package/dist/v2/{wrapup-orchestrator-tbyx78dz.js → wrapup-orchestrator-r5t1pyj8.js} +20 -16
  331. package/dist/wrapup-orchestrator-kw3x3myw.js +26 -0
  332. package/package.json +22 -7
  333. package/src/config/index.ts +21 -4
  334. package/src/config/latch-permanence-guard.test.ts +6 -1
  335. package/src/config/live-run-config.ts +1 -0
  336. package/src/config/migrate-config-location.test.ts +31 -1
  337. package/src/config/migrate-config-location.ts +8 -41
  338. package/src/config/protected-tools.test.ts +70 -0
  339. package/src/config/raw-loader.test.ts +15 -0
  340. package/src/config/raw-loader.ts +1 -1
  341. package/src/config/removed-agent-source-fence.test.ts +10 -1
  342. package/src/config/schema/magic-context.test.ts +20 -1
  343. package/src/config/schema/magic-context.ts +21 -7
  344. package/src/shared/announcement.ts +6 -5
  345. package/src/shared/cache-ttl-display.test.ts +1 -1
  346. package/src/shared/cache-ttl-display.ts +17 -16
  347. package/src/shared/fold-render-lock-fence.test.ts +84 -0
  348. package/src/shared/fold-render-lock-fixtures.test.ts +276 -0
  349. package/src/shared/model-cache-ttl.test.ts +2 -2
  350. package/src/shared/model-cache-ttl.ts +8 -4
  351. package/src/shared/model-suggestion-retry.test.ts +104 -1
  352. package/src/shared/model-suggestion-retry.ts +63 -6
  353. package/src/shared/models-dev-cache.ts +3 -11
  354. package/src/shared/protected-tools-policy.ts +23 -0
  355. package/src/shared/quota-diagnostic.ts +9 -0
  356. package/src/shared/rpc-server.ts +6 -54
  357. package/src/shared/sqlite-context-pragmas.test.ts +105 -0
  358. package/src/shared/sqlite-context-pragmas.ts +24 -0
  359. package/src/shared/sqlite-helpers.ts +17 -0
  360. package/src/shared/sqlite.ts +75 -0
  361. package/src/shared/status-detail-text.test.ts +18 -1
  362. package/src/shared/status-summary.ts +11 -0
  363. package/src/shared/status-view-check.ts +5 -0
  364. package/src/shared/status-view.test.ts +11 -2
  365. package/src/shared/status-view.ts +12 -1
  366. package/src/shared/storage-permissions.test.ts +204 -0
  367. package/src/shared/storage-permissions.ts +192 -0
  368. package/src/shared/temporal-upgrade-fixture.ts +50 -0
  369. package/src/shared/user-facing-codes.ts +6 -0
  370. package/src/shared/write-transaction-timing.test.ts +39 -0
  371. package/src/shared/write-transaction-timing.ts +25 -1
  372. package/src/tui/dialogs/status-dialog-render.test.ts +1 -1
  373. package/src/tui/index.tsx +3 -1
  374. package/src/tui/startup-order.test.ts +16 -0
  375. package/src/tui-compiled/index.tsx +3 -1
  376. package/src/v2/tui/index.ts +9 -2
  377. package/dist/index-anq4nn6f.js +0 -131
  378. package/dist/index-e44zzy84.js +0 -10
  379. package/dist/index-x23gah8c.js +0 -16
  380. package/dist/v2/recomp-orchestrator-1aq7h44c.js +0 -36
  381. package/dist/v2/server-e44zzy84.js +0 -10
  382. package/dist/v2/server-fgmxwrgy.js +0 -16
  383. package/dist/v2/server-r7cmye3k.js +0 -131
  384. package/dist/wrapup-orchestrator-h28bqsy6.js +0 -26
  385. package/dist/v2/{server-ckpc8pxy.js → server-1qbnzc80.js} +8 -8
@@ -8,6 +8,7 @@ import type {
8
8
  } from "../../features/magic-context/dreamer/task-registry";
9
9
  import { toolTemplateError } from "../../shared/historian-tool-template";
10
10
  import { isValidPromptSurfaceModelKey } from "../../shared/prompt-surface";
11
+ import { DEFAULT_PROTECTED_TOOLS, mergeProtectedTools } from "../../shared/protected-tools-policy";
11
12
  import { AgentOverrideConfigSchema } from "./agent-overrides";
12
13
 
13
14
  export const DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
@@ -918,6 +919,8 @@ export interface MuralConfig {
918
919
  }
919
920
 
920
921
  export interface MagicContextConfig {
922
+ /** The loader sets this to distinguish an explicit `5m` from the built-in default; users do not configure it. */
923
+ cacheTtlConfigured?: boolean;
921
924
  enabled: boolean;
922
925
  /** User-level setting that lets a session in the canonical home directory use project memory. */
923
926
  allow_home_project: boolean;
@@ -955,6 +958,7 @@ export interface MagicContextConfig {
955
958
  execute_threshold_tokens?: { default?: number; [modelKey: string]: number | undefined };
956
959
  protected_tokens?: number;
957
960
  protected_tags?: number;
961
+ protected_tools: Record<string, number>;
958
962
  clear_reasoning_age: number;
959
963
  history_budget_percentage: number;
960
964
  historian_timeout_ms: number;
@@ -1038,7 +1042,8 @@ export interface MagicContextConfig {
1038
1042
  * on its own; when off, the messages sent to the model are byte-identical to
1039
1043
  * the age-based-only behavior. Experimental, opt-in, default off until cache
1040
1044
  * stability is proven. */
1041
- smart_drops: boolean;
1045
+ /** Deprecated and ignored: supersession reclaim is always on. */
1046
+ smart_drops?: unknown;
1042
1047
  /**
1043
1048
  * Age-tier caveman compression for long user/assistant text parts.
1044
1049
  * Graduated from `experimental.caveman_text_compression`; opt-in, default off.
@@ -1187,7 +1192,7 @@ export const MagicContextConfigSchema = z
1187
1192
  .union([z.string(), z.object({ default: z.string() }).catchall(z.string())])
1188
1193
  .default("5m")
1189
1194
  .describe(
1190
- 'How long Magic Context assumes the provider\'s cached prefix stays valid. This is MC\'s own deferral gate — it does not change the provider\'s actual cache lifetime. String (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "provider/model": "1h", "provider/*": "never" }); keys resolve most-specific first (exact provider/model, bare model ID, shorter dash-prefixes, then the provider/* wildcard). Explicit per-model entries win; otherwise GPT-5.6 and later (including gpt-6*, through any provider prefix) use a built-in 30m lifetime before the object default or 5m fallback. An unset or global "5m" opts into built-in defaults; any other global string is an explicit policy and wins. Policy is frozen per session, including across restarts; a model switch resolves against that frozen policy. /ctx-status shows the effective value and source. OpenAI documents at least 30 minutes since the latest write or reuse: https://developers.openai.com/api/docs/guides/prompt-caching (Cache lifetime and Summary of model differences). Set to "never" to mean MC never assumes expiry (for lanes kept warm externally by a cache-keep tool) — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time. Provider-side extended TTL is a separate request-level concern (cache_control: { ttl } in the request body).',
1195
+ 'How long Magic Context assumes the provider\'s cached prefix stays valid. This is MC\'s own deferral gate — it does not change the provider\'s actual cache lifetime. String (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "provider/model": "1h", "provider/*": "never" }); keys resolve most-specific first (exact provider/model, bare model ID, shorter dash-prefixes, then the provider/* wildcard, then default). User and project settings, including an explicit "5m" or object default, apply on the next pass. Only built-in defaults are frozen per session, including across restarts: when unset, GPT-5.6 and later (including gpt-6*, through any provider prefix) use 30m, other models use 5m. /ctx-status shows the effective TTL and whether it comes from your config or a frozen built-in default. Editing the TTL does not itself rewrite prompt bytes; a lowered TTL rebuilds only after normal idle expiry. OpenAI documents at least 30 minutes since the latest write or reuse: https://developers.openai.com/api/docs/guides/prompt-caching (Cache lifetime and Summary of model differences). Set to "never" to mean MC never assumes expiry (for lanes kept warm externally by a cache-keep tool) — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time. Provider-side extended TTL is a separate request-level concern (cache_control: { ttl } in the request body).',
1191
1196
  ),
1192
1197
  prompt_surface: PromptSurfaceConfigSchema.default({ default: "full" }).describe(
1193
1198
  "Prompt-surface presets: default is full; models use bare model IDs, provider/model, or provider/* routing keys. Guidance and tool-description overrides are user-level only. OpenCode 1.x, Pi, and OMP register tool descriptions once per process (they follow the default preset). OpenCode 2 rewrites the five ctx_* descriptions per request from the draft model.",
@@ -1346,12 +1351,12 @@ export const MagicContextConfigSchema = z
1346
1351
  .boolean()
1347
1352
  .default(true)
1348
1353
  .describe(
1349
- "When true (default), Magic Context creates and re-tightens its storage directories to owner-only 0700 and storage files to owner-only 0600. Set false only for a deliberate trusted-group deployment whose operator manages directory, database, WAL/SHM, cache, and RPC file permissions externally; Magic Context then never chmods or supplies restrictive creation modes. USER-LEVEL ONLY — ignored in project config for security. On Windows, POSIX chmod modes are already meaningless, so this setting is a no-op.",
1354
+ "When true (default), Magic Context creates and re-tightens its TypeScript-mode storage directories to owner-only 0700 and storage files to owner-only 0600. Set false only for a deliberate trusted-group deployment whose operator manages those files externally; in TypeScript mode Magic Context then skips its own creation modes and recursive tightening. In Rust transform mode, ck-mc's store library always keeps the shared data directory owner-only 0700, regardless of this setting. USER-LEVEL ONLY — ignored in project config for security. On Windows, POSIX chmod modes are already meaningless, so this setting is a no-op.",
1350
1355
  ),
1351
1356
  })
1352
1357
  .default({ enforce_private_permissions: true })
1353
1358
  .describe(
1354
- "Storage permission policy. The default keeps session content and memories owner-private. Disabling enforcement is for trusted shared-group storage managed externally; every group member able to read the storage can read all stored session content and memories.",
1359
+ "Storage permission policy. The default keeps session content and memories owner-private. Disabling enforcement is for trusted shared-group storage managed externally; TypeScript mode follows that policy, but Rust transform mode always keeps its shared data directory owner-only because ck-mc's store library enforces it.",
1355
1360
  ),
1356
1361
  embedding: EmbeddingConfigSchema.default({
1357
1362
  provider: "local",
@@ -1438,10 +1443,18 @@ export const MagicContextConfigSchema = z
1438
1443
  "Pi-only child-process extension controls. This setting is user-level only; project configuration cannot choose which extensions a user's subagent children load.",
1439
1444
  ),
1440
1445
  smart_drops: z
1441
- .boolean()
1442
- .default(false)
1446
+ .unknown()
1447
+ .optional()
1448
+ .describe(
1449
+ "Deprecated: ignored. Content-aware supersession reclaim is always on, only on passes already rebuilding the cache. Remove this key; it no longer does anything.",
1450
+ )
1451
+ .meta({ deprecated: true }),
1452
+ protected_tools: z
1453
+ .record(z.string(), z.number().int().nonnegative())
1454
+ .default({ ...DEFAULT_PROTECTED_TOOLS })
1455
+ .transform((map) => mergeProtectedTools(map))
1443
1456
  .describe(
1444
- "Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 3), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart.",
1457
+ "Keep each tool's newest N still-active results in every automatic drop lane (issue 621). User and project maps merge over defaults {todowrite: 1, ctx_reduce: 3}; 0 turns protection off. Names are case-insensitive and ignore leading mcp_. Holds even at 95% pressure, with no byte cap: large protected outputs can reach refusal sooner. Queued drops, from the agent or historian publication, are held until newer calls displace the result and a later cache-rebuilding pass applies them. The historian's summary is unaffected; the raw result leaves at the next fold. Frozen strips are unaffected. Changes and rotation never originate a bust; dropped results are never restored.",
1445
1458
  ),
1446
1459
  caveman_text_compression: z
1447
1460
  .object({
@@ -1571,6 +1584,7 @@ export const MagicContextConfigSchema = z
1571
1584
 
1572
1585
  /** Settings whose fresh values can be used by later agent runs without changing rendered prompt bytes. */
1573
1586
  export const LIVE_RELOAD_CONFIG_PATHS = [
1587
+ "cache_ttl",
1574
1588
  "mural.model",
1575
1589
  "toast_duration_ms",
1576
1590
  "historian.opencode.model",
@@ -15,10 +15,11 @@
15
15
  * Leave both empty (`""` and `[]`) to skip the dialog entirely.
16
16
  */
17
17
 
18
- import * as fs from "node:fs";
18
+ import { existsSync, readFileSync } from "node:fs";
19
19
  import * as path from "node:path";
20
20
  import { compareSemverCore } from "../hooks/auto-update-checker/semver";
21
21
  import { getMagicContextStorageDir } from "./data-path";
22
+ import { ensureStorageDirectorySync, writeStorageFileAtomicSync } from "./storage-permissions";
22
23
 
23
24
  /**
24
25
  * Bump only when there are user-visible changes worth a startup dialog.
@@ -60,8 +61,8 @@ type AnnouncementStateRead =
60
61
  function readAnnouncementState(): AnnouncementStateRead {
61
62
  try {
62
63
  const file = getStateFilePath();
63
- if (!fs.existsSync(file)) return { status: "missing" };
64
- const version = fs.readFileSync(file, "utf-8").trim();
64
+ if (!existsSync(file)) return { status: "missing" };
65
+ const version = readFileSync(file, "utf-8").trim();
65
66
  if (!version) return { status: "error" };
66
67
  return { status: "valid", version };
67
68
  } catch {
@@ -88,8 +89,8 @@ export function markAnnouncementSeen(version: string): void {
88
89
  if (!version) return;
89
90
  try {
90
91
  const dir = getMagicContextStorageDir();
91
- fs.mkdirSync(dir, { recursive: true });
92
- fs.writeFileSync(getStateFilePath(), version);
92
+ ensureStorageDirectorySync(dir);
93
+ writeStorageFileAtomicSync(getStateFilePath(), version);
93
94
  } catch {
94
95
  // best-effort
95
96
  }
@@ -45,7 +45,7 @@ describe("resolveCacheTtlDisplay", () => {
45
45
  });
46
46
 
47
47
  expect(configured.source).toBe("config");
48
- expect(session).toEqual({ value: "45m", source: "session", modelKey });
48
+ expect(session).toEqual({ value: "1h", source: "config", modelKey });
49
49
  expect(fallback).toEqual({ value: "5m", source: "default", modelKey });
50
50
  });
51
51
 
@@ -10,7 +10,7 @@ export interface CacheTtlDisplay {
10
10
  }
11
11
 
12
12
  export interface ResolveCacheTtlDisplayArgs {
13
- frozen?: ResolvedCacheTtl;
13
+ frozen?: ResolvedCacheTtl & { builtInDefault?: ResolvedCacheTtl };
14
14
  configured: MagicContextConfig["cache_ttl"];
15
15
  configuredExplicitly: boolean;
16
16
  modelKey: string | undefined;
@@ -20,12 +20,22 @@ export interface ResolveCacheTtlDisplayArgs {
20
20
  }
21
21
 
22
22
  /**
23
- * Resolve status-only TTL text without changing the scheduler's persisted truth.
24
- * Use the session value only when its persisted model key matches the model being
25
- * displayed; otherwise use live config because the session value may be for another model.
23
+ * Resolve the lifetime shown in status without changing the value saved for the scheduler.
24
+ * The next pass uses current settings; a saved built-in lifetime applies only to the same
25
+ * model, even after an override is removed.
26
26
  */
27
27
  export function resolveCacheTtlDisplay(args: ResolveCacheTtlDisplayArgs): CacheTtlDisplay {
28
- if (args.frozen) return args.frozen;
28
+ const resolved = resolveModelCacheTtl(
29
+ args.configured,
30
+ args.modelKey,
31
+ args.configuredExplicitly,
32
+ );
33
+ if (resolved.source === "config") return resolved;
34
+ if (args.frozen && (!args.modelKey || args.frozen.modelKey === args.modelKey)) {
35
+ return args.frozen.source === "config"
36
+ ? (args.frozen.builtInDefault ?? resolved)
37
+ : args.frozen;
38
+ }
29
39
  if (
30
40
  (args.sessionModelKey && (!args.modelKey || args.sessionModelKey === args.modelKey)) ||
31
41
  (!args.modelKey && !args.sessionModelKey && args.sessionValue !== "5m")
@@ -37,22 +47,13 @@ export function resolveCacheTtlDisplay(args: ResolveCacheTtlDisplayArgs): CacheT
37
47
  };
38
48
  }
39
49
 
40
- const resolved = resolveModelCacheTtl(args.configured, args.modelKey);
41
- if (
42
- resolved.source === "default" &&
43
- typeof args.configured === "string" &&
44
- args.configuredExplicitly
45
- )
46
- return { ...resolved, source: "config" };
47
50
  return resolved;
48
51
  }
49
52
 
50
53
  export function formatCacheTtlDisplay(display: CacheTtlDisplay): string {
51
- if (display.source === "OpenAI GPT-5.6+ default")
52
- return `Cache TTL: ${display.value} (${display.source})`;
53
54
  if (display.source === "session") return `Cache TTL: ${display.value} (session)`;
54
55
  if (display.source === "config") {
55
- return `Cache TTL: ${display.value} (config for ${display.modelKey ?? "current model"})`;
56
+ return `Cache TTL: ${display.value} (your config)`;
56
57
  }
57
- return `Cache TTL: ${display.value} (default — no cache_ttl for ${display.modelKey ?? "unknown model"})`;
58
+ return `Cache TTL: ${display.value} (built-in default, frozen for this session)`;
58
59
  }
@@ -0,0 +1,84 @@
1
+ import { expect, test } from "bun:test";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import ts from "typescript";
5
+
6
+ const root = resolve(import.meta.dir, "../../../../");
7
+
8
+ for (const [host, path, functions] of [
9
+ [
10
+ "OpenCode",
11
+ "packages/plugin/src/hooks/magic-context/inject-compartments.ts",
12
+ ["materializeM0", "softRefreshCachedM1"],
13
+ ],
14
+ [
15
+ "Pi",
16
+ "packages/pi-plugin/src/inject-compartments-pi.ts",
17
+ ["materializeM0Pi", "softRefreshCachedM1Pi"],
18
+ ],
19
+ ] as const) {
20
+ test(`${host} fold and refresh render m[1] inside their writer section`, () => {
21
+ const source = ts.createSourceFile(
22
+ path,
23
+ readFileSync(resolve(root, path), "utf8"),
24
+ ts.ScriptTarget.Latest,
25
+ true,
26
+ );
27
+ const inspected: string[] = [];
28
+ const violations: string[] = [];
29
+ function visit(node: ts.Node): void {
30
+ if (
31
+ ts.isFunctionDeclaration(node) &&
32
+ node.name &&
33
+ functions.some((name) => name === node.name?.text)
34
+ ) {
35
+ inspected.push(node.name.text);
36
+ const calls: ts.CallExpression[] = [];
37
+ function collect(child: ts.Node): void {
38
+ if (ts.isCallExpression(child)) calls.push(child);
39
+ ts.forEachChild(child, collect);
40
+ }
41
+ collect(node);
42
+ const admission = calls.find(
43
+ (call) =>
44
+ call.expression.getText(source).endsWith(".exec") &&
45
+ call.arguments[0]?.getText(source) === '"BEGIN IMMEDIATE"',
46
+ );
47
+ expect(admission).toBeDefined();
48
+ const commit = calls.find(
49
+ (call) =>
50
+ admission &&
51
+ call.pos > admission.pos &&
52
+ call.expression.getText(source).endsWith(".exec") &&
53
+ call.arguments[0]?.getText(source) === '"COMMIT"',
54
+ );
55
+ expect(commit).toBeDefined();
56
+ const renders = calls.filter((call) =>
57
+ /^renderM1(?:Pi)?WithMetadata$/.test(call.expression.getText(source)),
58
+ );
59
+ expect(renders).toHaveLength(1);
60
+ for (const render of renders) {
61
+ if (
62
+ !admission ||
63
+ !commit ||
64
+ render.pos < admission.pos ||
65
+ render.pos > commit.pos
66
+ )
67
+ violations.push(`${node.name.text}: m[1] render outside writer`);
68
+ }
69
+ for (const call of calls) {
70
+ if (
71
+ call.expression.getText(source).endsWith("onFoldPrepare") &&
72
+ admission &&
73
+ call.pos > admission.pos
74
+ )
75
+ violations.push(`${node.name.text}: wire preparation inside writer`);
76
+ }
77
+ }
78
+ ts.forEachChild(node, visit);
79
+ }
80
+ visit(source);
81
+ expect(inspected.sort()).toEqual([...functions].sort());
82
+ expect(violations).toEqual([]);
83
+ });
84
+ }
@@ -0,0 +1,276 @@
1
+ import { expect, test } from "bun:test";
2
+ import { rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { appendCompartments } from "../features/magic-context/compartment-storage";
6
+ import {
7
+ insertMemory,
8
+ setMemoryClassification,
9
+ updateMemorySeenCount,
10
+ updateMemoryVerification,
11
+ } from "../features/magic-context/memory/storage-memory";
12
+ import {
13
+ recordIndexedMessageTime,
14
+ recordMessageFtsRowid,
15
+ } from "../features/magic-context/message-fts-rowid-map";
16
+ import { runMigrations } from "../features/magic-context/migrations";
17
+ import { getOrCreateSessionMeta } from "../features/magic-context/storage";
18
+ import { initializeDatabase } from "../features/magic-context/storage-db";
19
+ import { recoverUnresolvedCompartments } from "../features/magic-context/store-generation-rebase";
20
+ import { renderMemoryBlockV2 } from "../hooks/magic-context/inject-compartments";
21
+ import { estimateTokens } from "../hooks/magic-context/read-session-formatting";
22
+ import { Database } from "./sqlite";
23
+ import { createTestTempDirFromPath } from "./test-temp-dir";
24
+
25
+ export interface LockedDeltaFixture {
26
+ db: Database;
27
+ sibling: Database;
28
+ directory: string;
29
+ sessionId: string;
30
+ projectPath: string;
31
+ close: () => void;
32
+ }
33
+
34
+ export function createLockedDeltaFixture(
35
+ label: string,
36
+ open?: (path: string) => Database,
37
+ ): LockedDeltaFixture {
38
+ const directory = createTestTempDirFromPath(join(tmpdir(), `mc-locked-delta-${label}-`));
39
+ const path = join(directory, "context.db");
40
+ const db = open ? open(path) : new Database(path);
41
+ if (!open) {
42
+ initializeDatabase(db);
43
+ runMigrations(db);
44
+ }
45
+ db.exec("PRAGMA journal_mode=WAL");
46
+ const sibling = new Database(path);
47
+ const sessionId = label;
48
+ const projectPath = "git:locked-delta";
49
+ getOrCreateSessionMeta(db, sessionId);
50
+ return {
51
+ db,
52
+ sibling,
53
+ directory,
54
+ sessionId,
55
+ projectPath,
56
+ close: () => {
57
+ sibling.close();
58
+ db.close();
59
+ rmSync(directory, { recursive: true, force: true });
60
+ },
61
+ };
62
+ }
63
+ export interface LockedDeltaOptions {
64
+ memoryBudget?: number;
65
+ temporalAwareness?: boolean;
66
+ }
67
+ interface LockedDeltaDriver<Markers> {
68
+ host: string;
69
+ create: (label: string) => LockedDeltaFixture;
70
+ materialize: (fixture: LockedDeltaFixture, options: LockedDeltaOptions) => Markers;
71
+ refresh: (
72
+ fixture: LockedDeltaFixture,
73
+ options: LockedDeltaOptions,
74
+ beforeAdmission: () => void,
75
+ ) => void;
76
+ render: (fixture: LockedDeltaFixture, options: LockedDeltaOptions, markers: Markers) => string;
77
+ temporalHeadings?: boolean;
78
+ }
79
+ function compartment() {
80
+ return {
81
+ sequence: 0,
82
+ startMessage: 1,
83
+ endMessage: 2,
84
+ startMessageId: "a",
85
+ endMessageId: "b",
86
+ title: "locked history",
87
+ content: "locked history",
88
+ p1: "locked history",
89
+ p2: "summary",
90
+ p3: "outcome",
91
+ p4: "anchor",
92
+ legacy: 0,
93
+ };
94
+ }
95
+ function persistedDelta(f: LockedDeltaFixture): string {
96
+ return getOrCreateSessionMeta(f.db, f.sessionId).cachedM1Bytes?.toString("utf8") ?? "";
97
+ }
98
+ function refreshAtAdmission<Markers>(
99
+ driver: LockedDeltaDriver<Markers>,
100
+ f: LockedDeltaFixture,
101
+ options: LockedDeltaOptions,
102
+ write: () => void,
103
+ ) {
104
+ let admissions = 0;
105
+ let hooks = 0;
106
+ const exec = f.db.exec.bind(f.db);
107
+ f.db.exec = (sql) => {
108
+ if (sql === "BEGIN IMMEDIATE") admissions++;
109
+ return exec(sql);
110
+ };
111
+ try {
112
+ driver.refresh(f, options, () => {
113
+ expect(admissions).toBe(0);
114
+ hooks++;
115
+ write();
116
+ });
117
+ expect(hooks).toBe(1);
118
+ expect(admissions).toBe(1);
119
+ } finally {
120
+ f.db.exec = exec;
121
+ }
122
+ }
123
+
124
+ /** These fixtures use real sibling writes, then compare persisted bytes to an independent live render. */
125
+ export function registerLockedDeltaRegressions<Markers>(driver: LockedDeltaDriver<Markers>): void {
126
+ for (const change of ["importance", "reinforcement", "verification"] as const) {
127
+ test(`${driver.host} m[1] reads memory ${change} under its writer`, () => {
128
+ const f = driver.create(`${driver.host}-${change}`);
129
+ try {
130
+ const sample = insertMemory(f.db, {
131
+ projectPath: "git:budget-only",
132
+ category: "ARCHITECTURE",
133
+ content: "candidate A ".repeat(40),
134
+ });
135
+ const options = {
136
+ memoryBudget:
137
+ 4 * (estimateTokens(renderMemoryBlockV2([sample], "new-memories")) + 2),
138
+ };
139
+ const markers = driver.materialize(f, options);
140
+ const a = insertMemory(f.db, {
141
+ projectPath: f.projectPath,
142
+ category: "ARCHITECTURE",
143
+ content: "candidate A ".repeat(40),
144
+ });
145
+ const b = insertMemory(f.db, {
146
+ projectPath: f.projectPath,
147
+ category: "ARCHITECTURE",
148
+ content: "candidate B ".repeat(40),
149
+ });
150
+ setMemoryClassification(f.db, a.id, { importance: 50 });
151
+ setMemoryClassification(f.db, b.id, { importance: 50 });
152
+ f.db
153
+ .prepare(
154
+ "UPDATE memories SET last_seen_at = CASE WHEN id = ? THEN 1000 ELSE 0 END, verified_at = NULL WHERE id IN (?, ?)",
155
+ )
156
+ .run(a.id, a.id, b.id);
157
+ const before = driver.render(f, options, markers);
158
+ expect(before).toContain("candidate A");
159
+ expect(before).not.toContain("candidate B");
160
+ refreshAtAdmission(driver, f, options, () => {
161
+ if (change === "importance")
162
+ setMemoryClassification(f.sibling, b.id, { importance: 100 });
163
+ else if (change === "reinforcement") updateMemorySeenCount(f.sibling, b.id);
164
+ else updateMemoryVerification(f.sibling, b.id, "verified");
165
+ });
166
+ const fresh = driver.render(f, options, markers);
167
+ expect(fresh).toContain("candidate B");
168
+ expect(fresh).not.toContain("candidate A");
169
+ expect(persistedDelta(f)).toBe(fresh);
170
+ expect(persistedDelta(f)).not.toBe(before);
171
+ } finally {
172
+ f.close();
173
+ }
174
+ });
175
+ }
176
+
177
+ test(`${driver.host} m[1] reads in-place recovered compartment ranges under its writer`, () => {
178
+ const f = driver.create(`${driver.host}-ranges`);
179
+ try {
180
+ const options = {};
181
+ const markers = driver.materialize(f, options);
182
+ appendCompartments(f.db, f.sessionId, [compartment()]);
183
+ f.db
184
+ .prepare(
185
+ "UPDATE compartments SET rebase_status = 'unresolved' WHERE session_id = ?",
186
+ )
187
+ .run(f.sessionId);
188
+ const before = driver.render(f, options, markers);
189
+ expect(before).toContain("## 1-2");
190
+ const revision = f.db
191
+ .prepare("SELECT version FROM compartment_history_versions WHERE session_id = ?")
192
+ .get(f.sessionId) as { version: number };
193
+ refreshAtAdmission(driver, f, options, () => {
194
+ const recovered = recoverUnresolvedCompartments({
195
+ db: f.sibling,
196
+ sessionId: f.sessionId,
197
+ resolveOrdinal: (id) => (id === "a" ? 3 : id === "b" ? 4 : undefined),
198
+ reason: "Recovered fixture anchors",
199
+ });
200
+ expect(recovered.rowsRewritten).toBe(1);
201
+ });
202
+ const updated = f.db
203
+ .prepare("SELECT version FROM compartment_history_versions WHERE session_id = ?")
204
+ .get(f.sessionId) as { version: number };
205
+ expect(updated.version).toBeGreaterThan(revision.version);
206
+ const fresh = driver.render(f, options, markers);
207
+ expect(fresh).toContain("## 3-4");
208
+ expect(fresh).not.toContain("## 1-2");
209
+ expect(persistedDelta(f)).toBe(fresh);
210
+ } finally {
211
+ f.close();
212
+ }
213
+ });
214
+
215
+ test(`${driver.host} m[1] uses the baseline cutoff for a newly expired additive memory`, () => {
216
+ const f = driver.create(`${driver.host}-expiry`);
217
+ const realNow = Date.now;
218
+ let now = 1000;
219
+ Date.now = () => now;
220
+ try {
221
+ const options = {};
222
+ const markers = driver.materialize(f, options);
223
+ now = 2000;
224
+ const before = driver.render(f, options, markers);
225
+ expect(before).not.toContain("eligible at the baseline cutoff");
226
+ refreshAtAdmission(driver, f, options, () => {
227
+ insertMemory(f.sibling, {
228
+ projectPath: f.projectPath,
229
+ category: "ARCHITECTURE",
230
+ content: "Memory eligible at the baseline cutoff",
231
+ expiresAt: 2500,
232
+ });
233
+ now = 3000;
234
+ });
235
+ const fresh = driver.render(f, options, markers);
236
+ expect(fresh).toContain("eligible at the baseline cutoff");
237
+ expect(persistedDelta(f)).toBe(fresh);
238
+ } finally {
239
+ Date.now = realNow;
240
+ f.close();
241
+ }
242
+ });
243
+
244
+ if (driver.temporalHeadings) {
245
+ test(`${driver.host} m[1] reads indexed temporal headings under its writer`, () => {
246
+ const f = driver.create(`${driver.host}-dates`);
247
+ const originalXdgDataHome = process.env.XDG_DATA_HOME;
248
+ process.env.XDG_DATA_HOME = f.directory;
249
+ try {
250
+ const options = { temporalAwareness: true };
251
+ const markers = driver.materialize(f, options);
252
+ appendCompartments(f.db, f.sessionId, [compartment()]);
253
+ for (const ordinal of [1, 2])
254
+ recordMessageFtsRowid(f.db, f.sessionId, ordinal, ordinal);
255
+ const before = driver.render(f, options, markers);
256
+ expect(before).not.toContain("2025-01-01");
257
+ refreshAtAdmission(driver, f, options, () => {
258
+ for (const ordinal of [1, 2])
259
+ recordIndexedMessageTime(
260
+ f.sibling,
261
+ f.sessionId,
262
+ ordinal,
263
+ Date.UTC(2025, 0, 1),
264
+ );
265
+ });
266
+ const fresh = driver.render(f, options, markers);
267
+ expect(fresh).toContain("2025-01-01");
268
+ expect(persistedDelta(f)).toBe(fresh);
269
+ } finally {
270
+ if (originalXdgDataHome === undefined) delete process.env.XDG_DATA_HOME;
271
+ else process.env.XDG_DATA_HOME = originalXdgDataHome;
272
+ f.close();
273
+ }
274
+ });
275
+ }
276
+ }
@@ -29,8 +29,8 @@ describe("known model cache TTL", () => {
29
29
  expect(resolveCacheTtl("5m", "openai/gpt-6")).toBe("30m");
30
30
  expect(resolveCacheTtl("10m", "openai/gpt-6")).toBe("10m");
31
31
  });
32
- it("honors per-model entries before built-ins and built-ins before object defaults", () => {
33
- expect(resolveCacheTtl({ default: "10m" }, "openai/gpt-6")).toBe("30m");
32
+ it("honors per-model entries and configured defaults before built-ins", () => {
33
+ expect(resolveCacheTtl({ default: "10m" }, "openai/gpt-6")).toBe("10m");
34
34
  expect(resolveCacheTtl({ default: "10m", "gpt-6": "5m" }, "openai/gpt-6")).toBe("5m");
35
35
  expect(resolveCacheTtl({ default: "10m", "openai/*": "never" }, "openai/gpt-6")).toBe(
36
36
  "never",
@@ -28,6 +28,8 @@ const MODEL_CACHE_LIFETIMES = [
28
28
  export function resolveModelCacheTtl(
29
29
  config: CacheTtlConfig | undefined,
30
30
  modelKey: string | undefined,
31
+ configuredExplicitly = typeof config === "object" ||
32
+ (typeof config === "string" && config !== "5m"),
31
33
  ): ResolvedCacheTtl {
32
34
  if (config && typeof config !== "string") {
33
35
  const match =
@@ -37,11 +39,13 @@ export function resolveModelCacheTtl(
37
39
  modelKey !== "default"
38
40
  ? config[modelKey]
39
41
  : resolveModelConfigValue(config, modelKey)?.value;
40
- if (match !== undefined) return { value: match, source: "config", modelKey };
42
+ const value = match ?? config.default;
43
+ if (value !== undefined && configuredExplicitly)
44
+ return { value, source: "config", modelKey };
41
45
  }
42
- // A non-5m global string is an explicit policy. The generic 5m default is
43
- // not evidence of provider eviction: documented lifetimes avoid paid rewrites.
44
- if (typeof config === "string" && config !== "5m")
46
+ // The loader supplies provenance: an explicit 5m is policy too. Older callers
47
+ // without that bit still distinguish non-default strings from the schema's 5m.
48
+ if (typeof config === "string" && configuredExplicitly)
45
49
  return { value: config, source: "config", modelKey };
46
50
  const model =
47
51
  canonicalModelIdentity(modelKey ?? "")