@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
@@ -1,4 +1,6 @@
1
- import { describe, expect, mock, test } from "bun:test";
1
+ import { describe, expect, mock, spyOn, test } from "bun:test";
2
+ import { withDreamerModelCooldown } from "../features/magic-context/dreamer/provider-output-failure";
3
+ import { recordedGeminiQuotaMessages } from "../features/magic-context/dreamer/provider-output-failure.test-support";
2
4
 
3
5
  import { HiddenAgentStepLimit } from "../v2/hooks/hidden-child";
4
6
  import {
@@ -406,6 +408,107 @@ describe("promptSyncWithModelSuggestionRetry", () => {
406
408
  });
407
409
  });
408
410
 
411
+ describe("run-scoped primary quota cooldown", () => {
412
+ test("preserves fallback order, retries the primary after reset, and forgets it in a new run", async () => {
413
+ let now = 1791425104275;
414
+ const clock = spyOn(Date, "now").mockImplementation(() => now);
415
+ const models: string[] = [];
416
+ const primary = { providerID: "google", modelID: "antigravity-gemini-3.8-flash" };
417
+ let latestModel = "";
418
+ let primaryCalls = 0;
419
+ const client = createClient(
420
+ mock(async (args: PromptCall) => {
421
+ latestModel = `${args.body.model?.providerID}/${args.body.model?.modelID}`;
422
+ models.push(latestModel);
423
+ if (latestModel.startsWith("google/")) primaryCalls++;
424
+ if (latestModel === "deepseek/first") throw new Error("fallback unavailable");
425
+ return {};
426
+ }),
427
+ );
428
+ const run = () =>
429
+ promptSyncWithValidatedOutputRetry(client, createArgs(primary), {
430
+ fallbackModels: ["deepseek/first", "deepseek/second"],
431
+ fetchOutput: async () =>
432
+ latestModel.startsWith("google/") && primaryCalls <= 2
433
+ ? recordedGeminiQuotaMessages()
434
+ : [
435
+ {
436
+ info: { role: "assistant" },
437
+ parts: [{ type: "text", text: "valid" }],
438
+ },
439
+ ],
440
+ validateOutput: () => "valid",
441
+ });
442
+ try {
443
+ await withDreamerModelCooldown(async () => {
444
+ await run();
445
+ await run();
446
+ });
447
+ await withDreamerModelCooldown(async () => {
448
+ await run();
449
+ await run();
450
+ now += 5_700_000;
451
+ await run();
452
+ });
453
+ expect(models).toEqual([
454
+ "google/antigravity-gemini-3.8-flash",
455
+ "deepseek/first",
456
+ "deepseek/second",
457
+ "deepseek/first",
458
+ "deepseek/second",
459
+ "google/antigravity-gemini-3.8-flash",
460
+ "deepseek/first",
461
+ "deepseek/second",
462
+ "deepseek/first",
463
+ "deepseek/second",
464
+ "google/antigravity-gemini-3.8-flash",
465
+ ]);
466
+ } finally {
467
+ clock.mockRestore();
468
+ }
469
+ });
470
+
471
+ test("records primary quota exhaustion alongside a failing fallback, not a manifest error", async () => {
472
+ let model = "";
473
+ const client = createClient(
474
+ mock(async (args: PromptCall) => {
475
+ model = args.body.model?.providerID ?? "";
476
+ if (model === "deepseek") throw new Error("SessionExecutionFence.Lost");
477
+ return {};
478
+ }),
479
+ );
480
+ let caught: unknown;
481
+ await withDreamerModelCooldown(async () => {
482
+ try {
483
+ await promptSyncWithValidatedOutputRetry(
484
+ client,
485
+ createArgs({ providerID: "google", modelID: "antigravity-gemini-3.8-flash" }),
486
+ {
487
+ fallbackModels: ["deepseek/deepseek-flash"],
488
+ fetchOutput: async () => recordedGeminiQuotaMessages(),
489
+ validateOutput: () => {
490
+ throw new Error("verify manifest missing complete root element");
491
+ },
492
+ },
493
+ );
494
+ } catch (error) {
495
+ caught = error;
496
+ }
497
+ });
498
+ expect(String(caught)).toContain("primary quota exhausted until");
499
+ expect(String(caught)).toContain("SessionExecutionFence.Lost");
500
+ expect(String(caught)).not.toContain("manifest missing");
501
+ expect(getPromptFailureDetail(caught)).toMatchObject({
502
+ failureClass: "provider_error",
503
+ modelAttempted: "deepseek/deepseek-flash",
504
+ modelsTried: ["google/antigravity-gemini-3.8-flash", "deepseek/deepseek-flash"],
505
+ });
506
+ expect(getPromptFailureDetail(caught)?.providerError).toContain(
507
+ "primary quota exhausted until",
508
+ );
509
+ });
510
+ });
511
+
409
512
  describe("promptSyncWithValidatedOutputRetry", () => {
410
513
  test("records a step cap as step_limit without a provider error", async () => {
411
514
  const prompt = mock(async () => {
@@ -1,4 +1,10 @@
1
1
  import type { createOpencodeClient } from "@opencode-ai/sdk";
2
+ import {
3
+ DreamerProviderOutputFailureError,
4
+ primaryQuotaFailure,
5
+ providerOutputFailureFromInvalidManifest,
6
+ rememberPrimaryQuota,
7
+ } from "../features/magic-context/dreamer/provider-output-failure";
2
8
  import { DreamTokenBudgetExceeded } from "../features/magic-context/dreamer/token-budget";
3
9
  import { detectOverflow } from "../features/magic-context/overflow-detection";
4
10
  import { HiddenCompletionRefusal } from "../hooks/magic-context/compartment-runner-types";
@@ -59,6 +65,8 @@ export interface PromptAttemptInfo {
59
65
  totalAttempts: number;
60
66
  /** Explicit model override for this attempt, when one was supplied. */
61
67
  model?: { providerID: string; modelID: string };
68
+ /** A run-local quota deadline prevented dispatch; not an actual model call. */
69
+ skipped?: boolean;
62
70
  }
63
71
 
64
72
  export interface PromptRetryOptions {
@@ -436,14 +444,25 @@ function throwWithPromptFailure(
436
444
  legacyError instanceof Error ? legacyError : new Error(extractMessage(legacyError));
437
445
  const last = failedAttempts.at(-1);
438
446
  const failureClass = last?.failureClass ?? "unknown";
447
+ const quota = failedAttempts.find(
448
+ (failure) =>
449
+ failure.error instanceof DreamerProviderOutputFailureError &&
450
+ failure.error.quotaResetAt !== undefined,
451
+ )?.error as DreamerProviderOutputFailureError | undefined;
452
+ if (quota && error !== quota && !error.message.includes(quota.message))
453
+ error.message = `${quota.message}; ${error.message}`;
439
454
  const providerError =
440
- failureClass === "provider_error"
441
- ? sanitizeDiagnosticText(shortErr(last?.error ?? legacyError)).slice(0, 500)
455
+ failureClass === "provider_error" || quota
456
+ ? sanitizeDiagnosticText(
457
+ `${quota ? `${quota.message}; ` : ""}${shortErr(last?.error ?? legacyError)}`,
458
+ ).slice(0, 500)
442
459
  : null;
443
460
  promptFailureDetails.set(error, {
444
461
  failureClass,
445
462
  modelAttempted: last?.attempt.label ?? null,
446
- modelsTried: failedAttempts.map((failure) => failure.attempt.label),
463
+ modelsTried: failedAttempts
464
+ .filter((failure) => !failure.attempt.skipped)
465
+ .map((failure) => failure.attempt.label),
447
466
  providerError,
448
467
  timeoutMs: failureClass === "provider_timeout" ? timeoutMs : null,
449
468
  childSessionId: transport ? (transport.childSessionId ?? null) : args.path.id || null,
@@ -667,6 +686,16 @@ async function attemptAndValidate<TOutput, TValidated>(
667
686
  }
668
687
 
669
688
  try {
689
+ // Only the structured account-pool notice bypasses caller validation.
690
+ // The near-zero-token heuristic remains owned by manifest callers.
691
+ const messages = Array.isArray(output)
692
+ ? output
693
+ : (output as { messages?: unknown } | null)?.messages;
694
+ const text = extractLatestAssistantText(messages);
695
+ if (text) {
696
+ const failure = providerOutputFailureFromInvalidManifest(messages, text);
697
+ if (failure?.quotaResetAt !== undefined) throw failure;
698
+ }
670
699
  if (!extractLatestAssistantText(output)) {
671
700
  const assistantFailure = extractLatestAssistantFailure(output);
672
701
  if (assistantFailure) {
@@ -721,8 +750,22 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
721
750
  const failedAttempts: FailedAttempt[] = [];
722
751
  let firstError: unknown = null;
723
752
  let lastError: unknown = null;
753
+ const cooledPrimary = primaryQuotaFailure(explicitPrimaryLabel);
724
754
 
725
755
  try {
756
+ if (cooledPrimary)
757
+ throw {
758
+ error: cooledPrimary,
759
+ failureClass: "provider_error",
760
+ attempt: {
761
+ label: explicitPrimaryLabel,
762
+ attemptIndex: 0,
763
+ isFallback: false,
764
+ totalAttempts,
765
+ model: baseBody.model,
766
+ skipped: true,
767
+ },
768
+ } satisfies FailedAttempt;
726
769
  return await attemptAndValidate(
727
770
  client,
728
771
  baseArgs,
@@ -743,6 +786,19 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
743
786
  failedAttempts.push(failure);
744
787
  firstError = failure.error;
745
788
  lastError = failure.error;
789
+ if (
790
+ failure.error instanceof DreamerProviderOutputFailureError &&
791
+ failure.error.quotaResetAt !== undefined
792
+ )
793
+ failure.error.message = failure.error.message.replace(
794
+ /^provider quota exhausted/,
795
+ "primary quota exhausted",
796
+ );
797
+ const quota = !cooledPrimary && rememberPrimaryQuota(explicitPrimaryLabel, failure.error);
798
+ if (quota)
799
+ log(
800
+ `[${callContext}] ${quota.message}; skipping ${explicitPrimaryLabel} for this run until reset`,
801
+ );
746
802
  if (isNonRetryable(failure.error, options.signal)) {
747
803
  throwWithPromptFailure(
748
804
  failure.error,
@@ -763,9 +819,10 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
763
819
  );
764
820
  }
765
821
 
766
- log(
767
- `[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`,
768
- );
822
+ if (!cooledPrimary && !quota)
823
+ log(
824
+ `[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`,
825
+ );
769
826
  }
770
827
 
771
828
  for (let i = 0; i < fallbacks.length; i += 1) {
@@ -25,14 +25,14 @@
25
25
  * for Pi and Pi's own path is used.
26
26
  */
27
27
 
28
- import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
28
+ import { readFileSync } from "node:fs";
29
29
  import { join } from "node:path";
30
30
  import type { ContextLimitProvenance } from "./context-limit-provenance";
31
31
  import { getMagicContextStorageDir } from "./data-path";
32
32
  import { getHarness } from "./harness";
33
33
  import { modelRefLookupOrder } from "./harness-provider-map";
34
34
  import { sessionLog } from "./logger";
35
- import { shouldEnforcePrivateStoragePermissions } from "./storage-permissions";
35
+ import { writeStorageFileAtomicSync } from "./storage-permissions";
36
36
  import {
37
37
  deriveWindowGeometry,
38
38
  getWindowOverlay,
@@ -182,16 +182,8 @@ function persistApiCache(): void {
182
182
  }
183
183
  }
184
184
  try {
185
- const dir = getMagicContextStorageDir();
186
- mkdirSync(dir, { recursive: true });
187
185
  const target = persistFilePath();
188
- const tmp = `${target}.${process.pid}.tmp`;
189
- if (shouldEnforcePrivateStoragePermissions()) {
190
- writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 0o600 });
191
- } else {
192
- writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8" });
193
- }
194
- renameSync(tmp, target);
186
+ writeStorageFileAtomicSync(target, JSON.stringify(obj));
195
187
  } catch {
196
188
  // best-effort — a failed persist only loses cold-start warmth, not correctness
197
189
  }
@@ -0,0 +1,23 @@
1
+ /** Default result keep counts shared by runtime and configuration; this database-free module is bundled with the TUI. */
2
+ export const DEFAULT_PROTECTED_TOOLS: Readonly<Record<string, number>> = {
3
+ todowrite: 1,
4
+ ctx_reduce: 3,
5
+ };
6
+
7
+ export function normalizeProtectedToolName(name: string | null): string {
8
+ return (name ?? "").toLowerCase().replace(/^mcp_/, "");
9
+ }
10
+
11
+ /** Normalize before merging tiers so aliases cannot defeat a project override. */
12
+ export function mergeProtectedTools(
13
+ ...maps: (Readonly<Record<string, number>> | undefined)[]
14
+ ): Record<string, number> {
15
+ return Object.fromEntries(
16
+ [DEFAULT_PROTECTED_TOOLS, ...maps].flatMap((map) =>
17
+ Object.entries(map ?? {}).map(([name, count]) => [
18
+ normalizeProtectedToolName(name),
19
+ count,
20
+ ]),
21
+ ),
22
+ );
23
+ }
@@ -0,0 +1,9 @@
1
+ /** Only expose the generated quota deadline, not arbitrary provider output,
2
+ * account identifiers, or private paths from the surrounding failure text. */
3
+ export function primaryQuotaDiagnostic(error: string | null | undefined): string | null {
4
+ return (
5
+ error?.match(
6
+ /primary quota exhausted until \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/,
7
+ )?.[0] ?? null
8
+ );
9
+ }
@@ -1,15 +1,5 @@
1
1
  import { randomBytes, timingSafeEqual } from "node:crypto";
2
- import {
3
- chmodSync,
4
- mkdirSync,
5
- readdirSync,
6
- readFileSync,
7
- renameSync,
8
- rmSync,
9
- unlinkSync,
10
- writeFileSync,
11
- } from "node:fs";
12
- import { dirname } from "node:path";
2
+ import { readdirSync, readFileSync, unlinkSync } from "node:fs";
13
3
  import type { Server, ServerWebSocket } from "bun";
14
4
  import { log } from "./logger";
15
5
  import {
@@ -25,7 +15,7 @@ import {
25
15
  rpcPortDir,
26
16
  rpcPortFilePath,
27
17
  } from "./rpc-utils";
28
- import { shouldEnforcePrivateStoragePermissions } from "./storage-permissions";
18
+ import { writeStorageFileAtomicSync } from "./storage-permissions";
29
19
 
30
20
  type RpcHandler = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
31
21
 
@@ -177,38 +167,11 @@ export class MagicContextRpcServer {
177
167
  // recent instead of cross-wiring via one shared project file.
178
168
  try {
179
169
  void this.warnIfOtherLiveInstance();
180
- const dir = dirname(this.portFilePath);
181
- // The port file carries the RPC bearer token. The normal policy keeps
182
- // it owner-only; a trusted-group deployment explicitly delegates every
183
- // storage mode to its operator, so this path must not chmod or supply a
184
- // restrictive creation mode in that case.
185
- const enforcePrivatePermissions = shouldEnforcePrivateStoragePermissions();
186
- if (enforcePrivatePermissions) {
187
- mkdirSync(dir, { recursive: true, mode: 0o700 });
188
- try {
189
- chmodSync(dir, 0o700);
190
- } catch {
191
- // Continue RPC startup when directory tightening is rejected.
192
- }
193
- } else {
194
- mkdirSync(dir, { recursive: true });
195
- }
196
- // Registered before the file appears, so the storage migration guard in
197
- // this process never sees its own discovery file as another live host.
170
+ // The discovery file carries the RPC bearer token. The shared writer
171
+ // creates its directory and temporary file with owner-only modes.
198
172
  this.unregisterOwnInstance ??= registerOwnRpcServerInstance(this.instanceId);
199
- const tmpPath = `${this.portFilePath}.tmp`;
200
- // A stale tmp from a crashed write could exist with loose perms;
201
- // writeFileSync's mode only applies on create, so remove it first.
202
- try {
203
- rmSync(tmpPath, { force: true });
204
- } catch {
205
- // best-effort
206
- }
207
- // Synchronous write so the renameSync below sees a fully-written file.
208
- // The private mode keeps the bearer token out of other local accounts;
209
- // externally managed storage intentionally leaves its mode to the umask.
210
- writeFileSync(
211
- tmpPath,
173
+ writeStorageFileAtomicSync(
174
+ this.portFilePath,
212
175
  JSON.stringify({
213
176
  port: this.port,
214
177
  pid: process.pid,
@@ -217,18 +180,7 @@ export class MagicContextRpcServer {
217
180
  token: this.token,
218
181
  instance_id: this.instanceId,
219
182
  }),
220
- enforcePrivatePermissions
221
- ? { encoding: "utf-8", mode: 0o600 }
222
- : { encoding: "utf-8" },
223
183
  );
224
- renameSync(tmpPath, this.portFilePath);
225
- if (enforcePrivatePermissions) {
226
- try {
227
- chmodSync(this.portFilePath, 0o600);
228
- } catch {
229
- // Continue RPC startup when port-file tightening is rejected.
230
- }
231
- }
232
184
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
233
185
  } catch (err) {
234
186
  log(`[rpc] failed to write port file: ${err}`);
@@ -0,0 +1,105 @@
1
+ import { afterEach, describe, expect, it, spyOn } from "bun:test";
2
+ import { readFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ closeDatabase,
7
+ openDatabase,
8
+ openDatabaseAsync,
9
+ } from "../features/magic-context/storage-db";
10
+ import { Database } from "./sqlite";
11
+ import { configureContextDatabasePragmas } from "./sqlite-context-pragmas";
12
+ import { createTestTempDirFromPath } from "./test-temp-dir";
13
+
14
+ const roots: string[] = [];
15
+ function fixturePath(): string {
16
+ const root = createTestTempDirFromPath(join(tmpdir(), "mc-context-pragmas-"));
17
+ roots.push(root);
18
+ return join(root, "context.db");
19
+ }
20
+ afterEach(() => {
21
+ closeDatabase();
22
+ for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
23
+ });
24
+
25
+ describe("context database durability", () => {
26
+ it("fresh synchronous open uses WAL and synchronous=NORMAL", () => {
27
+ const db = openDatabase(fixturePath());
28
+ expect(db?.prepare("PRAGMA journal_mode").get()).toEqual({ journal_mode: "wal" });
29
+ expect(db?.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 1 });
30
+ });
31
+
32
+ it("fresh async open uses WAL and synchronous=NORMAL", async () => {
33
+ const db = await openDatabaseAsync({ dbPath: fixturePath() });
34
+ expect(db?.prepare("PRAGMA journal_mode").get()).toEqual({ journal_mode: "wal" });
35
+ expect(db?.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 1 });
36
+ });
37
+
38
+ it("refuses NORMAL when SQLite cannot activate WAL", () => {
39
+ const db = new Database(fixturePath());
40
+ const prepare = db.prepare.bind(db);
41
+ const spy = spyOn(db, "prepare").mockImplementation((sql: string) =>
42
+ prepare(sql === "PRAGMA journal_mode=WAL" ? "PRAGMA journal_mode" : sql),
43
+ );
44
+ try {
45
+ expect(() => configureContextDatabasePragmas(db)).toThrow("requires WAL");
46
+ expect(prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 2 });
47
+ } finally {
48
+ spy.mockRestore();
49
+ db.close();
50
+ }
51
+ });
52
+
53
+ it("read-only inspection leaves a rollback-journal store unchanged", () => {
54
+ const path = fixturePath();
55
+ const writer = new Database(path);
56
+ writer.exec("CREATE TABLE probe(id INTEGER)");
57
+ writer.close();
58
+ const bytes = readFileSync(path);
59
+ const reader = new Database(path, { readonly: true });
60
+ try {
61
+ configureContextDatabasePragmas(reader, true);
62
+ expect(reader.prepare("PRAGMA journal_mode").get()).toEqual({ journal_mode: "delete" });
63
+ expect(reader.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 2 });
64
+ } finally {
65
+ reader.close();
66
+ }
67
+ expect(readFileSync(path)).toEqual(bytes);
68
+ });
69
+
70
+ it("an independently reopened WAL writer receives the connection-local policy", () => {
71
+ const path = fixturePath();
72
+ const first = new Database(path);
73
+ configureContextDatabasePragmas(first);
74
+ first.close();
75
+ const reopened = new Database(path);
76
+ try {
77
+ // Bun defaults WAL to NORMAL; Node defaults it to FULL. Exercise an
78
+ // explicit FULL connection so the assertion is portable to both.
79
+ reopened.exec("PRAGMA synchronous=FULL");
80
+ expect(reopened.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 2 });
81
+ configureContextDatabasePragmas(reopened);
82
+ expect(reopened.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 1 });
83
+ } finally {
84
+ reopened.close();
85
+ }
86
+ });
87
+
88
+ it("WAL configuration releases its read cursor before a sibling commits", () => {
89
+ const path = fixturePath();
90
+ const db = new Database(path);
91
+ const sibling = new Database(path);
92
+ try {
93
+ configureContextDatabasePragmas(db);
94
+ db.exec(
95
+ "CREATE TABLE probe(id INTEGER PRIMARY KEY, value INTEGER); INSERT INTO probe VALUES (1, 0)",
96
+ );
97
+ sibling.exec("UPDATE probe SET value = 1 WHERE id = 1");
98
+ db.exec("UPDATE probe SET value = 2 WHERE id = 1");
99
+ expect(db.prepare("SELECT value FROM probe").get()).toEqual({ value: 2 });
100
+ } finally {
101
+ db.close();
102
+ sibling.close();
103
+ }
104
+ });
105
+ });
@@ -0,0 +1,24 @@
1
+ import type { Database } from "./sqlite";
2
+
3
+ /**
4
+ * NORMAL avoids a WAL sync on each commit. WAL still recovers consistently after
5
+ * a crash, but the latest committed work may be lost on power failure. Never use
6
+ * this durability policy in a rollback journal. In-memory test stores cannot use
7
+ * WAL and keep their existing synchronous mode.
8
+ *
9
+ * Call after the schema fence and busy timeout, before any context-store writes.
10
+ * Read-only diagnostics must not switch a legacy store's journal mode.
11
+ */
12
+ export function configureContextDatabasePragmas(db: Database, readonly = false): void {
13
+ const [row] = db
14
+ .prepare(readonly ? "PRAGMA journal_mode" : "PRAGMA journal_mode=WAL")
15
+ .all() as { journal_mode?: string }[];
16
+ const mode = row?.journal_mode?.toLowerCase();
17
+ if (mode === "memory" || (readonly && mode !== "wal")) return;
18
+ if (mode !== "wal") {
19
+ throw new Error(
20
+ `context.db requires WAL before synchronous=NORMAL (got ${mode ?? "unknown"})`,
21
+ );
22
+ }
23
+ db.exec("PRAGMA synchronous=NORMAL");
24
+ }
@@ -25,3 +25,20 @@ export function closeQuietly(db: Database | null | undefined): void {
25
25
  // intentional: caller wants quiet close
26
26
  }
27
27
  }
28
+
29
+ /**
30
+ * True only when the connection reports that no transaction is open. Caches
31
+ * keyed on SQLite's change counters must not keep a value read inside a
32
+ * transaction: a rollback restores the rows without moving total_changes(),
33
+ * data_version or schema_version, so the stale entry would look current.
34
+ * Unknown state (a proxy that cannot read the native getter) counts as "in a
35
+ * transaction" so callers fall back to a fresh read.
36
+ */
37
+ export function isKnownAutocommit(db: Database): boolean {
38
+ const state = db as unknown as { inTransaction?: boolean; isTransaction?: boolean };
39
+ try {
40
+ return state.inTransaction === false || state.isTransaction === false;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
@@ -365,6 +365,12 @@ function trackSqliteConnection(
365
365
  ...metadata,
366
366
  reference: new WeakRef(db),
367
367
  });
368
+ const prepare = db.prepare.bind(db);
369
+ Object.defineProperty(db, "prepare", {
370
+ configurable: true,
371
+ writable: true,
372
+ value: (sql: string) => ownPreparedStatement(db, prepare(sql)),
373
+ });
368
374
  return db;
369
375
  }
370
376
 
@@ -540,6 +546,75 @@ export type Database = BetterSqlite3.Database;
540
546
  */
541
547
  export type Statement = BetterSqlite3.Statement<unknown[], unknown>;
542
548
 
549
+ type FinalizableStatement = Statement & { finalize(): void };
550
+ interface StatementOwner {
551
+ references: Set<WeakRef<FinalizableStatement>>;
552
+ registered: WeakSet<FinalizableStatement>;
553
+ /** Set size at which collected references are next swept out. */
554
+ pruneAt: number;
555
+ }
556
+ const statementOwners = new WeakMap<Database, StatementOwner>();
557
+ const FIRST_PRUNE_AT = 256;
558
+
559
+ function ownPreparedStatement(db: Database, statement: Statement): Statement {
560
+ const finalizable = statement as FinalizableStatement;
561
+ // Node finalizes its statements on close and exposes no finalize() method.
562
+ if (typeof finalizable.finalize !== "function") return statement;
563
+ let owner = statementOwners.get(db);
564
+ if (!owner) {
565
+ const references = new Set<WeakRef<FinalizableStatement>>();
566
+ owner = { references, registered: new WeakSet(), pruneAt: FIRST_PRUNE_AT };
567
+ statementOwners.set(db, owner);
568
+ const close = db.close.bind(db) as (...args: unknown[]) => unknown;
569
+ Object.defineProperty(db, "close", {
570
+ configurable: true,
571
+ writable: true,
572
+ value: (...args: unknown[]) => {
573
+ let failure: unknown;
574
+ for (const reference of references) {
575
+ try {
576
+ reference.deref()?.finalize();
577
+ } catch (error) {
578
+ failure ??= error;
579
+ }
580
+ }
581
+ references.clear();
582
+ const result = close(...args);
583
+ if (failure) throw failure;
584
+ return result;
585
+ },
586
+ });
587
+ }
588
+ if (!owner.registered.has(finalizable)) {
589
+ // No FinalizationRegistry: its callbacks run at GC time, where a throw
590
+ // is uncaught (seen in Bun's parallel test runner). Sweep references to
591
+ // collected statements whenever the set doubles instead, so one-shot
592
+ // statements in a long-running host stay bounded.
593
+ if (owner.references.size >= owner.pruneAt) {
594
+ for (const reference of owner.references) {
595
+ if (reference.deref() === undefined) owner.references.delete(reference);
596
+ }
597
+ owner.pruneAt = Math.max(FIRST_PRUNE_AT, owner.references.size * 2);
598
+ }
599
+ owner.references.add(new WeakRef(finalizable));
600
+ owner.registered.add(finalizable);
601
+ }
602
+ return statement;
603
+ }
604
+
605
+ /**
606
+ * Cached statements must be finalized before closing their connection. Bun's
607
+ * prepare() leaves SQLite's native close deferred until all statements finish,
608
+ * even after the JS Database reports closed. Delayed WAL checkpoints can then
609
+ * overwrite a restored database if a caller reuses the file in the meantime.
610
+ * The shared Database owns every preparation; use this for caches that also
611
+ * accept direct bun:sqlite handles. Weak references avoid retaining one-shot
612
+ * statements for the lifetime of a long-running host.
613
+ */
614
+ export function prepareCachedStatement(db: Database, sql: string): Statement {
615
+ return ownPreparedStatement(db, db.prepare(sql));
616
+ }
617
+
543
618
  const privilegeDepth = new WeakMap<Database, number>();
544
619
  const transformPassScope = new AsyncLocalStorage<
545
620
  { active: boolean; remainingWaitMs: number } | undefined
@@ -100,7 +100,7 @@ describe("status detail text", () => {
100
100
  expect(formatStatusDetailMarkdown(STATUS_FIXTURE)).toBe(`## Magic Context Status
101
101
 
102
102
  - **Context:** 75.0% of usable context (96,000 / 128,000 tokens)
103
- - **Cache lifetime:** 1h (config for anthropic/claude-opus-5)
103
+ - **Cache lifetime:** 1h (your config)
104
104
  - **Automatic compression:** at 65.0% of usable context
105
105
  - **History compression:** Compressing history · 12 history blocks
106
106
  - **Reclaimable:** 3 spent tool outputs (~14k tokens)
@@ -148,6 +148,23 @@ describe("status detail text", () => {
148
148
  ).not.toContain("MC-S05");
149
149
  });
150
150
 
151
+ test("shows the primary quota deadline without exposing surrounding account diagnostics", () => {
152
+ const summary = formatStatusDetailMarkdown({
153
+ ...STATUS_FIXTURE,
154
+ dreamerFailures: [
155
+ {
156
+ task: "verify",
157
+ error: "primary quota exhausted until 2026-10-08T03:40:04.275Z; account=private@example.com",
158
+ lastSucceededAt: null,
159
+ retryCount: 1,
160
+ },
161
+ ],
162
+ });
163
+ expect(summary).toContain("primary quota exhausted until 2026-10-08T03:40:04.275Z");
164
+ expect(summary).not.toContain("private@example.com");
165
+ expect(summary).not.toContain("manifest missing");
166
+ });
167
+
151
168
  test("keeps internal vocabulary and identifiers out of the summary", () => {
152
169
  const summary = formatStatusDetailMarkdown({
153
170
  ...STATUS_FIXTURE,